Python 如果QMenu中的QWidgetAction在
我试图在QMenu中实现三态复选框。 我的菜单层次结构类似于:Python 如果QMenu中的QWidgetAction在,python,pyqt4,qmenu,qcheckbox,qwidgetaction,Python,Pyqt4,Qmenu,Qcheckbox,Qwidgetaction,我试图在QMenu中实现三态复选框。 我的菜单层次结构类似于: menuA |-- a101 |-- a102 menuB |-- b101 其中第一层(menuA、menuB)为三态复选框,而其子项为正常复选框,使用QAction实现 因此,通过使用QWidgetAction和QCheckBox,我似乎能够让三态在第一层工作 但是,当我尝试使用将子项包含到第一层项中的setMenu时,即使选项能够相应地显示子项,选项也不再可检查 最初我只使用QAction小部件,但当
menuA
|-- a101
|-- a102
menuB
|-- b101
其中第一层(menuA、menuB)为三态复选框,而其子项为正常复选框,使用QAction实现
因此,通过使用QWidgetAction
和QCheckBox
,我似乎能够让三态在第一层工作
但是,当我尝试使用将子项包含到第一层项中的setMenu
时,即使选项能够相应地显示子项,选项也不再可检查
最初我只使用QAction小部件,但当我迭代子项时,第一层项始终显示为完整检查,如果可能的话,我想纠正它,因此我尝试使用三态
例如,如果选中了a101
,menuA
将设置为部分状态。如果同时选中了a101
和a102
,则menuA
将设置为(完全)检查状态
class CustomCheckBox(QtGui.QCheckBox):
def uuu init uuuu(self,text=”“,parent=None):
超级(自定义复选框,自我)。\uuuuu初始化\uuuuuuuu(文本,父项=父项)
self.setText(文本)
self.setTristate(真)
类QSubAction(QtGui.QAction):
def uuu init uuuu(self,text=”“,parent=None):
超级(QSubAction,self)。\uuuu init\uuuu(文本,父级)
self.setCheckable(真)
self.toggled.connect(self.checkbox\u切换)
def复选框_切换(自身、值):
打印值
类QCustomMenu(QtGui.QMenu):
“”“自定义的QMenu。”“”
def uuu init uuuu(self,title,parent=None):
超级(QCustomMenu,self)。\uuuu初始化(title=str(title),parent=parent)
self.setup_菜单()
def鼠标压力事件(自身、事件):
action=self.activeAction()
如果不存在(action,QSubAction)且action不是None:
action.trigger()
返回
elif isinstance(动作、QSubAction):
action.toggle()
返回
返回QtGui.QMenu.mousePressEvent(self,event)
def设置菜单(自我):
self.setContextMenuPolicy(QtCore.Qt.DefaultContextMenu)
def contextMenuEvent(自身、事件):
没有右键点击=[卡扎菲行动]
如果有([isinstance(self.actionAt(event.pos()),instance),例如,在no_right_click中):
返回
pos=event.pos()
def添加操作(自我、操作):
超级(QCustomMenu,self).addAction(action)
类MainApp(QtGui.QWidget):
def uuu init uuu(self,parent=None):
超级(主应用程序,自我)。\uuuuu初始化\uuuuuuu(父级)
自检记录={
“种皮”:{
“菜单”:[“a101”、“a102”],
},
“testBC”:{
“菜单”:[“c101”、“c102”、“c103”],
“menuB”:[“b101”]
},
}
v_layout=QtGui.QVBoxLayout()
self.btn1=QtGui.QPushButton(“测试btn1”)
v_layout.addWidget(self.btn1)
self.setLayout(v_布局)
self.setup_connections()
def设置_连接(自):
self.btn1.clicked.connect(self.button1\u测试)
def按钮1_测试(自身):
self.qmen=qcustommen(title='',parent=self)
对于自测试目录项()中的pk、pv:
基本菜单=QCustomMenu(标题=主键,父菜单=自身)
基本复选框=自定义复选框(主键,基本菜单)
base\u action=QtGui.QWidgetAction(base\u复选框)
base_action.setMenu(base_qmenu)#这会导致选项不可检查
base\u action.setDefaultWidget(base\u复选框)
self.qmen.addAction(基本动作)
对于pv中的v:
动作=QSubAction(v,self)
基本菜单添加操作(操作)
self.qmenu.exec(QtGui.QCursor.pos())
如果名称=“\uuuuu main\uuuuuuuu”:
app=QtGui.QApplication(sys.argv)
w=MainApp()
w、 show()
sys.exit(app.exec_())
无法设置子菜单状态的原因是QMenu自动使用单击子菜单打开它,“使用”单击事件
要实现这一点,您必须确保用户单击的位置,如果它是QWidgetActions之一,则触发它,确保事件不会进一步传播
此外,三态逻辑被添加到子态,使用检查所有菜单操作以确定实际状态的切换信号
请注意,contextMenuEvent(连同菜单策略设置)已被删除
最后,考虑使用一个不在菜单项中触发动作的复选框,因为它违背了菜单项的预期行为,这是违反直觉的。
class CustomCheckBox(QtGui.QCheckBox):
def __init__(self, text="", parent=None):
super(CustomCheckBox, self).__init__(text, parent=parent)
self.setText(text)
self.setTristate(True)
def mousePressEvent(self, event):
# only react to left click buttons and toggle, do not cycle
# through the three states (which wouldn't make much sense)
if event.button() == QtCore.Qt.LeftButton:
self.toggle()
def toggle(self):
super(CustomCheckBox, self).toggle()
newState = self.isChecked()
for action in self.actions():
# block the signal to avoid recursion
oldState = action.isChecked()
action.blockSignals(True)
action.setChecked(newState)
action.blockSignals(False)
if oldState != newState:
# if you *really* need to trigger the action, do it
# only if the action wasn't already checked
action.triggered.emit(newState)
class QSubAction(QtGui.QAction):
def __init__(self, text="", parent=None):
super(QSubAction, self).__init__(text, parent)
self.setCheckable(True)
class QCustomMenu(QtGui.QMenu):
"""Customized QMenu."""
def __init__(self, title, parent=None):
super(QCustomMenu, self).__init__(title=str(title), parent=parent)
def mousePressEvent(self,event):
actionAt = self.actionAt(event.pos())
if isinstance(actionAt, QtGui.QWidgetAction):
# the first mousePressEvent is sent from the parent menu, so the
# QWidgetAction found is one of the sub menu actions
actionAt.defaultWidget().toggle()
return
action = self.activeAction()
if not isinstance(action,QSubAction) and action is not None:
action.trigger()
return
elif isinstance(action,QSubAction):
action.toggle()
return
QtGui.QMenu.mousePressEvent(self,event)
def addAction(self, action):
super(QCustomMenu, self).addAction(action)
if isinstance(self.menuAction(), QtGui.QWidgetAction):
# since this is a QWidgetAction menu, add the action
# to the widget and connect the action toggled signal
action.toggled.connect(self.checkChildrenState)
self.menuAction().defaultWidget().addAction(action)
def checkChildrenState(self):
actionStates = [a.isChecked() for a in self.actions()]
if all(actionStates):
state = QtCore.Qt.Checked
elif any(actionStates):
state = QtCore.Qt.PartiallyChecked
else:
state = QtCore.Qt.Unchecked
self.menuAction().defaultWidget().setCheckState(state)
您好@musciamante,谢谢您的回复。我注意到基本菜单缺少“右箭头”,这可能是因为使用了QWidgetAction吗?是的,是的。我假设QMenu决定只在找到注册到菜单的QAction时绘制箭头,这意味着它实际上绘制了QAction内容(因此它的特性是:图标、复选框/收音机和子菜单箭头),而如果有QWidgetAction,它只绘制周围的帧,剩下的部分由defaultWidget()完成。如果您想绘制箭头,恐怕您需要自己绘制,可能需要添加箭头字符或增加contentMargin()的右组件,并在paintEvent中绘制箭头。