PyQt5勉強③

# はじめに

前回から、QtDesignerを使ってGUIを作成しています。

https://qiita.com/kiryu-3/items/6b45db42daa59941e93f

今回は、部品それぞれにイベントを設定していきます。

## 前回の復習
[前回](https://qiita.com/kiryu-3/items/6b45db42daa59941e93f)までは、以下のようなフォームを作成してきました。 
 
![QtDesigner1](https://imgur.com/OU9dYfO.png)

今回もこのフォームをそのまま使います。

## Signals & Slotsとは

Signals & Slotsは、Qt Frameworkで使われる、イベントの発生と処理を簡単にするためのメカニズムです。 
 QtDesignerのSignals & Slotsを利用すると、GUIアプリケーションの作成が簡単になります。

Signalsは、イベントを発生させるオブジェクトが送信するシグナルであり、Slotsは、イベントを受信して処理するオブジェクトが持つスロットです。  
これらのシグナルとスロットを接続することで、シグナルが発生したときに、スロットで定義された処理が自動的に呼び出されます。

QtDesignerのSignals & Slotsは、ユーザーインターフェースを設計する際に非常に便利であり、コードを書くことなくGUIアプリケーションを作成することができます。

### Signals & Slotsの設定
現時点での、オブジェクトインスペクタとシグナル/スロットエディタを示します。  
オブジェクト名は一部変更しています。

![SignalsSlots①](https://imgur.com/ckT7VEy.png)　![SignalsSlots②](https://imgur.com/aombJlS.png)

シグナル/スロットエディタで、以下のように設定します。
![SignalsSlots③](https://imgur.com/VphmjeK.png)

PushButtonの"Submit"に"clicked"シグナルを設定します。  
次にLineEditの"EmailAdress"と"Password"に"clear"スロットを設定します。

この状態で.uiファイルを保存し、"pyuic5"コマンドを実行します。
「Submitボタンを押すと、フォームに入力していた内容がクリアになる」という処理を、一切コードを書かずに行うことができます。

``` sample1.py
# -*- coding: utf-8 -*-

# Form implementation generated from reading ui file 'form3.ui'
#
# Created by: PyQt5 UI code generator 5.15.9
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again.  Do not edit this file unless you know what you are doing.


from PyQt5 import QtCore, QtGui, QtWidgets


class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(260, 230)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.label_2 = QtWidgets.QLabel(self.centralwidget)
        self.label_2.setGeometry(QtCore.QRect(10, 10, 71, 16))
        self.label_2.setObjectName("label_2")
        self.EmailAdress = QtWidgets.QLineEdit(self.centralwidget)
        self.EmailAdress.setGeometry(QtCore.QRect(10, 30, 221, 20))
        self.EmailAdress.setObjectName("EmailAdress")
        self.label_3 = QtWidgets.QLabel(self.centralwidget)
        self.label_3.setGeometry(QtCore.QRect(10, 60, 241, 16))
        self.label_3.setObjectName("label_3")
        self.label_4 = QtWidgets.QLabel(self.centralwidget)
        self.label_4.setGeometry(QtCore.QRect(10, 90, 71, 16))
        self.label_4.setObjectName("label_4")
        self.Password = QtWidgets.QLineEdit(self.centralwidget)
        self.Password.setGeometry(QtCore.QRect(10, 110, 221, 20))
        self.Password.setEchoMode(QtWidgets.QLineEdit.Password)
        self.Password.setObjectName("Password")
        self.Submit = QtWidgets.QPushButton(self.centralwidget)
        self.Submit.setGeometry(QtCore.QRect(10, 160, 75, 23))
        font = QtGui.QFont()
        font.setBold(True)
        font.setWeight(75)
        self.Submit.setFont(font)
        self.Submit.setStyleSheet("background-color:#D3DEF1;\n"
"")
        self.Submit.setObjectName("Submit")
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 260, 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)

        # ボタンがクリックされた時の処理を設定する
        self.Submit.clicked.connect(self.EmailAdress.clear) # type: ignore
        self.Submit.clicked.connect(self.Password.clear) # type: ignore

        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
        self.label_2.setText(_translate("MainWindow", "Email Adress"))
        self.label_3.setText(_translate("MainWindow", "We\'ll never share your email with anyone else."))
        self.label_4.setText(_translate("MainWindow", "Password"))
        self.Submit.setText(_translate("MainWindow", "Submit"))


if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    MainWindow = QtWidgets.QMainWindow()
    ui = Ui_MainWindow()
    ui.setupUi(MainWindow)
    MainWindow.show()
    sys.exit(app.exec_())
```
![SignalsSlots④](https://imgur.com/ip98oL9.png)

###  よく使われるSignal

1. clicked()
QPushButtonやQToolButtonなど、ボタンがクリックされたときに発行されるSignalです。  
これに紐づけたSlotを定義することで、ボタンがクリックされたときに任意の処理を行うことができます。

2. valueChanged()
QSpinBoxやQSliderなど、値が変更されたときに発行されるSignalです。  
これに紐づけたSlotを定義することで、値が変更されたときに任意の処理を行うことができます。

3. textChanged()
QLineEditなど、テキストが変更されたときに発行されるSignalです。  
これに紐づけたSlotを定義することで、テキストが変更されたときに任意の処理を行うことができます。

4. currentIndexChanged()
QComboBoxやQTabWidgetなど、現在選択されているアイテムが変更されたときに発行されるSignalです。  
これに紐づけたSlotを定義することで、アイテムが変更されたときに任意の処理を行うことができます。

5. triggered()
QActionやQShortcutなど、アクションがトリガーされたときに発行されるSignalです。  
これに紐づけたSlotを定義することで、アクションがトリガーされたときに任意の処理を行うことができます。

6. customSignal
Qtでは、自分でSignalを定義することもできます。これを使うことで、独自のイベントを発行することができます。

## 新しいウィンドウの表示

Signals & Slotsは、Qtの強力な機能の1つですが、すべてのことをカバーするわけではありません。  
特に、2つ以上のウィンドウを扱う場合には、Pythonコードを使用して直接制御する必要がある場合があります。

#### WarningとInformationの表示

今回は、EmailAdressとPasswordのいづれかが入力されていない状態でSubmitボタンが押されたときは**Warning**、  
両方が一文字でも入力されたことが確認できた時は**Information**を表示するようにします。

"pyuic5"コマンドを実行します。
``` sample2.py
# -*- coding: utf-8 -*-

# Form implementation generated from reading ui file 'form.ui'
#
# Created by: PyQt5 UI code generator 5.15.9
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again.  Do not edit this file unless you know what you are doing.


from PyQt5 import QtCore, QtGui, QtWidgets


class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(260, 210)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.label_2 = QtWidgets.QLabel(self.centralwidget)
        self.label_2.setGeometry(QtCore.QRect(10, 10, 71, 16))
        self.label_2.setObjectName("label_2")
        self.EmailAdress = QtWidgets.QLineEdit(self.centralwidget)
        self.EmailAdress.setGeometry(QtCore.QRect(10, 30, 221, 20))
        self.EmailAdress.setObjectName("EmailAdress")
        self.label_3 = QtWidgets.QLabel(self.centralwidget)
        self.label_3.setGeometry(QtCore.QRect(10, 60, 241, 16))
        self.label_3.setObjectName("label_3")
        self.label_4 = QtWidgets.QLabel(self.centralwidget)
        self.label_4.setGeometry(QtCore.QRect(10, 90, 71, 16))
        self.label_4.setObjectName("label_4")
        self.Password = QtWidgets.QLineEdit(self.centralwidget)
        self.Password.setGeometry(QtCore.QRect(10, 110, 221, 20))
        self.Password.setEchoMode(QtWidgets.QLineEdit.Password)
        self.Password.setObjectName("Password")
        self.Submit = QtWidgets.QPushButton(self.centralwidget)
        self.Submit.setGeometry(QtCore.QRect(10, 160, 75, 23))
        font = QtGui.QFont()
        font.setBold(True)
        font.setWeight(75)
        self.Submit.setFont(font)
        self.Submit.setStyleSheet("background-color:#D3DEF1;\n""
")
        self.Submit.setObjectName("Submit")
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 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)

        # ボタンがクリックされた時の処理を設定する
        # 変更後
        self.Submit.clicked.connect(self.handle_submit) # type: ignore

        """ 
        # 変更前
        self.Submit.clicked.connect(self.EmailAdress.clear) # type: ignore
        self.Submit.clicked.connect(self.Password.clear) # type: ignore
        """
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
        self.label_2.setText(_translate("MainWindow", "Email Adress"))
        self.label_3.setText(_translate("MainWindow", "We\'ll never share your email with anyone else."))
        self.label_4.setText(_translate("MainWindow", "Password"))
        self.Submit.setText(_translate("MainWindow", "Submit"))

    # ボタンがクリックされた時の処理を設定する
    def handle_submit(self):
        # Emailアドレスとパスワードを取得する
        email = self.EmailAdress.text()
        password = self.Password.text()

        # Emailアドレスとパスワードがどちらも入力されているか確認する
        if not email or not password:
            # 入力が不完全であることをユーザーに知らせる
            QtWidgets.QMessageBox.warning(None, "Warning", "Please enter both email address and password.")
        else:
            # 入力が完全であることをユーザーに知らせる
            QtWidgets.QMessageBox.information(None, "Information", "Sending information...")

        # Emailアドレスとパスワードをクリアする
        self.EmailAdress.clear()
        self.Password.clear()


if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    MainWindow = QtWidgets.QMainWindow()
    ui = Ui_MainWindow()
    ui.setupUi(MainWindow)
    MainWindow.show()
    sys.exit(app.exec_())

```

このようにウィンドウを直接制御しました。以下のようなウィンドウが表示されるはずです。

![Warning①](https://imgur.com/NUxe5zP.png)　![Warning②](https://imgur.com/HYjyiUV.png)  
![Information①](https://imgur.com/uq8fJ6Y.png)　![Information②](https://imgur.com/jg1nhip.png)  

# 最後に

これでQt Designerを使ってのGUI作成は終了です。

次回は別軸で動いてきたfoliumと組み合わせていきます。

https://qiita.com/kiryu-3/items/d1388a6a584c3f26d914

# 参考
https://www.tutorialspoint.com/pyqt5/pyqt5_signals_and_slots.htm