我可以将eventFilter
设置为忽略小部件的“默认”事件吗? mousePressEvents等?还是可以将这两者混在一起?
在我的代码中,我有一个mousePressEvent
和一个自定义事件,该事件是为QMenu中的菜单项的右键单击菜单创建的。
有时,当我执行代码/或在选项卡上单击鼠标右键时,我会看到以下情况之一:
AttributeError: 'PySide2.QtCore.QEvent' object has no attribute 'pos'
或有时:
RuntimeWarning: Invalid return value in function QTabBar.eventFilter, expected bool, got NoneType.
这样,是否有更好的解决方法,还是应该像Rename Item
所示重新考虑如何继续使用Delete Item
和show_adv_qmenu()
? >
class MyWin(QtGui.QMainWindow):
def __init__(self, parent=None):
super(MyWin, self).__init__()
self.rename_menu_allowed = False
central_widget = QtGui.QWidget()
self.setCentralWidget(central_widget)
vlay = QtGui.QVBoxLayout(central_widget)
hlay = QtGui.QHBoxLayout()
vlay.addLayout(hlay)
vlay.addStretch()
self.add_button = QtGui.QToolButton()
self.tab_bar = QtGui.QTabBar(self)
self.add_button.setIcon(QtGui.QIcon('add.png'))
self.add_button.setMenu(self.set_menu())
self.add_button.setPopupMode(QtGui.QToolButton.InstantPopup)
self.tab_bar.setTabButton(
0,
QtGui.QTabBar.ButtonPosition.RightSide,
self.add_button
)
hlay.addWidget(self.add_button)
hlay.addWidget(self.tab_bar)
self.my_extra_menus = defaultdict(list)
def set_menu(self):
menu_options = ['food', 'drinks', 'snacks']
qmenu = QtGui.QMenu(self.add_button)
for opt in menu_options:
qmenu.addAction(opt, partial(self.set_new_tab, opt))
return qmenu
def set_new_tab(self, opt):
self.tab_bar.addTab(opt)
def mousePressEvent(self, event):
index = self.tab_bar.tabAt(event.pos())
if event.button() == QtCore.Qt.RightButton:
self._showContextMenu(event.pos(), index)
else:
super(MyWin, self).mousePressEvent(event)
def eventFilter(self, obj, event):
# Custom event only for right mouse click within the qmenu
if obj == self.qmenu:
if event.type() == QtCore.QEvent.MouseButtonPress and event.button() == QtCore.Qt.RightButton:
index = self.tab_bar.tabAt(event.pos())
action = obj.actionAt(event.pos())
self.show_adv_qmenu(obj, action, index)
def _showContextMenu(self, position, index):
self.qmenu = QtGui.QMenu(self)
self.qmenu.setTearOffEnabled(True) # New item is not shown in tearoff mode
self.qmenu.setTitle(self.tab_bar.tabText(index))
self.qmenu.installEventFilter(self)
add_item_action = QtGui.QAction('Add Menu Item', self)
slot = partial(self._addMenuItem, index)
add_item_action.triggered.connect(slot)
self.qmenu.addAction(add_item_action)
self.qmenu.addSeparator()
if self.my_extra_menus.get(index):
for menuItem in self.my_extra_menus[index]:
self.qmenu.addMenu(menuItem)
self.qmenu.addSeparator()
global_position = self.mapToGlobal(self.pos())
self.qmenu.exec_(QtCore.QPoint(
global_position.x() - self.pos().x() + position.x(),
global_position.y() - self.pos().y() + position.y()
))
def _addMenuItem(self, index):
# For first tier menu
first_tier_menu = []
for i in self.qmenu.actions():
first_tier_menu.append(i.text())
new_menu_name, ok = QtGui.QInputDialog.getText(
self,
"Name of Menu",
"Name of new Menu Item:"
)
if ok:
if new_menu_name in list(filter(None, first_tier_menu)):
self.err_popup()
else:
menu = QtGui.QMenu(new_menu_name, self)
menu.setTearOffEnabled(True) # New item is shown in tearoff mode, unless I close and re-tearoff
add_item_action = QtGui.QAction('Add sub Item', menu)
slot = partial(self._addActionItem, menu)
add_item_action.triggered.connect(slot)
menu.addAction(add_item_action)
menu.addSeparator()
self.my_extra_menus[index].append(menu)
def _addActionItem(self, menu):
# For second tier menu
new_item_name, ok = QtGui.QInputDialog.getText(
self,
"Name of Menu Item",
"Name of new Menu Item:"
)
second_tier_menu = []
for i in menu.actions():
second_tier_menu.append(i.text())
if ok:
if new_item_name in list(filter(None, second_tier_menu)):
self.err_popup()
else:
action = QtGui.QAction(new_item_name, self)
slot = partial(self._callActionItem, new_item_name)
action.setCheckable(True)
action.toggled.connect(slot)
menu.addAction(action)
def _callActionItem(self, name, flag):
# Function for the checked items..
print name
print flag
def show_adv_qmenu(self, obj, action, index):
self.adv_qmenu = QtGui.QMenu()
rename_menu_action = QtGui.QAction('Rename Item', self)
rename_slot = partial(self.rename_menu_item, obj, action)
rename_menu_action.triggered.connect(rename_slot)
self.adv_qmenu.addAction(rename_menu_action)
delete_menu_action = QtGui.QAction('Delete Item', self)
delete_slot = partial(self.delete_menu_item, obj, action, index)
delete_menu_action.triggered.connect(delete_slot)
self.adv_qmenu.addAction(delete_menu_action)
# global_position = self.mapToGlobal(self.pos())
# self.adv_qmenu.exec_(QtCore.QPoint(global_position))
self.adv_qmenu.exec_(QtGui.QCursor().pos())
def rename_menu_item(self, obj, selected_action):
rename_name, ok = QtGui.QInputDialog.getText(
self,
"renaming",
"New name:"
)
if ok:
for i in obj.actions():
if selected_action.text() == i.text():
i.setText(rename_name)
print "Name changed : {0} --> {1}".format(selected_action.text(), i.text())
def delete_menu_item(self, obj, selected_action, index):
obj.removeAction(selected_action)
def err_popup(self):
msg = QtGui.QMessageBox()
msg.setIcon(QtGui.QMessageBox.Critical)
msg.setText("Input name already exists. Please check.")
msg.setWindowTitle('Unable to add item')
msg.setStandardButtons(QtGui.QMessageBox.Ok)
msg.exec_()
很抱歉输入长代码。
答案 0 :(得分:1)
eventFilter必须返回一个布尔值,但是在您的情况下,它不返回任何内容,因此第二个错误将您抛出。另外,我还改善了您的逻辑:
def eventFilter(self, obj, event):
# Custom event only for right mouse click within the qmenu
if obj is self.qmenu and event.type() == QtCore.QEvent.MouseButtonPress:
if event.button() == QtCore.Qt.RightButton:
index = self.tab_bar.tabAt(event.pos())
action = self.qmenu.actionAt(event.pos())
if index != -1 and action is not None:
self.show_adv_qmenu(obj, action, index)
return super(MyWin, self).eventFilter(obj, event)