マイブログ リスト

医療言語処理講座

2020年2月16日日曜日

Pyゼミ2.05 PyQt5によるラジオボタンとチェックボックス

ラジオボタンとチェックボックスの使い方を学ぼう.


Keyword:PyQt5,ラジオボタン,チェックボックス


画像のセグメンテーションにラベルを付けるようなとき,ラジオボタン(どれか一つ選択)やチェックボックス(複数選択)を使いたいときがあります.

インターネットを調べるとラジオボタンやチェックボックスについての記事がいろいろあるけど,実際使ってみようとするといろいろと問題が発生します.
  • チェック付けた情報をどうやって得るの?
  • チェックボックスを増やしたり減らしたり簡単にしたいよ.

これらを短いプログラムで解決していこうと思います.
具体的には次のような仕様になります.
  • 3つの部位をラジオボタンで選択する.
  • 5つの状態をチェックボックスで選択する.
  • OKボタンをクリックすると,チェックのついた情報がコンソールに表示する.
  • ラジオボタンやチェックボックスの項目はリストで持ち,増やしたり減らしたりできる.

GUIのイメージは次のようになります.




プログラム概要


はじめに'__main__'を見てみる

リストpartsに3つの部位が格納され,stateにはLevel1からLevel5までの5つの状態が格納されています.

    parts = [ 'Brain' , 'Lung' , 'Abdomen' ]
    state = [ 'Level1' , 'Level2' , 'Level3' , 'Level4' , 'Level5']

リスト内の値を自由に追加や編集することでいろいろ応用可能です.

この2つのリストを引数にCheckBox_RadioButtonクラスを呼びだします.


次に,イニシャライザ__init__を見てみる

partsとstateの要素数をそれぞれself.nPtとself.nStに代入しています.
そして,ラジオボタンを生成するself.radbtnをボタンの個数分のリストを作成しています(とりあえず空白" "を値としてリストを作成).
このリストにあとでラジオボタンを作成します.

    self.radbtn = list(" " * self.nPt)

同様にチェックボックスのボタンを生成するリストself.chkboxも作成しています.

部位と状態のリストをクラス内で使用するためself.partsとself.stateに代入しています.


initUIを見てみよう


最初に部位を選択するラジオボタンを作る.

”部位”を表示するラベルlblPrtを作成しします.
ラジオボタンは複数のラジオボタンから一つ選択しますが,複数ラジオボタンをグループにまとめるためradbtngrpを作成します.

    self.radbtngrp = QButtonGroup()

ラジオボタンや”部位”ラベルを配置するBoxLayoutを作成します.
縦に並べるので,QVBoxLayoutを使い,それにラベルlblPrtを配置します.

        vboxPrt = QVBoxLayout()
        vboxPrt.addWidget(lblPrt, alignment=Qt.AlignTop | Qt.AlignHCenter)

次に部位の数分(self.nPt)繰り返してi番目のラジオボタンをリストself.radbtn[i]に作成します.

    self.radbtn[i] = QRadioButton( self.parts[i])

このラジオボタンに対して,チェック可能かどうかsetCheckableをTrueに,フォーカスするかどうかsetFocusPolicyについてフォーカスしないに,そして,ラジオボタンをチェックしたときの動作をclicked.connectを使ってself.clickedRadbtn関数を呼ぶようにします.
ラジオボタンのグループradbtngrpにaddButtonを使って,i番目のラジオボタンself.radbtn[i]とIDのiの値を追加します.
このIDはどのボタンがチェックされているか調べるのに使われます.
そして最後にvboxPrtレイアウトにaddWidgetを使って配置している.

            self.radbtn[i] = QRadioButton( self.parts[i])
            self.radbtn[i].setCheckable(True)
            self.radbtn[i].setFocusPolicy(Qt.NoFocus)
            self.radbtn[i].clicked.connect(self.clickedRadbtn)                           
            self.radbtngrp.addButton(self.radbtn[i], i)
            vboxPrt.addWidget(self.radbtn[i], alignment=Qt.AlignTop)  


次に,状態を選択するチェックボックスを作る

最初に”状態”のラベルlblSttを作成し,チェックボックスを配置するレイアウトvboxSttに配置する.

状態の数分(self.nSt)繰り返して,i番目のチェックボックスをリストself.chkbox[i]に作成し,このチェックボックスがチェックされたときの動作をclicked.connectを使ってself.clickedCbxbtn関数を呼ぶようにし,最後にvboxSttレイアウトにaddWidgetを使って配置します.

            self.chkbox[i] = QCheckBox(self.state[i],self)
            self.chkbox[i].stateChanged.connect(self.clickedCbxbtn)
            vboxStt.addWidget(self.chkbox[i], alignment=Qt.AlignTop)

このようにfor文を使うことで簡単にボタンを追加することができます.


OKボタンを作る.

ボタンをチェックした後,情報を取得するアクションとしてOKボタンokbtnをQPushButtonを使って作ります.
このokbtnをクリックしたときの動作をclicked.connectを使って,self.clickedOkbtn関数を呼び出すようにします.
OKボタンokbtnを配置するだけですが,レイアウトokboxを作成して.これにOKボタンを配置します.


レイアウトを作成する.

最後にラジオボタンのレイアウトvboxPart,チェックボックスのレイアウトvboxSttを水平にレイアウトするためhboxをQHBoLayoutで作成しこれに2つのレイアウトを配置ます.

        hbox = QHBoxLayout()
        hbox.addLayout(vboxPrt)
        hbox.addLayout(vboxStt) 

