しまぞうブログ

プログラミングと資産運用

【Python】PyQt(PySide)でGUIを作成する【QtDesigner】

PythonGUIライブラリにはいろんなものがありますね。
調べるとTkinter, Kivy, PyQt, wxPythonが有名どころのようです。

その中でも私はPyQt(PySide)を使うことにしました。
主な理由 は以下の3点です。

  • QtDesigner がVBAのユーザフォームのような見た目で使いやすそう
  • 日本語の記事が割と揃ってる(調べてもわからないのはツライ)
  • 環境構築がラク(anacondaだとQtDesignerが同梱されている)

試しにPySideを使用して簡単なGUIを作成しました。
わからなくなった時にすぐに調べられるようにブログに残しておきます。

環境構築

作業環境

・Windows10
・Anaconda 4.10(scoopでインストール)
Python 3.8

PyQt(PySide)のインストール

はじめにPyQt(PySide)をインストールします。

PyQt商用利用する場合は有償らしいです。
私は商用利用する予定はありませんが、念のためPySideにしました。使い方はPyQtと同じです。

AnacondaでPySideをインストールするには「anaconda pyside」とかで検索すると、それらしいパッケージが見つかると思います。

今回は「conda-forge pyside2」を使用します。
コマンドプロンプトで以下のように打ち込んでインストールします。

conda install -c conda-forge pyside2

QtDesignerでフォームを作成する

視覚的にフォームを作成するためにQtDesignerを使います。

準備

Anacondaを使っていれば、どこかに「designer.exe」というものが眠っています。これがQtDesignerそのものです。
インストールなどの作業は不要です。

私の場合「(ユーザ)\scoop\apps\anaconda3\current\Library\bin」の中にdesigner.exeがありました。

使い方

QtDesignerの基本的な使い方は以下のサイトに記載があります。
Anaconda附属のPyQt5とQtDesigerにてGUI作成

レイアウト関係がイマイチ理解できなかったので、以下のYoutubeを参考にしました。
英語ですが、画面の動きを見ていればなんとなく使い方がつかめるかと思います。

www.youtube.com

QtDesignerは初めは戸惑う部分もありますが、直感的に使えてわかりやすいです。
細かいところを気にしなければ、簡単にフォームを作成できます。

作成例

サンプルとして以下のようなフォームを作成しました。
ボタンをクリックするとTextLabelのテキストの表示が変わるようにします。

QtDesignerで作成したものを保存すると.uiという拡張子のファイルが作成されます。

.uiを.pyに変換

Pythonで実行するには、.uiファイルを.pyファイルに変換する必要があります。その手順を説明します。

変換用のexeを探す

PyQt5ならpyqt5-uic、PySide2ならpyside-uicを使用します。

私の環境ではこれらのファイルは
「 (ユーザ) \scoop\apps\anaconda3\current\Library\bin\pyuic5」
「(ユーザ)\scoop\persist\anaconda3\envs\(環境名)\Library\bin\pyside2-uic」
にありました。見つからない場合は検索するとよいです。

その際、Windowsの設定によっては検索対象となっていない場合があるので、設定を直しましょう。私の場合、以下のサイトの「検索インデックスを再構築する」の部分に記載の設定を変更する必要がありました。

office-hack.com

変換コマンド

保存場所を確認したら、以下のコマンドで変換します(PySideの場合の例)。
*.uiは変換したい.uiファイル、*.pyは変換後のファイル名です。

pyside2-uic *.ui -o *.py

pyside2-uicにパスが通っていない場合は、以下のようにします。

(ユーザ)\scoop\persist\anaconda3\envs\(環境名)\Library\bin\pyside2-uic *.ui -o *.py

試しに実行してみると、同フォルダに.pyファイルが生成されました。
中身はこんな感じです。

# -*- coding: utf-8 -*-

# Form implementation generated from reading ui file ,
# licensing of *** applies.
#
# Created: Sun Aug 29 11:03:07 2021
#      by: pyside2-uic  running on PySide2 5.13.2
#
# WARNING! All changes made in this file will be lost!

from PySide2 import QtCore, QtGui, QtWidgets

