コンテキストメニューを作る
いろいろな Widgets を作っていくと、右クリックメニューを作りたくなるかと思います。
今回は、PySide で右クリックメニューを作る方法を説明したいと思います。
コード
まずはコード。
# -*- coding: utf-8 -*-
import sys
from PySide2 import QtCore, QtGui, QtWidgets
class UISample(QtWidgets.QDialog):
def __init__(self, parent=None):
super(UISample, self).__init__(parent)
layout = QtWidgets.QVBoxLayout()
# カスタムUIを作成
self.list = QtWidgets.QTreeView()
layout.addWidget(self.list)
self.list2 = QtWidgets.QTreeView()
# List1で表示するContextMenuを設定
self.list.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
self.list.customContextMenuRequested.connect(self.listContext)
# List2で表示するContextMenuを指定
self.list2.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
self.list2.customContextMenuRequested.connect(self.list2Context)
layout.addWidget(self.list2)
# ここまでUI作成
self.setLayout(layout)
def listContext(self, pos):
menu = QtWidgets.QMenu(self.list)
action_01 = menu.addAction('ほげほげ')
action_01.triggered.connect(lambda: self.action('A'))
action_02 = menu.addAction('ふがふが')
action_02.triggered.connect(lambda: self.action('B'))
menu.addSeparator()
subMenu = menu.addMenu('SubMenu')
action_03 = subMenu.addAction('さぶめにゅー1')
action_03.triggered.connect(self.subMenu)
menu.exec_(self.list.mapToGlobal(pos))
def action(self, actionText):
print(actionText)
def subMenu(self):
print("SUBMENU")
def list2Context(self, pos):
menu = QtWidgets.QMenu(self.list2)
menu.addAction('list2_Menu')
menu.addAction('list2_Menu2')
menu.addAction('list2_Menu2')
menu.exec_(self.list2.mapToGlobal(pos))
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
a = UISample()
a.show()
sys.exit(app.exec_())
実行すると、こんな感じの UI が表示されます。
以下、詳しい解説。
Signal-Slot を作る
まず、メニューを表示したい場合は、PySide のお約束の 「Signal-Slot」 を作成します。
self.list.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
self.list.customContextMenuRequested.connect(self.listContext)
それがこの部分になります。
PySide では、右クリックで表示されるメニューのことを「コンテキストメニュー」と呼びます。
今回のように、自分でこのコンテキストメニューを作りたい場合は
Signal-Slot とは別に、setContexdtMenuPolicy で CustomContextMenu の設定をしておきます。
このフラグを ON にしたら、次にメニューを呼び出すためのシグナルを 作成します。
このコンテキストメニューは、Widgets 単位で作成することができます。
ので、例えば今回のサンプルのように
2 つの ListView があった場合に、それぞれ別のメニューを表示出来るようにしたい場合
それぞれの Widgets ごとにこの ContextMenu の設定を行います。
def listContext(self, pos):
menu = QtWidgets.QMenu(self.list)
action_01 = menu.addAction('ほげほげ')
action_01.triggered.connect(lambda: self.action('A'))
action_02 = menu.addAction('ふがふが')
action_02.triggered.connect(lambda: self.action('B'))
menu.addSeparator()
subMenu = menu.addMenu('SubMenu')
action_03 = subMenu.addAction('さぶめにゅー1')
action_03.triggered.connect(self.subMenu)
menu.exec_(self.list.mapToGlobal(pos))
そんでもって、右クリックを押したときに呼ばれる関数を作成します。
単純に右クリックに割り当てただけだと、もちろんメニューは表示されません。
メニュー部分は QWidgets.QMneu クラスを使用します。
Menu を追加する
action_01 = menu.addAction('ほげほげ')
action_01.triggered.connect(lambda: self.action('A'))
まず、実行させたい Widget を親にした Menu を作成します。
そして、メニューに追加するには addAction(メニュー名) を追加します。
追加すると、QAction オブジェクトが帰ってくるので
このメニューをクリックしたときに実行される関数(Slot)を
.triggered.connect(###) で、指定します。
この QAction というのは、各種メニュー、ツールボタン、キーボードのショートカット
等を介していろんなコマンドを呼び出すのに使用するものです。
セパレーターを追加する
menu.addSeparator()
Menu の途中に線を入れたい場合は、入れたいところでこの Separator を追加します。
サブメニューを追加する
subMenu = menu.addMenu('SubMenu')
action_03 = subMenu.addAction('さぶめにゅー1')
action_03.triggered.connect(self.subMenu)
サブメニューを作りたい場合は addMenu コマンドを使用します。
このコマンドを使用すると、QMenu オブジェクトが返ってきます。
ので、あとは上と同じくその Menu オブジェクトに対して addAction でアクションを追加すると
サブメニューを作成できます。
サブメニューにさらにサブメニューを追加したい場合も同様に
addMenu を追加すれば OK です。
表示する
最後に、設定ができたら表示します。
menu.exec_(self.list.mapToGlobal(pos))
表示するには exec_(Menu を表示する Point) を使用します。
customContextMenuRequested の Signal は、現在のマウスポジションを引数で受け取るのですが
この Point は現在の Widgets のローカル座標が返ってきてしまいます。
しかしながら、ContextMenu の表示位置はグローバル座標である必要があります。
ので、Local→Global の座標返還を行うために mapToGLobal 関数を使用します。
これは、この関数のオブジェクト(この例だと self.list )の座標系を
Global に変換してくれます。
ので、この関数を使用すればローカル座標だった pos を グローバルに変換して
正しい位置にメニューを表示することができます。
一応、
# QCursorでカーソルのグローバルポジションを取得する
cursor = QtGui.QCursor()
menu.exec_(cursor.pos())
でも、同じ効果となります。