読者です 読者をやめる 読者になる 読者になる

yu00’s blog

プログラミングに関する備忘録です

スタイルシート詳細

PyQt5入門 PythonでGUI作成 - yu00’s blog


スタイルシートについて詳細を説明します.

スタイルシートの設定

アプリケーション全体に同じスタイルを設定するには
QApplication.setStyleSheet(),
特定のウィジェットとその子ウィジェットにスタイルを
設定するにはQWidget.setStyleSheet()を使います.

次はアプリケーション全体のQPushButtonの
文字色を赤にする例です.

#! /usr/bin/python3
# -*- coding: utf-8 -*-

import sys
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton

if __name__ == '__main__':
    app = QApplication(sys.argv)
    
    style = 'QPushButton{color: red}'
    app.setStyleSheet(style)
    
    window = QWidget()
    
    button = QPushButton('button', window)
    
    window.show()
    sys.exit(app.exec_())

f:id:yu00:20150919205252p:plain

スタイルの書き方

スタイルはselector, property, valueを指定します.

    # selector{property: value}
    style = 'QPushButton{color: red}'


複数のクラスに同じ設定をしたいときは,
コンマ(,)で区切ります.

    style = 'QPushButton, QLabel{color: red}'


クラスごとに設定するには次のようにします.

    style = '''
        QPushButton{color: red}
        QLabel{color: blue}
        '''


複数のプロパティを設定するには
セミコロン(;)で区切ります.

    style = '''
        QPushButton{
            color: red;
            font-size: 15pt;
        }
        '''

スタイルを適用する範囲を指定

セレクターでスタイルを適用する範囲を指定できます.

    # すべてのウィジェット
    style = '*{color: red}'
    
    # QPushButtonとその派生クラスのインスタンス
    style = 'QPushButton{color: red}'
    
    # (QPushButton.flat==false)となるインスタンス
    style = 'QPushButton[flat="false"]{color: red}'
    
    # QPushButtonのインスタンスのみ.派生クラスは適用しない
    style = '.QPushButton{color: red}'
    
    # QPushuButtonでobject nameがmyButtonとなるインスタンス
    style = 'QPushButton#myButton{color: red}'
    button = QPushButton('button')
    button.setObjectName('myButton')
    
    # QDialogの子孫(子,孫...)のQPushButtonのインスタンス
    style = 'QDialog QPushButton{color: red}'
    
    # QDialogの子のQPushButtonのインスタンス
    style = 'QDialog > QPushButton{color: red}'

セレクターの組み合わせ

セレクタを組み合わせて使うことができます.

    # object nameがmyWidgetの子のウィジェットすべて
    style = 'QWidget#myWidget > *{color: red}'

Sub-Controls

ウィジェットによっては,サブコントロールを持つ
ものもあります.
サブコントロールのスタイルはコロンコロン(::)で指定します.
例えばQComboBoxのサブコントロールに
下ボタンがあります.
f:id:yu00:20150921115642p:plain
QComboBoxの下ボタンはdrop-downで指定します.

    style = 'QComboBox::drop-down{image: url(my-drop-down.png)}'

f:id:yu00:20150921115807p:plain

Pseudo-States

ウィジェットによっては,pseudo-states(疑似状態)を持つものが
あります.
pseudo-statesはコロン(:)で指定します.
例えばQPushBottonのpseudo-statesに
マウスのカーソルが乗っている状態があります.
マウスのカーソルが乗っている状態はhoverで指定します.

    style = 'QPushButton:hover{color: red}'

f:id:yu00:20150921135344p:plain


エクスクラメーション(!)はNOTを表します.
次は,マウスが乗っていないとき文字色が赤になります.

    style = 'QPushButton:!hover{color: red}'


pseudo-statesをつなげるとANDになります.
次は,マウスが乗っているかつ押されているときに文字色が赤になります.

    style = 'QPushButton:hover:pressed{color: red}'