class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(365, 303)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.verticalLayout = QtWidgets.QVBoxLayout(self.centralwidget)
        self.verticalLayout.setObjectName("verticalLayout")
        self.horizontalLayout = QtWidgets.QHBoxLayout()
        self.horizontalLayout.setObjectName("horizontalLayout")
        self.pushButton = QtWidgets.QPushButton(self.centralwidget)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Preferred)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.pushButton.sizePolicy().hasHeightForWidth())
        self.pushButton.setSizePolicy(sizePolicy)
        self.pushButton.setObjectName("pushButton")
        self.horizontalLayout.addWidget(self.pushButton)
        self.pushButton_2 = QtWidgets.QPushButton(self.centralwidget)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Preferred)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.pushButton_2.sizePolicy().hasHeightForWidth())
        self.pushButton_2.setSizePolicy(sizePolicy)
        self.pushButton_2.setObjectName("pushButton_2")
        self.horizontalLayout.addWidget(self.pushButton_2)
        self.pushButton_3 = QtWidgets.QPushButton(self.centralwidget)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Preferred)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.pushButton_3.sizePolicy().hasHeightForWidth())
        self.pushButton_3.setSizePolicy(sizePolicy)
        self.pushButton_3.setObjectName("pushButton_3")
        self.horizontalLayout.addWidget(self.pushButton_3)
        self.verticalLayout.addLayout(self.horizontalLayout)
        self.label = QtWidgets.QLabel(self.centralwidget)
        self.label.setLayoutDirection(QtCore.Qt.LeftToRight)
        self.label.setAlignment(QtCore.Qt.AlignCenter)
        self.label.setObjectName("label")
        self.verticalLayout.addWidget(self.label)
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 365, 21))
        self.menubar.setObjectName("menubar")
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)

        self.retranslateUi(MainWindow)
        QtCore.QObject.connect(self.pushButton, QtCore.SIGNAL("clicked()"), MainWindow.showHiragana)
        QtCore.QObject.connect(self.pushButton_2, QtCore.SIGNAL("clicked()"), MainWindow.showKanji)
        QtCore.QObject.connect(self.pushButton_3, QtCore.SIGNAL("clicked()"), MainWindow.showEnglish)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        MainWindow.setWindowTitle(QtWidgets.QApplication.translate("MainWindow", "MainWindow", None, -1))
        self.pushButton.setText(QtWidgets.QApplication.translate("MainWindow", "ひらがな", None, -1))
        self.pushButton_2.setText(QtWidgets.QApplication.translate("MainWindow", "漢字", None, -1))
        self.pushButton_3.setText(QtWidgets.QApplication.translate("MainWindow", "英語", None, -1))
        self.label.setText(QtWidgets.QApplication.translate("MainWindow", "TextLabel", None, -1))
memo

出来上がった.pyを編集しても、QtDesignerで編集して新しく.uiを生成したら消えてしまいます。なので、.pyを編集するのはお勧めしません。

.uiから.pyへ変換する際に毎回コマンドを打つのが面倒という方は、右クリックメニューに登録するとラクです。登録方法は以下の記事を参考にしてください。

shimazoh.hatenablog.com

表示テスト

GUIを表示するには以下のようなプログラムを実行します。

import sys
from PySide2.QtWidgets import QApplication
from PySide2.QtWidgets import QMainWindow
from sample import Ui_MainWindow
 
class Test(QMainWindow, Ui_MainWindow):
  def __init__(self,parent=None):
    super(Test, self).__init__(parent)
    self.setupUi(self)

  def showHiragana(self):
    # ひらがなを押したとき
    return

  def showKanji(self):
    # 漢字を押したとき
    return

  def showEnglish(self):
    # 英語を押したとき
    return

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

showHiragana(), showKanji(), showEnglish()は「ひらがな」「漢字」「英語」ボタンを押したら実行する関数です。ここの動作は後で作成します。

上記のプログラムを実行すると、QtDesignerで作成したとおりに表示されました。

クリック時の動作を定義する

「ひらがな」「漢字」「英語」ボタンを押した際の動作を、showHiragana(), showKanji(), showEnglish()という関数に定義していきます。

各ボタンの動作は以下の通りとします。

  • ひらがな:TextLabelを”てすと”に変更
  • 漢字:TextLabelを”試験”に変更
  • 英語:TextLabelを”Test”に変更

これをプログラムにすると以下のようになります。

import sys
from PySide2.QtWidgets import QApplication
from PySide2.QtWidgets import QMainWindow
from sample import Ui_MainWindow
 
class Test(QMainWindow, Ui_MainWindow):
  def __init__(self,parent=None):
    super(Test, self).__init__(parent)
    self.setupUi(self)

  def showHiragana(self):
    self.label.setText('てすと')
    return

  def showKanji(self):
    self.label.setText('試験')
    return

  def showEnglish(self):
    self.label.setText('Test')
    return

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

11行目~21行目が各ボタンに対する動作になります。

self.labelという変数が画面上のTextLabelの内部変数で、表示文字列を変更するにはSetText()メソッドを使用します。

各フォームのメソッドについては、英文の公式ページ(以下リンク)に載っています。
日本語の良いまとめサイトがあれば、どなたか教えてください…

PySide2.QtWidgets — Qt for Python

実際に実行するとちゃんと文字列が変わりました。

まとめ

PythonGUIライブラリの1つであるPyQt(PySide)を使ってみました。

他のGUIライブラリを使ったことがないので実際のところよくわかりませんが、PyQt(PySide)は環境構築もラクで、初心者でも簡単に使えるのかなと思います。

また、試しに文字認識をするGUIアプリを作ってみました。 PyQt(PySide)の使用方法に迷われている方は、こちらの記事も参考になると思います。

shimazoh.hatenablog.com