带有上下文菜单的 QTreeWidget,无法获取正确的项目 [英] QTreeWidget with contextmenu, can't get correct item

查看:14
本文介绍了带有上下文菜单的 QTreeWidget,无法获取正确的项目的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有以下代码来创建一个 QTreeWidget 和一个包含 2 个操作的上下文菜单.

I have the following code to create a QTreeWidget and a contextmenu with 2 actions.

import sys
from PyQt5 import QtCore, QtWidgets

class Dialog(QtWidgets.QDialog):
    def __init__(self, parent=None):
        super(Dialog, self).__init__()

        self.tw = QtWidgets.QTreeWidget()
        self.tw.setHeaderLabels(['Name', 'Cost ($)'])
        cg = QtWidgets.QTreeWidgetItem(['carrots', '0.99'])
        c1 = QtWidgets.QTreeWidgetItem(['carrot', '0.33'])
        self.tw.addTopLevelItem(cg)
        self.tw.addTopLevelItem(c1)
        self.tw.installEventFilter(self)
        layout = QtWidgets.QVBoxLayout(self)
        layout.addWidget(self.tw)

    def eventFilter(self, source: QtWidgets.QTreeWidget, event):
        if (event.type() == QtCore.QEvent.ContextMenu and
            source is self.tw):
            menu = QtWidgets.QMenu()
            AAction = QtWidgets.QAction("AAAAA")
            AAction.triggered.connect(lambda :self.a(source.itemAt(event.pos())))
            BAction = QtWidgets.QAction("BBBBBB")
            BAction.triggered.connect(lambda :self.b(source, event))
            menu.addAction(AAction)
            menu.addAction(BAction)
            menu.exec_(event.globalPos())
            return True
        return super(Dialog, self).eventFilter(source, event)

    def a(self, item):
        if item is None:
            return
        print("A: {}".format([item.text(i) for i in range(self.tw.columnCount())]))
    def b(self, source, event):
        item = source.itemAt(event.pos())
        if item is None:
            return
        print("B: {}".format([item.text(i) for i in range(source.columnCount())]))

if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    window = Dialog()
    window.setGeometry(600, 100, 300, 200)
    window.show()
    sys.exit(app.exec_())

当打开标题中的上下文菜单并单击其中一个操作时,它会打印胡萝卜或胡萝卜,具体取决于我在上下文菜单中单击的位置.但是我把右键事件的位置给了函数.

When opening the contextmenu in the header and clicking on one of the actions it prints either carrot or carrots, depending on where in the contextmenu I click. But I give the position of right click event to the functions.

那么为什么会发生这种情况,我该怎么做才能阻止它?

So why is this happening and what can I do to stop it?

推荐答案

你的代码有2个错误:

  • 主要错误是 itemAt() 方法使用相对于 viewport() 的坐标,而不是相对于视图(QTreeWidget),所以你会得到不正确的项目(标题占用空间相对于 QTreeWidget 和 viewport() 的位置有一个偏移量).

  • The main error is that the itemAt() method uses the coordinates with respect to the viewport() and not with respect to the view (the QTreeWidget) so you will get incorrect items (the header occupies a space making the positions with respect to the QTreeWidget and the viewport() have an offset).

你不应该阻塞事件循环,例如阻塞 eventFilter 你可能阻塞了其他会导致难以调试的错误的事件,在这种情况下最好使用信号.

You should not block the eventloop, for example blocking the eventFilter you may be blocking other events that would cause errors that are difficult to debug, in this case it is better to use a signal.

class Dialog(QtWidgets.QDialog):
    rightClicked = QtCore.pyqtSignal(QtCore.QPoint)

    def __init__(self, parent=None):
        super(Dialog, self).__init__()

        self.tw = QtWidgets.QTreeWidget()
        self.tw.setHeaderLabels(["Name", "Cost ($)"])
        cg = QtWidgets.QTreeWidgetItem(["carrots", "0.99"])
        c1 = QtWidgets.QTreeWidgetItem(["carrot", "0.33"])
        self.tw.addTopLevelItem(cg)
        self.tw.addTopLevelItem(c1)

        self.tw.viewport().installEventFilter(self)

        layout = QtWidgets.QVBoxLayout(self)
        layout.addWidget(self.tw)

        self.rightClicked.connect(self.handle_rightClicked)

    def eventFilter(self, source: QtWidgets.QTreeWidget, event):
        if event.type() == QtCore.QEvent.ContextMenu and source is self.tw.viewport():
            self.rightClicked.emit(event.pos())
            return True

        return super(Dialog, self).eventFilter(source, event)

    def handle_rightClicked(self, pos):
        item = self.tw.itemAt(pos)
        if item is None:
            return
        menu = QtWidgets.QMenu()
        print_action = QtWidgets.QAction("Print")
        print_action.triggered.connect(lambda checked, item=item: self.print_item(item))
        menu.addAction(print_action)
        menu.exec_(self.tw.viewport().mapToGlobal(pos))

    def print_item(self, item):
        if item is None:
            return
        texts = []
        for i in range(item.columnCount()):
            text = item.text(i)
            texts.append(text)

        print("B: {}".format(",".join(texts)))

虽然没有必要使用 eventFilter 来处理 contextmenu,因为更简单的解决方案是将 QTreeWidget 的 contextMenuPolicy 设置为 Qt::CustomContextMenu 并使用 customContextMenuRequested 信号:

Although it is unnecessary that you use an eventFilter to handle the contextmenu since a simpler solution is to set the contextMenuPolicy of the QTreeWidget to Qt::CustomContextMenu and use the customContextMenuRequested signal:

class Dialog(QtWidgets.QDialog):
    def __init__(self, parent=None):
        super(Dialog, self).__init__()

        self.tw = QtWidgets.QTreeWidget()
        self.tw.setHeaderLabels(["Name", "Cost ($)"])
        cg = QtWidgets.QTreeWidgetItem(["carrots", "0.99"])
        c1 = QtWidgets.QTreeWidgetItem(["carrot", "0.33"])
        self.tw.addTopLevelItem(cg)
        self.tw.addTopLevelItem(c1)

        self.tw.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
        self.tw.customContextMenuRequested.connect(self.handle_rightClicked)

        layout = QtWidgets.QVBoxLayout(self)
        layout.addWidget(self.tw)

    def handle_rightClicked(self, pos):
        item = self.tw.itemAt(pos)
        if item is None:
            return
        menu = QtWidgets.QMenu()
        print_action = QtWidgets.QAction("Print")
        print_action.triggered.connect(lambda checked, item=item: self.print_item(item))
        menu.addAction(print_action)
        menu.exec_(self.tw.viewport().mapToGlobal(pos))

    def print_item(self, item):
        if item is None:
            return
        texts = []
        for i in range(item.columnCount()):
            text = item.text(i)
            texts.append(text)

        print("B: {}".format(",".join(texts)))

这篇关于带有上下文菜单的 QTreeWidget,无法获取正确的项目的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