コンマ(,)はORを表します.

    style = 'QPushButton:hover, QPushButton:pressed{color: red}'


また,サブコントロールと一緒に使うこともできます.

    style = 'QComboBox::drop-down:hover{image: url(my-drop-down.png)}'

自作クラスのスタイルシート

セレクタに自作のクラスを指定できます.

class MyButton(QPushButton):
    ...

...
    style = 'MyButton{color: red}'

スタイルシートによるスタイル設定

PyQt5入門 PythonでGUI作成 - yu00’s blog

スタイルシート

PyQt5でフォントや色を設定するにはスタイルシート
を使うと簡単です.

スタイルシートはHTML CSSのような形式でスタイルを
設定する機能です.

スタイルシートを使うことで,
アプリケーション全体に同じスタイルを使うことや,
クラスごと,インスタンスごとにスタイルを設定することも
できます.

スタイルシートの例

QWidget.setStyleSheet()を使うことで,
インスタンスごとにスタイルを設定できます.

#! /usr/bin/python3
# -*- coding: utf-8 -*-

import sys
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton

if __name__ == '__main__':
    app = QApplication(sys.argv)
    
    window = QWidget()
    
    button = QPushButton('button', window)
    # スタイルシートを設定.文字色を赤にする
    button.setStyleSheet('color:red')
    
    window.show()
    sys.exit(app.exec_())

f:id:yu00:20150919205252p:plain

QGridLayoutクラス

PyQt5入門 PythonでGUI作成 - yu00’s blog

QGridLayoutクラスはマス目に並べるクラスです.

マス目に並べる

addWidget(widget, row, column)

#! /usr/bin/python3
# -*- coding: utf-8 -*-

import sys
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton, QGridLayout

if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = QWidget()
    
    button1 = QPushButton('1')
    button2 = QPushButton('2')
    button3 = QPushButton('3')
    
    layout = QGridLayout()
    # レイアウトにウィジェットを追加
    layout.addWidget(button1, 0, 0)
    layout.addWidget(button2, 0, 1)
    layout.addWidget(button3, 1, 0)

    # ウィジェットにレイアウトをセット
    window.setLayout(layout)
    
    window.show()
    sys.exit(app.exec_())

f:id:yu00:20150917203356p:plain

複数のマスをまたがって並べる

addWidget(widget, fromRow, fromColumn, rowSpan, columnSpan)

rowSpan,columnSpan:占有する行数・列数

layout.addWidget(button1, 0, 0)
layout.addWidget(button2, 0, 1)
layout.addWidget(button3, 0, 2)
# 2行*3列またがる
layout.addWidget(button4, 1, 0, 2, 3)
layout.addWidget(button5, 1, 3)
layout.addWidget(button6, 2, 3)

f:id:yu00:20150917203225p:plain

特定の行・列にストレッチを設定する

setColumnStretch(column, stretch)

setRowStretch(row, stretch)

layout.setColumnStretch(0, 2)
layout.setColumnStretch(1, 3)
layout.setColumnStretch(2, 2)

f:id:yu00:20150917203959p:plain

QHBoxLayout・QVBoxLayoutクラス

PyQt5入門 PythonでGUI作成 - yu00’s blog

QHBoxLayout・QVBoxLayoutはウィジェット
横・縦に並べるクラスです.

ここではQHBoxLayoutを例に説明します.

横に並べる

addWidget(widget)

サンプルコード
#! /usr/bin/python3
# -*- coding: utf-8 -*-

import sys
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton, QHBoxLayout

if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = QWidget()
    
    button1 = QPushButton('1')
    button2 = QPushButton('2')
    button3 = QPushButton('3')
    
    layout = QHBoxLayout()
    # レイアウトにウィジェットを追加
    layout.addWidget(button1)
    layout.addWidget(button2)
    layout.addWidget(button3)
    
    # ウィジェットにレイアウトをセット
    window.setLayout(layout)
    
    window.show()
    sys.exit(app.exec_())
