PythonのPyQtを使って実際にGUI開発を行ってみます。
2021年8月現在では、公式サイトにあるPyQtのバージョンが4、5、6と3つのバージョンがダウンロードすることができます。
この記事では、PyQt6を使っています。
PyQtとは

PyQTとは、クロスプラットフォームなGUIツールキットです。
QtのPythonをバインディングして、GUIプログラミングを行うことができます。
PyQTのインストールについては、こちらの記事をご確認ください。
-
-
Pythonでデスクトップアプリの開発!作り方を解説
Pythonでデスクトップアプリを開発するために、TkinterやKivy、PyQt、wxPythonの4つから使いやすいかどうか調べてみました。 アプリケーションの作成するために、ラベルや文字、ボタンの設置はカンタンにできるのか実際に試し ...続きを見る
GUIについて
GUIとは、Graphical User Interface(グラフィカル ユーザ インターフェース)の略で、ユーザが視覚的に操作しやすいようにします。
GUIは視覚的に操作しますが、反対にCUIというのもあります。
CUIについて
CUIは、Character User Interface(キャラクター ユーザ インターフェース)の略で、コマンドを使った操作になります。
Windowsではコマンドプロンプトを利用したものがCUIにあたります。
メインウインドウを使ってメイン画面の作成

メイン画面の作成を行います。
タイトルは「マイアプリ」というタイトルで、画面サイズは500 x 400のサイズで作成します。
メイン画面なので、QMainWindowを利用します。
import sys
from PyQt6.QtWidgets import QApplication, QMainWindow
app = QApplication(sys.argv)
main = QMainWindow()
main.setWindowTitle('マイアプリ')
main.resize(500, 400)
main.show()
sys.exit(app.exec())
画面サイズの設定は、9行目のresizeを使っています。
setGeometryを使ってサイズの設定もできますが、アプリケーション実行時の位置も設定しないといけません。
めんどくさいのでresizeで設定することで、アプリケーションの実行されると中央に表示されます(デフォルトが中央表示なんだと思われる)。
あとは、main.showを行うことで画面が表示されます。

メニューバーの作成

ほとんどのアプリケーションでは、メニューバーがあるので実際に作成してみます。
WindowsとMacでメニューの表示でハマったので、その部分についても解説します。
ソースコードは、レイアウト画面作成で利用したコードを使います。
import sys
from PyQt6.QtGui import QAction
from PyQt6.QtWidgets import QApplication, QMainWindow
def newFile():
print('close')
app = QApplication(sys.argv)
main = QMainWindow()
main.setWindowTitle('マイアプリ')
main.resize(501, 400)
newAct = QAction("&New", main, triggered=newFile)
menuBar = main.menuBar()
fileMenu = menuBar.addMenu("&File")
fileMenu.addAction(newAct)
main.setMenuBar(menuBar)
main.show()
sys.exit(app.exec())
メニューバーを作成するには、QMainWindowにあるmenuBarを呼び出してその中に追加するイメージになります。
18行目からがその処理になります。
menuBar.addMenuを使ってメニューを追加します。
それを元に、addActionを使ってボタンがクリックされた時のアクションをセットします。
Macの場合、このActionをセットしないとメニューにすら表示されない
WindowsとMacの結果は下記なります。


WindowsとMacでは、メニューバーの表示の仕方が違います。
Windowsはアプリケーションの中にありますが、Macは画面の上部にあります。
ですがコードに関してはWindowsとMacで分ける様な作りにする必要はないです。
入力関連のパーツ

ラベルや入力項目、ラジオボタン、チェックボックスなどの使い方をここでは試してみます。
紹介するパーツは、下記になります。
パーツ一覧
- ラベル
- ライン入力
- ボタン
- ラジオボタン
- チェックボックス
ラベル
ラベルを使うには、QLabelを使います。
setTextを使ってラベルの文字をセットしています。
from PyQt6.QtWidgets import QLabel
label = QLabel()
label.setText('ラベル')
QLabelに直接テキストをセットすることもできます。
from PyQt6.QtWidgets import QLabel
label = QLabel('ラベル')
こっちの方が行数も減って見やすいです。

