yu00’s blog

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

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_())

ウィジェットの自動伸縮の設定

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


レイアウトでウィジェットを配置するとき,
ウィジェットは自動的に伸縮します.
ここでは自動伸縮の設定について説明します.

自動伸縮するかどうか

自動伸縮をするしないを設定するには,
QWidget.setSizePolicy()を使います.

QWidget.setSizePolicy(horizontalPolicy, verticalPolicy) -> none

QWidget.setSizePolicy(QSizePolicy) -> none

自動伸縮を設定します
horizontalPolicy, verticalPolicy:QSizePolicy.Policy型, 横方向,縦方向の自動伸縮

QSizePolicy.QSizePolicy(horizontalPolicy, verticalPolicy, type = DefaultType) -> QSizePolicy

horizontalPolicy, verticalPolicy:QSizePolicy.Policy型, 横方向,縦方向の自動伸縮


縦方向,横方向それぞれの自動伸縮は次のどれかを指定します.

QSizePolicy.Policy

QSizePolicy.Fixed
ウィジェットのサイズがQWidget.sizeHint()に固定されます.

QSizePolicy.Minimum
ウィジェットのサイズはsizeHint()以下になりません.
拡大はされます.

QSizePolicy.Maximum
ウィジェットのサイズはsizeHint()以上になりません.
縮小はされます.

QSizePolicy.Preferred
基本sizeHint()のサイズになりますが,拡大も縮小もします.

QSizePolicy.Expanding
基本sizeHint()のサイズになりますが,拡大も縮小します.
また,拡大のときは特別な領域も使用して
できるだけ大きくなるようにします.

QSizePolicy.MinimumExpanding
sizeHint()以下になりません.
拡大のときはできるだけ大きくなるようにします.

QSizePolicy.Ignored
ウィジェットのサイズをできるだけ大きくなるようにします.

サンプルコード

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

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

if __name__ == '__main__':
    app = QApplication(sys.argv)
    
    window = QWidget()

    button1 = QPushButton('1')
    button2 = QPushButton('2')
    
    # 自動伸縮を設定
    button1.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
    button2.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Expanding)
    
    layout = QHBoxLayout()
    layout.addWidget(button1)
    layout.addWidget(button2)
    window.setLayout(layout)
    
    window.show()
    sys.exit(app.exec_())

実行例

f:id:yu00:20150906212500p:plain
ボタン2は縦,横とも拡大します.

ウィジェットのサイズ比

2つウィジェット並べてサイズ比を1対2にしたいなど,各ウィジェットのサイズの比率
を変更するにはストレッチを設定します.
ストレッチはQSizePolicyで変更できます.
ただしウィジェットの最小サイズより小さくはならないので注意してください.

QSizePolicy.setHorizontalStretch(stretchFactor) -> none

横方向のストレッチを変更します.
stretchFactor:int型,サイズ比

QSizePolicy.setVerticalStretch(stretchFactor) -> none

縦方向のストレッチを変更します.

サンプルコード

ボタンを2対3の比率で横に配置するプログラムを書いてみます.

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

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

if __name__ == '__main__':
    app = QApplication(sys.argv)
    
    window = QWidget()

    button1 = QPushButton('1')
    button2 = QPushButton('2')

    # ボタンのサイズポリシーを取得
    sizePolicy1 = button1.sizePolicy()
    sizePolicy2 = button2.sizePolicy()
    # ストレッチを2対3にセット
    sizePolicy1.setHorizontalStretch(2)
    sizePolicy2.setHorizontalStretch(3)
    # サイズポリシーをセット
    button1.setSizePolicy(sizePolicy1)
    button2.setSizePolicy(sizePolicy2)
    
    layout = QHBoxLayout()
    layout.addWidget(button1)
    layout.addWidget(button2)
    window.setLayout(layout)
        
    window.show()
    sys.exit(app.exec_())

実行例

f:id:yu00:20150906222534p:plain

QWidgetクラス

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

PyQt5ではウェジェットでウィンドウを作成し,
その中にボタンやラベルなどの部品を配置していきます.
ここではウェジェットの基本的な使い方を説明します.

QWidgetを継承したクラスを作る

まずはわかりやすいように,QWidgetを継承した自分用のクラスを作ります.
QWidgetに関する設定はこの継承したクラス内で行います.

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

import sys
from PyQt5.QtWidgets import QApplication, QWidget

# QWidgetを継承したクラスを作る
class MyWidget(QWidget):
    def __init__(self):
        super().__init__()
        
        self.init_ui() # 初期設定
        
        self.show()
        
    def init_ui(self):
        """初期設定を行う"""
        pass # 後々この部分に初期設定を書く
    
if __name__ == '__main__':
    app = QApplication(sys.argv)
    
    window = MyWidget() # 自分のウィジェットを使う
    
    sys.exit(app.exec_())
    

ウィンドウの表示位置とサイズの変更

QWidget.setGeometry(x, y, w, h) -> none

ウィンドウの表示位置とサイズを変更します
x,y:int, ウィンドウx,y座標
w,h:int, ウィンドウ幅,高さ

ウィンドウタイトルの設定

QWidget.setWindowTitle(title) -> none

ウィンドウタイトルを設定します

サンプルコード
    def init_ui(self):
        self.setWindowTitle('My Title')
実行例

f:id:yu00:20150904180252p:plain

アイコンの設定

QWidget.setWindowIcon(icon) -> none

アイコンを設定します

サンプルコード
    def init_ui(self):
        self.setWindowIcon(QIcon('my-icon.png'))
実行例

f:id:yu00:20150904181917p:plain

ドキュメントの読み方

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

PyQt5の多くのクラスや関数はQtをラップしただけなので,
PyQt5を使ったプログラムを書く場合Qt本家のドキュメントを
参照することになると思います.
ここではQt本家のドキュメントの読み方を説明します.
ただ,リンク先の場所やドキュメントの形式が変わってしまう可能性はありますが...

次がよく見るページです.

リファレンストップページです.

ウィジェットとは何なのか,レイアウトなどの
ウィジェットに関する基本的な説明が書いてあります.

基本的なウィジェットのクラス,レイアウトクラスなど,
各クラスがカテゴリーごとに分類されて説明されています.

各クラスが説明されているページでは,基本的な関数やスロットとその説明などが
書かれています.