実行例

f:id:yu00:20150917163322p:plain

ストレッチを設定しながら並べる

addWidget(widget, stretch=0)

layout.addWidget(button1, 2)
layout.addWidget(button2, 3)

f:id:yu00:20150917163751p:plain

また,追加するレイアウトにもストレッチを設定できます.

addLayout(layout, stretch=0)

vbox = QVBoxLayout()
layout.addLayout(vbox, 2)

表示位置を設定しながら並べる

alignmentを設定することで右寄せや下寄せができます.

addWidget(widget, stretch=0, alignment=0)

from PyQt5.QtCore import Qt
...
layout.addWidget(button1, alignment=(Qt.AlignBottom | Qt.AlignRight))

f:id:yu00:20150917191240p:plain

固定長の空白を入れる

addSpacing(size)

レイアウトの最後にストレッチしない
サイズがsizeの空白を追加します.

    layout.addWidget(button1)
    layout.addWidget(button2)
    # サイズが20の空白を追加
    layout.addSpacing(20)
    layout.addWidget(button3)

f:id:yu00:20150917164522p:plain

伸縮する空白を入れる

addStretch(stretch)

レイアウトの最後にストレッチがstretch
の空白を追加します.

    layout.addWidget(button1, 2)
    layout.addWidget(button2, 2)
    # サイズ1/2の空白を追加
    layout.addStretch(1)
    layout.addWidget(button3, 2)

f:id:yu00:20150917190227p:plain

QLabelクラス

PyQt5入門 PythonでGUI作成 - yu00’s blog

QLabelクラスは文字または画像を表示するクラスです.

QLabelの作成

サンプルコード

#! /usr/bin/python3
# -*- coding: utf-8 -*-

import sys
from PyQt5.QtWidgets import QApplication, QWidget, QLabel

if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = QWidget()
    
    label = QLabel('Label', window)
    
    window.show()
    sys.exit(app.exec_())

実行例

f:id:yu00:20150916205805p:plain

文字の変更

label.setText('my-text')

またリッチテキストと呼ばれる書式を使い,HTML形式で書くこともできます.

label.setText('<h1>my-text</h1>')

f:id:yu00:20150916210641p:plain

画像の表示

QPixmapを使い画像を表示できます.

from PyQt5.QtGui import QPixmap
...
label.setPixmap(QPixmap('my-image.png'))

数値の表示

数値を文字として表示できます.

label.setNum(1.23)

枠線の表示

QLabelが継承しているQFrameの関数を使い枠線が表示できます.

from PyQt5.QtWidgets import QFrame
...
label.setFrameStyle(QFrame.Box | QFrame.Plain)

f:id:yu00:20150916212156p:plain

文字の表示位置の変更(右寄せ,下寄せなど)

from PyQt5.QtCore import Qt
...
label.setAlignment(Qt.AlignBottom | Qt.AlignRight)

QPushButtonクラス

PyQt5入門 PythonでGUI作成 - yu00’s blog

QPushButtonは普通のボタンです.

ボタンの作成

PyQt5.QtWidgets.QPushButton(text, parent=0)

文字がtextのボタンを作成します.

サンプルコード

#! /usr/bin/python3
# -*- coding: utf-8 -*-

import sys
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton

if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = QWidget()
    
    button = QPushButton('button', window)
    
    window.show()
    sys.exit(app.exec_())        

実行例

f:id:yu00:20150915181217p:plain

文字の変更

button.setText('my-text')

アイコンの設定

button.setIcon(QIcon('my-icon.png'))

f:id:yu00:20150915181753p:plain

シグナル:ボタンがクリックされた

QAbstractButton.clicked(checked = False)

シグナル:ボタンが押された

QAbstractButton.pressed()

シグナル:ボタンがはなされた

QAbstractButton.released()

シグナル:ボタンがトグルされた

QAbstractButton.toggled(checked)

ckecked:TrueかFalse.チェックされたときTrue

