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
[ ツッコむ ]
以上です。