水深1mm

広く浅い個人的メモ。

QtDesignerで配置後にPython上でのWidgetのカスタマイズ

ファイル

https://drive.google.com/file/d/0B4fmLEFqtND0THd4TmlDR0lQems/view?usp=sharing

QtDesigner上でこんな感じのUIを作成したとして

f:id:bonbonbe:20160827132118p:plain

このUIに対して

  • ボタンを動かせるようにする
  • GrapshicsView(以下View)にボタンが当たったら色を変える

という処理を追加する場合、ボタンとView用のWrapperクラスを作成する。

ButonWrapper

class ButtonWrapper(WrapperBase):
    collision = QtCore.Signal(WrapperBase)

    def __init__(self, widget, col):
        """
        :type widget: PySide.QtGui.QWidget
        """
        super(ButtonWrapper, self).__init__(widget)

        #: :type: QColor
        self.__color = col
        #: :type: WrapperBase
        self.__collision_target = None

        self.widget.mousePressEvent = self.__on_pressed
        self.widget.mouseMoveEvent = self.__on_moved

        self.__move_offset = None
        pass

    @property
    def color(self):
        return self.__color

    def __on_pressed(self, event):
        """
        :type event: PySide.QtGui.QMouseEvent
        """
        self.__move_offset = self.widget.mapFromGlobal(event.globalPos())
        pass

    def __on_moved(self, event):
        """
        :type event: PySide.QtGui.QMouseEvent
        """
        pos = self.widget.window().mapFromGlobal(event.globalPos())
        pos -= self.__move_offset
        self.widget.move(pos.x(), pos.y())

        if self.__collision_target and self.__collision_target.widget.geometry().intersects(self.widget.geometry()):
            self.collision.emit(self)
        pass

    def set_collision_target(self, wrapper):
        """
        :type wrapper: ButtonWrapper
        """
        self.__collision_target = wrapper
        pass

ViewWrapper

class ViewWrapper(WrapperBase):
    def __init__(self, widget):
        """
        :type widget: PySide.QtGui.QWidget
        """
        super(ViewWrapper, self).__init__(widget)

        self.widget.enterEvent = self.__on_enter
        self.widget.leaveEvent = self.__on_leave
        pass

    def __on_enter(self, event):
        print "mouse enter."
        pass

    def __on_leave(self, event):
        print "mouse leave."
        pass

    def set_color(self, color):
        scene = QtGui.QGraphicsScene()
        scene.setBackgroundBrush(color)
        self.widget.setScene(scene)
        pass

基本的には、両方とも__init__のwidgetにはQtDesigner上で配置されたWidgeを指定し、
その中のVirtualFunctionを書き換えるような処理を行っている。

(ぶっちゃけこのVirtualFunctionを書き換えられれば良いのでWrapperクラスとか作らなくていい)

ButonWrapper

self.widget.mousePressEvent = self.__on_pressed
self.widget.mouseMoveEvent = self.__on_moved

ViewWrapper

self.widget.enterEvent = self.__on_enter
self.widget.leaveEvent = self.__on_leave

書き換えられるVirtualFunctionについてはPySideの公式Referenceを読めばわかる。
MayaのPySideのバージョンは1.2らしいので1.2.1のRefernceあたりを見ればいいと思う。

個人的には1.2.1のRefernceだと図でクラスをたどれないので1.0.1が好き。


本来であれば

  • QWidgetなり継承してVirtualFunctionをオーバーライド、それをQtDesignerで格上げ先として
    使えるようにする
  • QtCreator(C++)で最終的なuiファイルまで作成してそれをPythonで使う(出来るか不明)

が正しいのだろうけど自分の頭では無理だった・・・。

ついでにWidget同士の当たり判定について

処理部分はここ↓

ButtonWraper

    if self.__collision_target and self.__collision_target.widget.geometry().intersects(self.widget.geometry()):
        self.collision.emit(self)
    pass

widgetからgeometry()でQRectを取得してそれをintersectsで判定している。
rect()から取得したQRectの場合、位置が来ないらしく正しい判定ができなかった。