ボタンをトグル式にする

ボタンをクリックしたとき,押しっぱなしの状態になり,
次にクリックしたとき元に戻るようにするには,
setCheckable()を使います.

サンプルコード

ボタンが押された状態のときCheckedと表示し,
はなされた状態のときNot Checkedと表示するプログラムです.

#! /usr/bin/python3
# -*- coding: utf-8 -*-

import sys
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton

class MyWidget(QWidget):
    def __init__(self):
        super().__init__()
        self.init_ui()
        self.show()
        
    def init_ui(self):
        self.button = QPushButton('button', self)
        # トグル式にする
        self.button.setCheckable(True)
        # シグナル・スロットの設定
        self.button.toggled.connect(self.slot_button_toggled)
    
    def slot_button_toggled(self, checked):
        """ ボタンがトグルされたときのスロット """
        if checked:
            self.button.setText('Checked')
        else:
            self.button.setText('Not Checked')
        
if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = MyWidget()
    sys.exit(app.exec_())

実行例

f:id:yu00:20150915184643p:plain f:id:yu00:20150915184651p:plain

シグナルとスロット詳細

PyQt5入門 PythonでGUI作成 - yu00’s blog


ここではシグナルとスロットについて,
詳しい使い方を説明します.

シグナルとスロットを接続する

シグナルとスロットを接続(設定)するにはconnect()を使います.

connect(slot)

シグナルにスロットslotを接続します.
(一部引数を省略しています.すべての引数はリファレンスを参照してください)

サンプルコード
app = QApplication(sys.argv)
button = QPushButton('button')
# スロットを設定
button.clicked.connect(app.quit)

スロットを解除する

一度接続したスロットを解除するにはdisconnect()を使います.

disconnect([slot])

スロットslotを解除します.slotを省略した場合,
そのシグナルに設定されたすべてのスロットが解除されます.

サンプルコード
app = QApplication(sys.argv)
button = QPushButton('button')
# スロットを設定
button.clicked.connect(app.quit)
# スロットを解除
button.clicked.disconnect(app.quit)    

一つのシグナルに複数のスロットを接続する

一つのシグナルに複数のスロットを設定することができます.
スロットは接続された順に実行されます.

サンプルコード

一つのボタンのシグナルに二つのラベルのスロットを設定しています.

button = QPushButton('button')
label1 = QLabel('label1')
label2 = QLabel('label2')

# ボタンが押された->ラベルを消す
button.clicked.connect(label1.clear)
button.clicked.connect(label2.clear)

複数のシグナルに一つのスロットを接続する

複数のシグナルに同じスロットを接続することができます.

サンプルコード

二つのボタンのシグナルに一つのラベルのスロットを設定しています.

button1 = QPushButton('button1')
button2 = QPushButton('button2')
label = QLabel('label')

# ボタンが押された->ラベルを消す
button1.clicked.connect(label.clear)
button2.clicked.connect(label.clear)

シグナルの引数をスロットに渡す

シグナルとスロットの関数に引数があった場合,
シグナルの引数と同じ値がスロットの引数に渡されます.
引数はいくつあってもよいですが,
引数の型は同じである必要があります.

サンプルコード

シグナルにQLineEdit.textChanged(str),
スロットにQLabel.setText(str)を使っています.

lineEdit = QLineEdit()
label = QLabel('label')

# テキストが変わった->テキストをセット
lineEdit.textChanged.connect(label.setText)

実行例

編集エリアに文字を入力すると同じ文字が下のラベルに
表示されます.
f:id:yu00:20150913175302p:plain

シグナルにオーバーロードがある場合

シグナルにオーバーロードがある場合は
connectのときどれを使うかを指定します.

例えばsignal(int, int)とsignal(QString)という
2つのオーバーロードがあるシグナルがあるとします.
この場合は次のように使い分けます.

signal[int, int].connect(slot1)
signal['QString'].connect(slot2)

このようにC++に対応するPythonの型かC++の型を文字で
指定します.

