2016/11/09(水) [n年前の日記]
#1 [python] PySideでツールパネルっぽいソレを作成できないかテスト
Photoshop や GIMP等の、ツールパネル?っぽいソレをPySideで作れるかどうかテスト。…ツールパネルって呼び方でいいのかな? ツールパレット? なんて呼ぶんだろアレ。要は、どれか一つ、ツールを選ぶと ―― どれか一つボタンを押すと、他のボタンがOFFになるアレ。
_Photoshop のツールギャラリー
こんな感じだろうか。
_button_test.py
どれか一つボタンを押すと、他のボタンがOFFになってる。更に、IDも変わってる。目的は果たせそう。
こういう動作にする際は、 _QButtonGroup を使う。 _QPushButton (フツーのボタン)でも、 _QRadioButton (ラジオボタン)でも、とにかく QButtonGroup に追加登録してやれば、こういう動作にすることができる模様。また、各ボタンを登録する際にIDを設定しておいてやると、後で変化があった際、押されたボタンのIDを取得することもできる。
QPushButtonは、そのままだとクリックした時しかONにならないけど。クリックするたびにON/OFFを切り替えるトグルボタン(?)にしたい時は、 _setCheckable(True) を呼んで設定してやる。
それと、最初に QPushButton のどれかしらが既に押されている状態にしたいわけだけど。その場合は、 _setChecked(True) を呼んでやれば、ON状態に設定できる。
QButtonGroup に登録されたボタンの状態が変わったら、QButtonGroup は _buttonClicked というシグナルを出すので、そのシグナルにメソッドを割り当ててやれば、どのIDのボタンが押されたのかが分かる。
_Photoshop のツールギャラリー
こんな感じだろうか。
_button_test.py
u"""押したままの状態になるボタンを並べてツールパネル相当を作るテスト.""" import sys from PySide.QtCore import * # NOQA from PySide.QtGui import * # NOQA class ToolPanel(QWidget): u"""ツールパネル用Widget.""" button_clicked = Signal(int) # シグナルを用意 def __init__(self, *argv, **keywords): u"""初期化.""" super(ToolPanel, self).__init__(*argv, **keywords) self.grp = QButtonGroup() gb = QGridLayout() dt = [ (0, 0, 0, "Brush"), (1, 0, 1, "Erase"), (0, 1, 2, "Box"), (1, 1, 3, "Box Fill"), (0, 2, 4, "Select"), (1, 2, 5, "Fill"), ] for x, y, id, s in dt: btn = QPushButton(s, self) btn.setCheckable(True) # トグルボタンにする self.grp.addButton(btn, id) # グループ登録してラジオボタン風に使う gb.addWidget(btn, y, x) self.setLayout(gb) self.grp.setExclusive(True) # 排他的なボタン処理にする self.grp.button(0).setChecked(True) # 最初のボタンを押しておく self.grp.buttonClicked.connect(self.changed_button) def changed_button(self): u"""ボタンが押された時に呼ばれる処理.""" self.button_clicked.emit(self.grp.checkedId()) # シグナルを発生 def get_id(self): u"""選択状態(ID)を返す.""" return self.grp.checkedId() class MyMainWidget(QWidget): u"""メインウインドウ.""" def __init__(self, *argv, **keywords): u"""初期化.""" super(MyMainWidget, self).__init__(*argv, **keywords) self.tb = ToolPanel(self) self.tb.button_clicked.connect(self.changed_toolbox) self.lbl = QLabel("----", self) self.lbl.setText("ID=%d" % self.tb.get_id()) l = QVBoxLayout() l.addWidget(self.tb) l.addWidget(self.lbl) self.setLayout(l) def changed_toolbox(self, id): u"""ツールの選択状態が切り替わった時に呼ばれる処理.""" self.lbl.setText("ID=%d" % id) def main(): u"""メイン処理.""" app = QApplication(sys.argv) w = MyMainWidget() w.show() sys.exit(app.exec_()) if __name__ == '__main__': main()
どれか一つボタンを押すと、他のボタンがOFFになってる。更に、IDも変わってる。目的は果たせそう。
こういう動作にする際は、 _QButtonGroup を使う。 _QPushButton (フツーのボタン)でも、 _QRadioButton (ラジオボタン)でも、とにかく QButtonGroup に追加登録してやれば、こういう動作にすることができる模様。また、各ボタンを登録する際にIDを設定しておいてやると、後で変化があった際、押されたボタンのIDを取得することもできる。
QPushButtonは、そのままだとクリックした時しかONにならないけど。クリックするたびにON/OFFを切り替えるトグルボタン(?)にしたい時は、 _setCheckable(True) を呼んで設定してやる。
それと、最初に QPushButton のどれかしらが既に押されている状態にしたいわけだけど。その場合は、 _setChecked(True) を呼んでやれば、ON状態に設定できる。
QButtonGroup に登録されたボタンの状態が変わったら、QButtonGroup は _buttonClicked というシグナルを出すので、そのシグナルにメソッドを割り当ててやれば、どのIDのボタンが押されたのかが分かる。
◎ アイコン画像を表示してみた。 :
各ボタンにテキストが表示されてる状態ではツールパネルっぽくないので、試しにアイコン画像を表示してみたり。
_button_test_with_icon.py.txt
_toolbar_32x32x8.png
こんな見た目になった。
_QIcon - PySide v1.0.7 documentation
_QGridLayout - PySide v1.0.7 documentation
_QWidget - PySide v1.0.7 documentation
_QApplication - PySide v1.0.7 documentation
_QStyleFactory - PySide v1.0.7 documentation
_button_test_with_icon.py.txt
u""" 押したままの状態になるボタンを並べてツールパネル相当を作るテスト. アイコン画像をボタン上に表示してみる版。 """ import sys from PySide.QtCore import * # NOQA from PySide.QtGui import * # NOQA class ToolPanel(QWidget): u"""ツールパネル用Widget.""" button_clicked = Signal(int) # シグナルを用意 def __init__(self, *argv, **keywords): u"""初期化.""" super(ToolPanel, self).__init__(*argv, **keywords) # 画像を読み込んで分割 im = QImage("./toolbar_32x32x8.png") h = im.size().height() imgs = [] for i in range(8): imgs.append(im.copy(i * h, 0, h, h)) self.grp = QButtonGroup() gb = QGridLayout() gb.setHorizontalSpacing(0) # GridLayoutの水平余白を設定 gb.setVerticalSpacing(0) # GridLayoutの垂直余白を設定 dt = [ (0, 0, 0, "Brush"), (1, 0, 1, "Erase"), (2, 0, 2, "Line"), (3, 0, 3, "Box Line"), (4, 0, 4, "Box Fill"), (0, 1, 5, "Fill"), (1, 1, 6, "Text"), (2, 1, 7, "Select"), ] for x, y, id, s in dt: icon = QIcon(QPixmap.fromImage(imgs[id])) # アイコン画像を生成 btn = QPushButton(icon, "", self) # アイコンサイズを画像の元サイズにする btn.setIconSize(imgs[id].size()) btn.setToolTip(s) # ツールチップを指定 btn.setCheckable(True) # トグルボタンにする self.grp.addButton(btn, id) # グループ登録してラジオボタン風に使う gb.addWidget(btn, y, x) self.setLayout(gb) self.grp.setExclusive(True) # 排他的なボタン処理にする self.grp.button(0).setChecked(True) # 最初のボタンを押しておく self.grp.buttonClicked.connect(self.changed_button) def changed_button(self): u"""ボタンが押された時に呼ばれる処理.""" self.button_clicked.emit(self.grp.checkedId()) # シグナルを発生 def get_id(self): u"""選択状態(ID)を返す.""" return self.grp.checkedId() class MyMainWidget(QWidget): u"""メインウインドウ.""" def __init__(self, *argv, **keywords): u"""初期化.""" super(MyMainWidget, self).__init__(*argv, **keywords) self.tb = ToolPanel(self) self.tb.button_clicked.connect(self.changed_toolbox) self.lbl = QLabel("----", self) self.lbl.setText("ID=%d" % self.tb.get_id()) l = QVBoxLayout() l.addWidget(self.tb) l.addWidget(self.lbl) self.setLayout(l) def changed_toolbox(self, id): u"""ツールの選択状態が切り替わった時に呼ばれる処理.""" self.lbl.setText("ID=%d" % id) def main(): u"""メイン処理.""" app = QApplication(sys.argv) app.setStyle(QStyleFactory.create("cleanlooks")) # app.setStyle(QStyleFactory.create("plastique")) # app.setStyle(QStyleFactory.create("windows")) # app.setStyle(QStyleFactory.create("windowsvista")) w = MyMainWidget() w.show() sys.exit(app.exec_()) if __name__ == '__main__': main()アイコン画像として、以下を自作した。スクリプトソースも含めて、CC0 / Public Domain ってことで。
こんな見た目になった。
- アイコンの指定には、QIcon を使う。QPixmap を渡すと作れるっぽいので、元画像が QImage なら、QIcon(QPixmap.fromImage(qimg)) てな記述になる。
- QIcon は、自動で画像を縮小して適切なサイズにしてしまうので、元画像サイズで表示したい場合は setIconSize() を呼んで元画像サイズを指定してやる。
- QGridLayout の水平余白、垂直余白は、setHorizontalSpacing()、setVerticalSpacing() で指定できる。
- ボタン上にマウスカーソルを合わせた際にピョコッと表示されるツールチップは、setToolTip() で指定できる。
- 各Widgetの全体的な見た目は、QApplication.setStyle() で変更できる。QStyleFactory.create("cleanlooks") や QStyleFactory.create("plastique") を渡してやると見た目が変わる。指定できる文字列としては、windows、motif、cde、plastique、cleanlooks があるらしい。
_QIcon - PySide v1.0.7 documentation
_QGridLayout - PySide v1.0.7 documentation
_QWidget - PySide v1.0.7 documentation
_QApplication - PySide v1.0.7 documentation
_QStyleFactory - PySide v1.0.7 documentation
[ ツッコむ ]
以上です。