PySide2をゼロから学んでいく～#5 チェックボックス～

https://qiita.com/karakuri-t910/items/c5dbd714cd54520f415d

* [#0 環境構築](https://qiita.com/karakuri-t910/items/f0736a322a6ba217a36c)
* [#1 ウィンドウ](https://qiita.com/karakuri-t910/items/7d2aac29d979f2413162)
* [#2 ボタン](https://qiita.com/karakuri-t910/items/5aae0ba3bed05eeb5109)
* [#3 ラベル](https://qiita.com/karakuri-t910/items/1bdbcbf8b4b92555c123)
* [#4 ラジオボタン](https://qiita.com/karakuri-t910/items/e54e771c576d2a0aa11a)
* [#5 チェックボックス ← ここ](https://qiita.com/karakuri-t910/items/03debf242efd944eecc2)
* [#6 コンボボックス](https://qiita.com/karakuri-t910/items/bfd18dffbe2674a3b478)

# はじめに
PythonでGUI開発をするためのライブラリ「PySide2」の基本的な使い方を、いくつかの段階に分けて説明していきます。

当ページではPySide2のチェックボックスについて説明しています。

# 環境
以下の通りになります。

* Windows 10
* Python 3.8以降

# 公式リファレンス
[QCheckBox](https://doc.qt.io/qtforpython-5/PySide2/QtWidgets/QCheckBox.html)

# ベースプログラム
以下のウィンドウを表示するだけのプログラムをベースにして実装していきます。

```python
# PySide2のモジュールを読み込む
from PySide2 import QtWidgets


# ウィンドウの見た目と各機能（今はウィンドウだけ）
class MainWindow(QtWidgets.QWidget):
    def __init__(self):
        super().__init__()


# アプリの実行と終了
app = QtWidgets.QApplication()
window = MainWindow()
window.show()
app.exec_()
```

# PySide2 でチェックボックス

## チェックボックスを表示する
単に3つのチェックボックスを表示してみます。

### サンプルプログラム
```python
# PySide2のモジュールを読み込む
from PySide2 import QtWidgets


# ウィンドウの見た目と各機能
class MainWindow(QtWidgets.QWidget):
    def __init__(self):
        super().__init__()

        # チェックボックスオブジェクトの生成
        checkBox1 = QtWidgets.QCheckBox("First", self)
        checkBox2 = QtWidgets.QCheckBox("Second", self)
        checkBox3 = QtWidgets.QCheckBox("Third", self)

        # チェックボックスの配置
        checkBox1.move(10, 0)
        checkBox2.move(10, 30)
        checkBox3.move(10, 60)


# アプリの実行と終了
app = QtWidgets.QApplication()
window = MainWindow()
window.show()
app.exec_()
```

### 実行結果
![000.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/614920/3e74169e-14ec-e31b-c48c-54d199f576bf.png)


### プログラム解説
<details><summary>解説を見る</summary><div>
<b>● チェックボックスオブジェクトの生成</b>
チェックボックスを表示するために`QtWidgets.QCheckBox()`でチェックボックスオブジェクトを取得します。

```
書式:
    QtWidgets.QCheckBox("string", window_object)
引数:
    string:
        チェックボックスの文字列
    window_object:
        チェックボックスを表示させるウィンドウ
戻り値:
    チェックボックスのオブジェクト
```

`QtWidgets.QCheckBox()`クラスの第一引数`string`にチェックボックスの隣に表示させたい文字列を記入します。第二引数`window_object`はどのウィンドウに表示させるかを指定します。複数のウィンドウを表示しないなら`self`を指定します。

サンプルプログラムではメインウィンドウに3つのチェックボックスを表示しています。

```python
# チェックボックスオブジェクトの生成
checkBox1 = QtWidgets.QCheckBox("First", self)
checkBox2 = QtWidgets.QCheckBox("Second", self)
checkBox3 = QtWidgets.QCheckBox("Third", self)
```

<b>● チェックボックスの配置</b>
他のウィジェットと同様に`move()`メソッドで配置場所を指定します。

```
書式:
    QtWidgets.QCheckBox("string", window_object).move(x, y)
引数:
    x:
        x座標
    y:
        y座標
```

配置場所もx座標、y座標で指定します。これは他のウィジェットでも同じです。

サンプルプログラムでは生成したチェックボックスオブジェクトの全てが同じ場所に表示されないように、各々のy座標を30pxずつズラしています。

```python
# チェックボックスの配置
checkBox1.move(10, 0)
checkBox2.move(10, 30)
checkBox3.move(10, 60)
```
</div>

## チェックボックスの初期入力状態の設定
ラジオボタン同様にチェックボックスにも初期入力を設定することができます。

### サンプルプログラム
```python
# PySide2のモジュールを読み込む
from PySide2 import QtWidgets


# ウィンドウの見た目と各機能
class MainWindow(QtWidgets.QWidget):
    def __init__(self):
        super().__init__()

        # チェックボックスオブジェクトの生成
        checkBox1 = QtWidgets.QCheckBox("First", self)
        checkBox2 = QtWidgets.QCheckBox("Second", self)
        checkBox3 = QtWidgets.QCheckBox("Third", self)

        # 1番目と3番目を最初からチェックを入れておく
        checkBox1.setChecked(True)
        checkBox3.setChecked(True)

        # チェックボックスの配置
        checkBox1.move(10, 0)
        checkBox2.move(10, 30)
        checkBox3.move(10, 60)


# アプリの実行と終了
app = QtWidgets.QApplication()
window = MainWindow()
window.show()
app.exec_()
```

### 実行結果
![001.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/614920/8671467b-b576-ca0d-0a4e-accba2acdbd6.png)


### プログラム解説
<details><summary>解説を見る</summary><div>
<b>● チェックボックスの初期値の処理</b>
`QtWidgets.QCheckBox()`クラスで生成したオブジェクトに`setChecked()`メソッドが存在しています。このメソッドを用いて初期入力値を設定します。

```
書式:
    QtWidgets.QCheckBox("string", window_object).setChecked(bool)
引数:
    bool:
        「True」または「False」を指定。
        初期入力状態にする場合はTrueを引数に指定。
```

`setChecked()`メソッドの引数に`True/False`を指定することで最初からチェックが入っている状態にできます。

サンプルプログラムでは1番目の`First`と3番目の`Third`にチェックを入れております。

```python
# 1番目と3番目を最初からチェックを入れておく
checkBox1.setChecked(True)
checkBox3.setChecked(True)
```
</div>

## チェックの有無を取得

### サンプルプログラム
```python
# PySide2のモジュールを読み込む
from PySide2 import QtWidgets
from PySide2 import QtCore


# ウィンドウの見た目と各機能
class MainWindow(QtWidgets.QWidget):
    def __init__(self):
        super().__init__()

        # チェックボックスオブジェクトの生成
        checkBox1 = QtWidgets.QCheckBox("First", self)
        checkBox2 = QtWidgets.QCheckBox("Second", self)
        checkBox3 = QtWidgets.QCheckBox("Third", self)

        # 1番目と3番目を最初からチェックを入れておく
        checkBox1.setChecked(True)
        checkBox3.setChecked(True)

        # チェックの有無を取得
        print("First:",  checkBox1.checkState() == QtCore.Qt.Checked)
        print("Second:", checkBox2.checkState() == QtCore.Qt.Checked)
        print("Third:",  checkBox3.checkState() == QtCore.Qt.Checked)

        # チェックボックスの配置
        checkBox1.move(10, 0)
        checkBox2.move(10, 30)
        checkBox3.move(10, 60)


# アプリの実行と終了
app = QtWidgets.QApplication()
window = MainWindow()
window.show()
app.exec_()
```

### 実行結果
![002.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/614920/cccc7080-5ed8-6fd6-75ee-460dedd5ef4a.png)


### プログラム解説
<details><summary>解説を見る</summary><div>
<b>● チェックの有無を取得する</b>
チェックボックスのチェック有無は`checkState()`メソッドで確認できます。

```
書式:
    QtWidgets.QCheckBox("string", window_object).checkState()
引数:
    なし
戻り値:
    Qt CheckState
```

`checkState()`メソッドはQtが独自で定義している「Qt CheckState」を戻り値として返します。Qt CheckStateはチェックボックスの選択状態を表す定数です。

Qt CheckStateの詳細は以下の通りです。

```
importモジュール:
    PySide2.QtGui
書式:
    QtCore.Qt.Unhecked（値: 0）
        チェックボックスが外されている状態。
    QtCore.Qt.PartiallyChecked（値: 1）
        チェックボックスで部分的に選択されている状態。
    QtCore.Qt.Checked（値: 2）
        チェックボックスが選択されている状態。
```

> `QtCore.Qt.PartiallyChecked`は一つの例としてツリー構造のチェックボックス（[こぼれ話](#pushpinツリー構造のチェックボックスってあったよね？)）の際に使用します。この話は結構難解なのでここでは割愛します。

単純な話ですが、`checkState()`メソッドはチェックボックスにチェックが入っていれば`QtCore.Qt.Checked`を戻り値として返し、入っていなければ`QtCore.Qt.Unhecked`を返すだけです。

サンプルプログラムではチェックが入っていればTrueと表示し、入っていなければFalseと表示するだけです。

```python
# チェックの有無を取得
print("First:",  checkBox1.checkState() == QtCore.Qt.Checked)
print("Second:", checkBox2.checkState() == QtCore.Qt.Checked)
print("Third:",  checkBox3.checkState() == QtCore.Qt.Checked)
```

</div>

## チェックされた瞬間を判定
たまにチェックボックスをチェックした瞬間に処理が実行されるアプリがあります。そのチェックした瞬間の状態を判定してみます。

### サンプルプログラム
```python
# PySide2のモジュールを読み込む
from PySide2 import QtWidgets
from PySide2 import QtCore


# ウィンドウの見た目と各機能
class MainWindow(QtWidgets.QWidget):
    def __init__(self):
        super().__init__()
        self.setGeometry(300, 300, 500, 300)

        # ラベルオブジェクトの生成とフォント設定
        self.label = QtWidgets.QLabel("チェックボックスの入力状態で表示が変化します。", self)
        self.setStyleSheet("QLabel { font-size: 24px; }")
        self.label.move(10, 100)

        # チェックボックスオブジェクトの生成と配置
        checkBox = QtWidgets.QCheckBox("Check me!!", self)
        checkBox.move(10, 10)

        # チェックボックスの入力状態に応じて関数を実行
        checkBox.stateChanged.connect(lambda: self.checkboxChange(checkBox.checkState()))


    # チェックされた瞬間に実行される関数（ラベルの色を変えるだけ）
    def checkboxChange(self, state):
        if state == QtCore.Qt.Checked:
            self.label.setStyleSheet("""QLabel {
                color: green;
                background-color: lime;
            }""")
            self.label.setText("チェックしました！！")
        elif state == QtCore.Qt.Unchecked:
            self.label.setStyleSheet("""QLabel {
                color: red;
                background-color: pink;
            }""")
            self.label.setText("チェックされていません・・・")
        else:
            self.label.setText("Unknown State.")


# アプリの実行と終了
app = QtWidgets.QApplication()
window = MainWindow()
window.show()
app.exec_()
```

### 実行結果
![000.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/614920/190bb3a0-5c34-6a51-cdad-f178d132c439.gif)

### プログラム解説
<details><summary>解説を見る</summary><div>
<b>● 入力状態に応じて関数を呼び出す</b>
チェックボックスの入力状態に応じて`stateChanged`シグナルを送ることができます。この`stateChanged`シグナルを用いて関数を呼び出します。

```
書式:
    QtWidgets.QCheckBox("string", window_object).stateChanged.connect(lambda: slot_function(qt_checkState))
引数:
    slot_function:
        チェックボックスの入力状態が変化したら呼び出す関数名（スロット関数）
    qt_checkstate:
        Qt CheckState、つまりチェックボックスの入力状態。
        基本的に QtWidgets.QCheckBox("string", window_object).checkState() メソッドを指定する
```

書式を見ても何のことなのか分かりにくいのでサンプルプログラムを中心に解説していきます。サンプルプログラムの関数を呼び出す処理は以下になります。

```python
# チェックボックスの入力状態に応じて関数を実行
checkBox.stateChanged.connect(lambda: self.checkboxChange(checkBox.checkState()))
```

`stateChanged.connect()`メソッドでチェックボックスの入力状態が変化したら`checkboxChange()`メソッドを

<b>○ なんで無名関数`lambda`を使ってるの？</b>
スロット関数に**引数を指定したいから**です。
もし引数を指定しなければ関数名だけを指定すれば良いです。

```python
# スロット関数に引数が必要ない場合
checkBox.stateChanged.connect(self.checkboxChange)
```

引数がある場合、サンプルプログラムのように無名関数`lambda`を指定すればいいことだけ知っておけばOKです。
</div>

# こぼれ話

## :pushpin:ツリー構造のチェックボックスってあったよね？
ツリー構造のチェックボックスとは以下の図のようなものです。

![xxx.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/614920/323aaea1-d098-8b30-fb61-e681116a521a.png)


この形式のチェックボックスは結構難解なので別のページにて解説しようと思います。正直かなり読み応えのあるプログラムになっちゃいます。

ヒントだけ言うと今回使用した`QtWidgets.QCheckBox()`クラスではなく、`QtWidgets.QTreeWidget()`クラスを使用することになります。