スポンサーサイト

    上記の広告は1ヶ月以上更新のないブログに表示されています。
    新しい記事を書く事で広告が消せます。

    eventFilter de のっとーり

     気がつけばポケモンカードの新弾「ガイアボルケーノ・ダイダルストーム」が発売され2ヶ月が経過・・・そしてガンダムトライエイジの新弾「G3」もそろそろ稼働開始って事で、お仕事したのにブログに載せる暇が全然ない最近です(-_-;)
     去年末の12月にポケモンカード初のイラスト集も発売され、一点だけですが掲載されてワショーイだったので、ブログで歓喜の宴を書き綴ろうと思ったのですがその暇もなく・・・

     と私事ばかり並べてしまいましたが、久しぶりの更新はQTネタです。

     例えば親ウィジェット(仮にWidgetAとする)のkeyPressEventで何らかの動作のショートカットを作成しても、そのWidgetAの子として作ったウィジェット(仮にTreeViewAとする)にフォーカスが当たっているとTreeViewAの方のショートカットが優先されてしまい、WidgetAのショートカットが死んでしまう事があります。と言うか100%死にます。

    eventFilter001.png



    上記UIのサンプルコードれす。

    # -*- coding:utf-8 -*-
    import sys
    from PySide import QtGui, QtCore
    
    class TestWidget(QtGui.QWidget):
        '''例文のWidgetAに該当するUIを定義するクラス。'''
        def __init__(self, parent=None):
            super(TestWidget, self).__init__(parent)
            self.setWindowTitle('Event Filter Example')
    
            # 例文のTreeViewAに該当するTreeViewを作成。----------------------------------
            model = QtGui.QStandardItemModel(0, 1)
            for i in range(10):
                item = QtGui.QStandardItem('item %080d' % i)
                model.setItem(i, 0, item)
    
            self.treeview = QtGui.QTreeView()
            self.treeview.setModel(model)
            self.treeview.resizeColumnToContents(0)
            # ---------------------------------------------------------------------
    
            # レイアウトの作成。---------------------------------------------------------
            layout = QtGui.QVBoxLayout(self)
            layout.addWidget(self.treeview)
            # ---------------------------------------------------------------------
    
        def moveWidget(self, value):
            pos = self.pos()
            pos.setX(pos.x() + value)
            self.move(pos)
    
        def keyPressEvent(self, event):
            '''このイベントでショートカットキーを定義する。'''
            key = event.key()
            mod = event.modifiers()
            if key == QtCore.Qt.Key_Right and mod == QtCore.Qt.ControlModifier:
                self.moveWidget(20)
            elif key == QtCore.Qt.Key_Left and mod == QtCore.Qt.ControlModifier:
                self.moveWidget(-20)
            else:
                super(TestWidget, self).keyPressEvent(event)
    
    
    if __name__ == '__main__':
        app = QtGui.QApplication(sys.argv)
        
        window = TestWidget()
        window.show()
        
        sys.exit(app.exec_())
    
     Ctrl+→(or←)でウィジェットを左右に動かせるショートカットを設定しましたが、例の場合TreeViewの→(or←)のショートカットが優先されてしまい、TreeViewの→(or←)が機能しなくなる状態までWidgetAに該当する親ウィジェットのショートカットは無視されてしまいます(´;ω;`)

     TreeViewのカスタムクラスを作成して、そのkeyPressEventの中で親ウィジェットであるWidgetAを操作すると言う手もあるのですが、それだと果たしてセットされるかどうかわからない親ウィジェットを操作するコードを埋め込んでしまう事になるので構造的に不合理で、しかも不細工ですよね(´ε`;)
    そんなコードは
    あえて言おう、カスであると!!

     ってことで他の手を探していたら、ありましたよ。流石Qtさん、よくわかってらっしゃる!

     その手段とはeventFilter!!

     eventFilterはQObjectからの派生クラスに装備されていて、コレを使うと起こっているイベントを全部取ってくることができます。
     このフィルタの便利なところは、自身で起こっているイベントを取ってくるのではなく他のオブジェクトで起こっているイベントを取ってこれる点ですね。(自分のイベントだったらkeyPressEventやcloseEventなどのイベントメソッドを上書きするだけでいいですもんね)
     さっそく上のサンプルコードをeventFilterを使って書き換えてみましょう。
    # -*- coding:utf-8 -*-
    import sys
    from PySide import QtGui, QtCore
    
    class TestWidget(QtGui.QWidget):
        '''例文のWidgetAに該当するUIを定義するクラス。'''
        def __init__(self, parent=None):
            super(TestWidget, self).__init__(parent)
            self.setWindowTitle('Event Filter Example')
    
            # 例文のTreeViewAに該当するTreeViewを作成。----------------------------------
            model = QtGui.QStandardItemModel(0, 1)
            for i in range(10):
                item = QtGui.QStandardItem('item %080d' % i)
                model.setItem(i, 0, item)
    
            self.treeview = QtGui.QTreeView()
            self.treeview.setModel(model)
            self.treeview.resizeColumnToContents(0)
    
            # EventFilterをtreeViewに組み込む。
            self.treeview.installEventFilter(self)
            # ---------------------------------------------------------------------
    
            # レイアウトの作成。---------------------------------------------------------
            layout = QtGui.QVBoxLayout(self)
            layout.addWidget(self.treeview)
            # ---------------------------------------------------------------------
    
        def moveWidget(self, value):
            pos = self.pos()
            pos.setX(pos.x() + value)
            self.move(pos)
    
        def eventFilter(self, obj, event):
            if event.type() == QtCore.QEvent.KeyPress:
                '''イベントタイプが「キーボードが何か押された時」、下記コードを実行する。'''
                key = event.key()
                mod = event.modifiers()
                if key == QtCore.Qt.Key_Right and mod == QtCore.Qt.ControlModifier:
                    self.moveWidget(20)
                elif key == QtCore.Qt.Key_Left and mod == QtCore.Qt.ControlModifier:
                    self.moveWidget(-20)
                else:
                    return False
                return True
            return False
    
    if __name__ == '__main__':
        app = QtGui.QApplication(sys.argv)
        
        window = TestWidget()
        window.show()
        
        sys.exit(app.exec_())
    

     まず、35行目にeventFilterメソッドを追加しました。
     eventFilterメソッド内で特定のオブジェクトのイベントを取得して、その際の挙動を書いてあげます。
     eventFilter内でFalseを返せばキャッチしたイベントの発生元へこのイベントをパスしますが、Trueを返すとイベントの発生元にはイベントを渡しません。
     この例ではキーがkeyPressイベントの場合に、そのキーを割り出してCtrl+→(or←)だった場合のみmoveWidgetメソッドを呼び出してからTrueを返してキャッチ元のイベントを発生させないようにしています。
     それ以外の場合はFalseを返して通常通りのイベントが発生するようになっています。

     そしてもうひとつ重要なのが22行目の
    installEventFilter()
    になります。このinstallEventFilterに監視させたいウィジェット(この場合WidgetA)をセットしてあげると、すべてのイベントがインストールされたウィジェットのeventFilterメソッドに通知されます。

     35行目のeventFilterメソッドでは引数にobjとeventの2つを取るようになっています。
     第一引数のobjにはイベントが発生したオブジェクト(ウィジェット)が入っているので、一部のオブジェクトにのみ操作したい場合はこのobjを判断基準にすればOK。
     第2引数には発生したeventが入っているので、それのtypeメソッドでイベントの種類を判別し(36行目)て適時実行してあげましょう。


     これで親ウィジェット、子ウィジェット互いがコード的に干渉することなく操作する事が出来ます!
    スポンサーサイト

    コメントの投稿

    非公開コメント

    プロフィール

    Eske

    Author:Eske
    萌えイラストレーターを目指す3DCGイラストレーター。
    現在ポケモンカードゲーム、ガンダムトライエイジ、ガンダムコンクエスト、妖怪ウォッチとりつきカードゲームなどで3DCGを使用したイラストレーターとして参加中。

    主にここでは日々気づいたメモなんかを残してます。
    イラストのお仕事も受け付けております。ココからアクセスできますので、お気軽にご相談下さい。

    最新記事
    最新コメント
    カテゴリ
    最新トラックバック
    月別アーカイブ
    検索フォーム
    リンク
    QRコード
    QR
    上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。