Enableの設定を行うことができます。
from PyQt6.QtWidgets import QLabel
label = QLabel('ラベル')
label.setEnabled(False)
setEnabledにはTrueまたはFalseを指定することで、テキストの表示が変わります。
今回は、Falseの設定を行っているので灰色で薄くなっています。

フォントを使うことで、文字のサイズを変更することができます。
from PyQt6.QtGui import QFont
from PyQt6.QtWidgets import QLabel
font = QFont()
font.setPointSize(30)
label = QLabel('ラベル')
label.setFont(font)
フォントのサイズを30に設定しています。
作ったフォントをラベルにセットすることで、反映されます。

スタイルシートをセットすることで、テキストの色も変更することができます。
from PyQt6.QtWidgets import QLabel
label = QLabel('ラベル')
label.setFont(font)
label.setStyleSheet('QLabel { color: #FFcc55}')
5行目にあるsetStyleSheetでテキストのカラーを変更しています。

ここで疑問に思ったことがあります。
setStyleSheetでフォントのサイズも設定したらいけるんじゃね?ってことでやってみました。
from PyQt6.QtWidgets import QLabel
label = QLabel('ラベル')
label.setStyleSheet('QLabel { color: #FFcc55; font-size: 50px}')
これを実行してみたら、問題なくフォントのサイズ変わりました。
QColorでカラーの設定ができますが、スタイルシートの理解があればsetStyleSheetの方が断然便利です
Line Edit (1行入力)
テキスト入力などの項目を追加するには、QLineEditを利用します。
HTMLで例えるとinputタグになります。
通常の入力は下記になります。
from PyQt6.QtWidgets import QLineEdit
inputLine = QLineEdit()

パスワードで入力することもできます。
パスワードモードにするにはsetEchoModeにQLineEdit.EchoMode.Passwordをセットします。
from PyQt6.QtWidgets import QLineEdit
inputLine = QLineEdit()
inputLine.setEchoMode(QLineEdit.EchoMode.Password)

ボタン
ボタンを作成します。
ボタンでは、通常のボタンからトグルボタン、フラットボタンを作成することができます。
ボタンの作成はQPushButtonを使います。
from PyQt6.QtWidgets import QPushButton
btn1 = QPushButton('ボタン')
btn1.setDefault(True)
btn2 = QPushButton('ボタン2')
btn2.setCheckable(True)
btn2.setChecked(True)
btn3 = QPushButton('ボタン3')
btn3.setFlat(True)
setDefaultをTrueにするとボタンが押された状態になります。
ボタン2はトグルボタンになります。
トグルボタンにするには、setCheckableをTrueにする必要があります。
ボタン3がフラットボタンになっており、setFlatでフラットの設定を行います。


ボタン2はトグルボタンになっているので、1枚目と2枚目でボタンのクリックで表示が変わります。
ラジオボタン
ラジオボタンの作成をします。
ラジオボタンの追加はQRadioButtonを使いますが、単体だとパラメータの取得がめんどくさいのでQButtonGroupも合わせて使います。
from PyQt6.QtWidgets import QButtonGroup, QRadioButton
radioGroup = QButtonGroup()
radioBtn1 = QRadioButton('ラジオ1')
radioBtn2 = QRadioButton('ラジオ2')
radioBtn3 = QRadioButton('ラジオ3')
radioBtn1.setChecked(True)
radioGroup.addButton(radioBtn1, 1)
radioGroup.addButton(radioBtn2, 2)
radioGroup.addButton(radioBtn3, 3)
上記のコードは、QButtonGroupの中にQRadioButtonをセットしています。
setCheckedは、デフォルトでこの値にチェックしているようにしています。
パラメータの取得するのがめんどくさいと言ったのは、10行目からの処理を使うことでカンタンに値を取得することができます。
addButtonで番号を割り振ることで、radioGroupで何にチェックされているか1発で取得できます。
これをしないと、1つ1つradioBtn1からradioBtn3までの値をチェックする必要が出てくるからです。
めんどくさいですよね?ってことです。

チェックボックス
チェックボックスの作成を行います。
作成するには、QCheckBoxを利用します。
チェックボックスではQButtonGroupを利用しません。
利用した場合は、複数チェックすることができなくなり1つのチェックになります。
from PyQt6.QtWidgets import QCheckBox
check1 = QCheckBox('チェック1')
check2 = QCheckBox('チェック2')
check3 = QCheckBox('チェック3')
check1.setChecked(True)
check2.setChecked(True)
setCheckedで、チェックの有無を設定します。