シグナルを自作する

シグナルを自作するにはpyqtSignal()とemit()を使います.

シグナルを作成するにはQObjectを継承したクラスのクラス変数として
pyqtSignal()を定義します.
そして,emit()を呼ぶことでシグナルが発行されます.

pyqtSignal([types])

シグナルを作成します.

types:
シグナルの引数の型です.型はC++に対応するPythonの型,または
C++の型名の文字で指定します.オーバーロードがある場合,
それぞれの型はリストで指定します.
(一部引数を省略しています)

次はpySignal()を使ってシグナルを作成する例です.

from PyQt5.QtCore import QObject, pyqtSignal

class MyObject(QObject):
    # signal1という名前の引数のないシグナルを作成
    signal1 = pyqtSignal()
    
    # 引数を持つ場合
    # signal2という名前のint型の二つの引数を持つシグナルを作成
    signal2 = pyqtSignal(int, int)
    
    # オーバーロードがある場合
    # signal3という名前のint, strの引数持つシグナルと
    # QStringの引数を持つシグナルを作成
    signal3 = pyqtSignal([int, str], ['QString'])

emit(*args)

シグナルを発行します.
args:スロットに渡される引数です.

サンプルコード

例として,Aボタンが押されたときに
シグナルを発行するウィジェットを作り,
Aボタンが押されたときに閉じるようにしてみます.

#! /usr/bin/python3
# -*- coding: utf-8 -*-

import sys
from PyQt5.QtWidgets import QApplication, QWidget
from PyQt5.QtCore import Qt, pyqtSignal

class MyWidget(QWidget):
    # シグナルを作成
    pressedA = pyqtSignal()
    
    def __init__(self):
        super().__init__()
        self.init_ui()
        self.show()
        
    def init_ui(self):
        # シグナルとスロットを接続
        # Aボタンが押された->閉じる
        self.pressedA.connect(self.close)
    
    def keyPressEvent(self, e):
        if e.key() == Qt.Key_A:
            # Aボタンが押されたときシグナルを発行
            self.pressedA.emit()
            
if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = MyWidget()
    sys.exit(app.exec_())

スロットを自作する

Pythonの関数をスロットとして使用することができます.

サンプルコード

#! /usr/bin/python3
# -*- coding: utf-8 -*-

import sys
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton

class MyWidget(QWidget):
    def __init__(self):
        super().__init__()
        self.init_ui()
        self.show()
        
    def init_ui(self):
        self.button = QPushButton('button', self)

        # シグナルにスロットを接続
        self.button.clicked.connect(self.button_clicked)
        
    def button_clicked(self):
        """ 自作のスロット """
        print('button clicked')

if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = MyWidget()
    sys.exit(app.exec_())

シグナルを発行したオブジェクトを取得する

複数のシグナルに1つのスロットが設定されていたとき,
QObject.sender()を使うことでシグナルを発行した
オブジェクトを取得できます.

サンプルコード

2つのボタンどちらが押されたかを表示するプログラムを書いてみます.

#! /usr/bin/python3
# -*- coding: utf-8 -*-

import sys
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton, QVBoxLayout

class MyWidget(QWidget):
    def __init__(self):
        super().__init__()
        self.init_ui()
        self.show()
        
    def init_ui(self):
        self.button1 = QPushButton('1')
        self.button2 = QPushButton('2')

        layout = QVBoxLayout(self)
        layout.addWidget(self.button1)
        layout.addWidget(self.button2)

        # シグナルにスロットを接続
        self.button1.clicked.connect(self.button_clicked)
        self.button2.clicked.connect(self.button_clicked)
        
    def button_clicked(self):
        # シグナルを発行したオブジェクトを取得
        # buttonはself.button1かself.button2になる
        button = self.sender()
        if button is self.button1:
            print('button 1')
        elif button is self.button2:
            print('button 2')
        
if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = MyWidget()
    sys.exit(app.exec_())