Pyside勉強 その八
次は、読みこんだUIに、マウスイベントを入れてみます。
色々手法はあるみたいですが
とりあえず、Filterを使ってWidgetのイベントを
オーバーライドする形にしてみます。
まぁはじめこの部分すんごいはまりましたw
まずはマウスイベントだけですが
なかなか文字数増えてますね汗
import os import sys from PySide import QtCore, QtGui from PySide.QtUiTools import QUiLoader from maya.app.general.mayaMixin import MayaQWidgetBaseMixin CURRENT_PATH = "D:/" class GUI(MayaQWidgetBaseMixin, QtGui.QMainWindow): def __init__(self, parent=None): super(GUI, self).__init__(parent) loader = QUiLoader() uiFilePath = os.path.join(CURRENT_PATH, 'UI.ui') self.UI = loader.load(uiFilePath) self.setCentralWidget(self.UI) self.UI.BackGound.setPixmap(QtGui.QPixmap("D:/GR.jpg")) self._filter = Filter() self.UI.widget.installEventFilter(self._filter) self.UI.pushButton.clicked.connect(self.test) self.setWindowTitle("UI_testWindow") self.resize(460, 610) def test(self): print "Push!" class Filter(QtCore.QObject): def eventFilter(self, widget, event): if event.type() == QtCore.QEvent.MouseButtonPress: self.origin = event.pos() self.rubberBand = QtGui.QRubberBand(QtGui.QRubberBand.Rectangle,widget) self.origin.setX(self.origin.x()) self.origin.setY(self.origin.y()) self.rubberBand.setGeometry(QtCore.QRect(self.origin,QtCore.QSize())) self.rubberBand.show() print "Click" print self.origin.x() print self.origin.y() elif event.type() == QtCore.QEvent.MouseMove: if self.rubberBand.isVisible(): self.movePos = event.pos() self.movePos.setX(self.movePos.x()+ widget.x()) self.movePos.setY(self.movePos.y()+ widget.y()) self.rubberBand.setGeometry(QtCore.QRect(self.origin,self.movePos).normalized()) print "Move" print event.x() print event.y() elif event.type() == QtCore.QEvent.MouseButtonRelease: print "Release" print event.x() print event.y() self.rubberBand.hide() rect = self.rubberBand.geometry() rect.setX(rect.x()-widget.x()) rect.setY(rect.y()-widget.y()) rect.setWidth(rect.width()-widget.x()) rect.setHeight(rect.height()-widget.y()) selected = [] #ウィンドウ内のQPushButtonをすべて取得 for child in widget.findChildren(QtGui.QPushButton): if rect.intersects(child.geometry()): selected.append(child) if selected: self.rectSelection(selected) return QtGui.QMainWindow.eventFilter(self, widget, event) def rectSelection(self,sel): print sel def main(): app = QtGui.QApplication.instance() ui = GUI() ui.show() if __name__ == '__main__': main()
実行するとこんな感じ
前回同様に画像が張ってあって、ボタンが真ん中に。
といったUI。
見た目は何も変わってません。
これにマウスイベントが組み込まれています。
実際に矩形選択すると、ログにはこう出ています。
マウスの位置と、ボタンが取得できていますね。
成功です。
では解説。
from PySide.QtUiTools import QUiLoader from maya.app.general.mayaMixin import MayaQWidgetBaseMixin CURRENT_PATH = "D:/"
UIファイルを読み込むモジュールを
インポートしておきます、
【CURRENT_PATH 】で絶対パスを記載して、あとでファイルと結合して
UIファイルを読み込みます。
この方が管理しやすいので、変更しました。
loader = QUiLoader()
ロードするコマンドをインスタンス化します。
uiFilePath = os.path.join(CURRENT_PATH, 'UI.ui')
self.UI = loader.load(uiFilePath)
上記で宣言したパスと、ファイル名を合体して
ロードを実行。読み込んだUIファイルを変数に格納しておきます。
self.setCentralWidget(self.UI)
継承したウィンドウ(メインウィンドウ)に読み込んだ
UIをセットしています。
self.UI.BackGound.setPixmap(QtGui.QPixmap("D:/GR.jpg"))
前回同様画像ファイルを設定
self._filter = Filter() self.UI.widget.installEventFilter(self._filter)
この部分が今回の一番の追加点になります。
マウスイベントを【installEventFilter】を
使用して、イベントを管理しています。
【installEventFilter】については以下が、めちゃくちゃわかりやすいです。
ヲドリテヒヅル eventFilter de のっとーり
で、中身ですが、
まずはマウスの処理をまとめたクラスを読み込み
変数self._filterに格納しておきます。
次にeventFilterを使ってイベントを全取得して、上記クラスに処理を投げています。
細かく書くと
self.UI.widget.installEventFilter(self._filter)
(メインウィンドウの子どもにあるパーツにinstallEventFilterで読み込んだクラスを
割り当てる)といった流れです。
eventFilterはイベントを全部取ってくることができる、とても便利な機能です。
他のやり方もあるみたいですが、私はこのやり方しかやったことがないので、割愛。
self.UI.pushButton.clicked.connect(self.test) self.setWindowTitle("UI_testWindow") self.resize(460, 610)
ボタンクリックの挙動と、ウィンドウの名前
UIサイズの変更を行っています。
def test(self): print "Push!"
ボタン挙動の関数です。
今見ると、押すとPush!!ってアホですねw
では次に、追加したクラスの中をみていきます。
class Filter(QtCore.QObject): def eventFilter(self, widget, event):
マウス処理を記載したクラスです。
ここで少しハマったので自信ありませんw
とりあえず動いているので進めます苦笑
クラスの名前は適当です。
関数名も同様です。
引数でウィジェットとイベントを指定しています。
if event.type() == QtCore.QEvent.MouseButtonPress:
event.type()なので、クラスに渡されたイベントが
マウスボタンが押されたという、イベントだったら
という分岐になります。
self.origin = event.pos()
イベントが発生したポジションを取得しています。
今回でいうと、マウスボタンが押された場所です。
self.rubberBand = QtGui.QRubberBand(QtGui.QRubberBand.Rectangle,widget)
widget上で、QRubberBandで矩形選択時に出るコレ↓を描画しています。
self.origin.setX(self.origin.x()) self.origin.setY(self.origin.y())
マウスボタンが押された場所をXとYで分解して
矩形選択の開始位置として、セットしています。
self.rubberBand.setGeometry(QtCore.QRect(self.origin,QtCore.QSize()))
矩形のジオメトリなので、線の部分かと思います
QtCore.QRectについては以下が一番わかり易いです。
QRect - qtmemo @ ウィキ - アットウィキ
あとここも
http://melpystudio.blog82.fc2.com/blog-entry-191.htmlQRect - qtmemo @ ウィキ - アットウィキ
引用すると
""
QtCore.QRect(x, y, width, height)
でQRectを作成します。xが矩形の左上X座標、yが左上Y座標、width/heightが矩形の幅/矩形の高さをそれぞれ表しています。
""
なので、QtCore.QRect(self.origin,QtCore.QSize())では
x, y,をself.originで。
width, heightQtCore.QSize()で指定います。
self.rubberBand.show()
最後に実勢の表示をこのコマンドで実行。
print "Click" print self.origin.x() print self.origin.y()
こちらはデバッグ用です。特に意味はありません。
elif event.type() == QtCore.QEvent.MouseMove:
マウスが動いたら。という分岐になります。
if self.rubberBand.isVisible():
クリック時に表示していた、矩形オブジェクトが表示状態なら=クリックが実行されて
いたらという意味になります。
self.movePos = event.pos() self.movePos.setX(self.movePos.x()+ widget.x()) self.movePos.setY(self.movePos.y()+ widget.y())
一行目で現在のポジションを取得し
2、3行目でXYそれぞれの最新の値を更新しています。
(self.movePos.x()+ widget.x())この部分ですが
self.movePos.x()(現在位置) + widget.x()(ウィジェットのX開始位置)
と言うことになるので、ウィジェットの開始位置からの移動値を
取得しているといったながれになります。
self.rubberBand.setGeometry(QtCore.QRect(self.origin,self.movePos).normalized())
上記で取得した位置を元に、ジオメトリを更新しています。
最後のnormalized()は正規化です。
正しい値にしているだけですので、実はなくても動きます。
保険かと思います。
サンプルに記載していたので、つけてますw
次に
elif event.type() == QtCore.QEvent.MouseButtonRelease:
マウスボタンが離されたらという分岐。
self.rubberBand.hide()
リリースなので、ジオメトリを非表示にしています。
rect = self.rubberBand.geometry() rect.setX(rect.x()-widget.x()) rect.setY(rect.y()-widget.y()) rect.setWidth(rect.width()-widget.x()) rect.setHeight(rect.height()-widget.y())
この部分ですが、今までと逆のことを行っています。
1行目では、矩形を取得し
2,3、4,5行目で
ウィジェットのローカル空間のXY位置と
横幅のサイズを取得しています。
selected = []
あとで使う空の配列
for child in widget.findChildren(QtGui.QPushButton): if rect.intersects(child.geometry()): selected.append(child)
widgetの子どもにあるボタンをまず全取得しておきます。
2行目で rect.intersectsで矩形の中にある(厳密には矩形に当たっている)
ジオメトリ(パーツ)なのか?と分岐を噛ましています。
矩形内なら、配列にボタンを追加しています。
まとめると、まずは該当ウィジェットの子どもにある
ボタンを全取得し、それを回し
矩形内に入っていたら(矩形座標内に)配列に足す。という
流れになります。
if selected:
self.rectSelection(selected)
配列が空じゃなかったら、関数に飛びます。
return QtGui.QMainWindow.eventFilter(self, widget, event)
ここ重要です。
eventFilterはリターンが必要です。
処理を返さないといけないので上記を指定しています。
ここはIFで分岐しているので
return Falseでもいけます。
def rectSelection(self,sel): print sel
最後です。
配列にボタンが入っていたら飛ぶ関数です。
特段今は意味がありません。
という感じになります。
eventFilter便利なんですが、調べるのに結構時間かかりました…
ふぅ。。
次はボタンが矩形内にあったときの挙動を作ってみます。
以上です。