レイアウト

レイアウトは複数あります。
レイアウトによって、縦に表示したり横に表示したりできます。
他にもグリッドレイアウトを使うことで、細かい配置の設定を行うことも可能です。
基本的なレイアウトは3つになります。
基本レイアウト
- QBoxLayout
- QGridLayout
- QFormLayout
QBoxLayoutの派生クラスとして、QHBoxLayoutとQVBoxLayoutがあります。
この2つが、横に表示したり縦に表示したりするレイアウトです。
MainWindowに直接レイアウトをセットすると下記のエラーが表示されます。
QWidget::setLayout: Attempting to set QLayout "" on QMainWindow "", which already has a layout
これが出ると、一旦Widgetに入れてそれを元にsetCentralWidgetでWidgetをセットしてあげます。
widget = QWidget()
widget.setLayout(layout)
mainWindow.setCertralWidget(widget)
MainWindowではレイアウトではなく、Widgetをセットすることになります。
MainWindowの構造は、ドキュメントをご確認ください。
QHBoxLayout
横並びに表示するレイアウトは、QHBoxLayoutを使います。
インポートする項目は、QtWidgetsのQHBoxLayoutになります。
from PyQt6.QtWidgets import QHBoxLayout, QLabel
layout = QHBoxLayout()
label1 = QLabel('テスト1')
label2 = QLabel('テスト2')
layout.addWidget(label1)
layout.addWidget(label2)
ラベルなどのWidgetをaddWidgetで追加することで横並びに追加されていきます。

QVBoxLayout
縦並びに表示するレイアウトは、QVBoxLayoutを使います。
インポートする項目は、QtWidgetsのQVBoxLayoutになります。
from PyQt6.QtWidgets import QVBoxLayout, QLabel
layout = QVBoxLayout()
label1 = QLabel('テスト1')
label2 = QLabel('テスト2')
layout.addWidget(label1)
layout.addWidget(label2
ラベルなどを追加することで縦に追加されていきます。

QGridLayout
グリッドレイアウトにするには、QGridLayoutを使います。
QHBoxLayoutやQVBoxLayoutは、addWidgetの時にWidgetを入れるだけでしたが、QGridLayoutは配置も指定する必要があります。
from PyQt6.QtWidgets import QGridLayout, QLabel
layout = QGridLayout()
label1 = QLabel('テスト1')
label2 = QLabel('テスト2')
label3 = QLabel('テスト3')
label4 = QLabel('テスト4')
layout.addWidget(label1, 1, 1)
layout.addWidget(label2, 1, 2)
layout.addWidget(label3, 2, 1)
layout.addWidget(label4, 2, 2)
addWidgetの引数は、セットするWidget、第二引数が行を表、第三引数が列を表します。
なので、上記のラベル1と2が同じ行になり、3と4が同じ行になります。

QFormLayout
QFormLayoutは2つの列が作成される便利なレイアウトです。
HTMLとかで、ラベルの横に入力フォームがある様なイメージをカンタンに作ることができます。
from PyQt6.QtWidgets import QVBoxLayout, QLabel, QFormLayout
layout = QVBoxLayout()
layout2 = QFormLayout()
layout3 = QFormLayout()
label1 = QLabel('テスト1')
input1 = QLineEdit()
label3 = QLabel('テスト3')
input2 = QLineEdit()
layout2.addRow(label1, input1)
layout3.addRow(label3, input2)
layout.addLayout(layout2)
layout.addLayout(layout3)
QFormLayoutはaddWidgetではなく、addRowを使ってセットしていきます。
このコードは、QFormLayoutを2つ作ってQVBoxLayoutにレイアウトを追加する処理です。

まとめ
PyQtを使ったGUI開発を試してみました。
メニューバーの作成や各ラベルやボタン、入力項目、チェックボックスなどパーツの追加についてカンタンに行うことができます。
MainWindowクラスにレイアウトを追加する場合に若干ハマりますが、独自のWindowを作ることで今風のUIとかも全然作れます。
この記事では書くことができなかった、ボタンを押した時のアクション処理や画面遷移については次回以降に書きます。