最後に,上のhboxとokboxを縦に配置するためにQVBoxLayoutを使ってvboxを作成し,これにhboxとokboxをレイアウトします.

        vbox = QVBoxLayout()
        vbox.addLayout(hbox)
        vbox.addLayout(okbox)


ボタンをクリックしたときの動作する関数を作る


クリックしたチェックボックスを調べる関数clickedCbxbtn

この関数では,どのチェックボックスがチェックされたのかコンソールに表示します.
チェックボックスself.chkbox[i]にチェックあるかどうかcheckState()を使って調べて表示します.

        for i in range(self.nSt):
            print(self.state[i], self.chkbox[i].checkState())


クリックしたラジオボタンを調べる関数clickedRadbtn

この関数はチェックされたラジオボタンをコンソールに表示します.
ラジオボタンのグループradbtngrpのどれがクリックされたのか,checkedId()を使ってIDを取得します.
このIDを用いてリストpartsのどれがチェックされているか知ることができます.

        checkid = self.radbtngrp.checkedId()         
        print( "PART:", self.parts[checkid] )


OKボタンがクリックされたらボタンの情報を表示するclickedOkbtn関数

ボタンのクリックされた情報を集約し,変数rsltStrにまとめます.
はじめにラジオボタンのIDを取得し,部位名称をrsltStrに代入します.

        checkid = self.radbtngrp.checkedId()   
        rsltStr = self.parts[checkid]

次に,チェックボックスchkbox[i]をひとつずつcheckState()を使って調べ,もしチェックされていれば値2が返るので,そのインデックス+1を変数rsltStrに追加します.

        for i in range(self.nSt):
            if self.chkbox[i].checkState() > 0:
                rsltStr += ":" + str(i+1)   

この変数rsltStrを表示すると,ラジオボタンとチェックボックスの状態を知ることができます.

        print("RESULT:", rsltStr)

例えば図のようにチェックしたとすると,結果は次のようになります.

コンソールに表示される結果は次のようになります.

---------- Clicked RadioButton
PART: Lung
---------- Clicked Changebox
Level1 0
Level2 0
Level3 2
Level4 0
Level5 0
========== Clicked OK ==========
RESULT: Lung:3

プログラムを入力して実行してみよう


# -*- coding: utf-8 -*-
import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.Qt import *
from PyQt5 import QtCore, QtWidgets

class CheckBox_RadioButton(QWidget):
    def __init__(self, Parts, State):
        super().__init__()

        self.nPt    = len(Parts)
        self.nSt    = len(State)       
        self.radbtn = list(" " * self.nPt)
        self.chkbox = list(" " * self.nSt)
        self.parts  = Parts  
        self.state  = State
        self.initUI()

    def initUI(self):
        """ Radio Button """
        lblPrt         = QLabel("部位")
        self.radbtngrp = QButtonGroup()
        vboxPrt        = QVBoxLayout()
        vboxPrt.addWidget(lblPrt, alignment=Qt.AlignTop | Qt.AlignHCenter)
        for i in range(self.nPt):
            self.radbtn[i] = QRadioButton( self.parts[i])
            self.radbtn[i].setCheckable(True)
            self.radbtn[i].setFocusPolicy(Qt.NoFocus)
            self.radbtn[i].clicked.connect(self.clickedRadbtn)                          
            self.radbtngrp.addButton(self.radbtn[i], i)
            vboxPrt.addWidget(self.radbtn[i], alignment=Qt.AlignTop)   
       
        """ CheckBox """
        lblStt  = QLabel("状態")
        vboxStt = QVBoxLayout()
        vboxStt.addWidget(lblStt, alignment=Qt.AlignTop | Qt.AlignHCenter)
        for i in range(self.nSt):
            self.chkbox[i] = QCheckBox(self.state[i],self)
            self.chkbox[i].stateChanged.connect(self.clickedCbxbtn)
            vboxStt.addWidget(self.chkbox[i], alignment=Qt.AlignTop)

        """ OK Button """
        okbtn = QPushButton("OK")
        okbtn.clicked.connect(self.clickedOkbtn)
        okbox = QHBoxLayout()
        okbox.addWidget(okbtn)
       
        """ Layout """
        hbox = QHBoxLayout()
        hbox.addLayout(vboxPrt)
        hbox.addLayout(vboxStt)       
        vbox = QVBoxLayout()
        vbox.addLayout(hbox)
        vbox.addLayout(okbox)
       
        self.setLayout(vbox)   

        self.setGeometry(100, 75, 250, 250)
        self.setWindowTitle("RadioButton&CheckBox")   
        self.show()
       
    def clickedCbxbtn(self):
        print("-"*10, "Clicked Changebox")
        for i in range(self.nSt):
            print(self.state[i], self.chkbox[i].checkState())
           
    def clickedRadbtn(self):
        print("-"*10, "Clicked RadioButton")
        checkid = self.radbtngrp.checkedId()        
        print( "PART:", self.parts[checkid] )
           
    def clickedOkbtn(self):
        print("="*10,"Clicked OK", "="*10)
        checkid = self.radbtngrp.checkedId()  
        rsltStr = self.parts[checkid]
        for i in range(self.nSt):
            if self.chkbox[i].checkState() > 0:
                rsltStr += ":" + str(i+1)     
        print("RESULT:", rsltStr)
       
if __name__ == '__main__':
    parts = ['Brain','Lung','Abdomen']
    state = ['Level1','Level2','Level3','Level4','Level5']
   
    app = 0     #For Spyder
    app = QApplication(sys.argv)

    ex = CheckBox_RadioButton(parts, state)
    sys.exit(app.exec_())