PyQt 的Tree Widget中拖放和点击的异常行为

2024-10-10 10:56:17 浏览数 (2)

在 PyQt 的 QTreeWidget 中,如果你遇到 拖放点击 的异常行为,可能是由于信号处理、事件拦截、拖放设置或树结构配置等问题导致的。以下是一些可能的常见问题和解决方案。

1、问题背景

一个 PyQt 应用程序中包含两个 Tree Widget,当用户从一个 Tree Widget 拖动项目并将其释放到另一个 Tree Widget 时,程序运行良好。但是,如果用户将项目拖动并释放到相同的 Tree Widget(这是一种不希望的行为,因此我在代码中禁用了接受拖放操作),Tree Widget 会忽略用户接下来的鼠标点击事件。

当用户拖动一个项目并将其释放到相同的 Tree Widget 时,可以看到以下问题:

  1. 用户点击左侧 Tree Widget 中的任何项目,而不会发生任何变化。
  2. 用户再次点击相同或其他项目时,选择才会发生改变。
  3. 试图点击展开图标,无论用户点击多少次,都不会触发任何事件。

要重现此问题,请运行代码并执行以下步骤:

  1. 从左侧的树中拖动一个项目,并将其释放到相同的树中。
  2. 单击左侧树中的任何项目,您将注意到没有任何变化
  3. 再次单击相同或其他项目,选择就会更改。

2、解决方案

为了解决这个问题,我修改了 MyTreeWidgetmousePressEvent 方法,以确保在用户点击 Tree Widget 时鼠标按下位置被正确记录。之前,mousePressEvent 方法只在左键点击时记录鼠标按下位置,这导致了上述异常行为。现在,只要用户点击 Tree Widge,即使没有按下左键,鼠标按下位置都会被记录。

修改后的 mousePressEvent 方法如下:

代码语言:javascript复制
class MyTreeWidget(QtGui.QTreeWidget):
    # ...
​
    def mousePressEvent(self, event):
        super(MyTreeWidget, self).mousePressEvent(event)
        self.mousePressPos = event.pos()

这样可以确保鼠标按下位置始终被正确记录,从而解决了上述异常行为。

完整的代码如下:

代码语言:javascript复制
from PyQt4 import QtGui, QtCore
import cPickle
​
class MyTreeItem(QtGui.QTreeWidgetItem):
    def __init__(self, parent=None):
        super(MyTreeItem, self).__init__(parent)
        self.setFlags(QtCore.Qt.ItemIsDragEnabled | QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsUserCheckable | QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsDropEnabled)
​
​
    def getPath(self):
        """
        Rebuild path from the tree.
        """
        if isinstance(self.parent(), MyTreeItem):
           path = '{0}/{1}'.format(self.parent().getPath() ,str(self.text(0)))
        #The top level item
        else:
           path = '/{0}'.format(str(self.text(0)))
​
        return path
​
​
    def getParents(self):
        """
        Get all the parents to the top level.
        """
        parents = []
​
        while self:
            self = self.parent()
            if isinstance(self, MyTreeItem):
                parents.append(self)
​
        return parents
​
​
    def getChildren(self):
        """
        Get all the children(flatten).
        """
        children = []
        if not self:
            return children
​
        childrenCount = self.childCount()
​
        if childrenCount == 0:
            return children
​
        for idx in range(childrenCount):
            citem = self.child(idx)
            if citem:
                children.append(citem)
                children.extend(citem.getChildren())
​
        return children
​
class MyTreeWidget(QtGui.QTreeWidget):
    def __init__(self, parent = None):
        super(MyTreeWidget, self).__init__(parent)
        self.setDragEnabled(True)
        self.setAcceptDrops(True)
        self.setHeaderLabels(["Select Members"])
        self.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection)
        self.mousePressPos = QtCore.QPoint(0,0)
​
​
    def mousePressEvent(self, event):
        super(MyTreeWidget, self).mousePressEvent(event)
        self.mousePressPos = event.pos()
​
​
    def mouseMoveEvent(self, event):
        super(MyTreeWidget, self).mouseMoveEvent(event)
​
        self.setAcceptDrops(False)
        drag = QtGui.QDrag(self)
        mime_data = QtCore.QMimeData()
        passme = []
        for sel in self.selectedItems():
            dnddict = {}
            dnddict['disp'] = sel.getPath()
            dnddict['val'] = sel.getPath()
​
            passme.append(dnddict)
​
        bstream = cPickle.dumps(passme)
        mime_data.setData("application/x-ltreedata", bstream)
        drag.setMimeData(mime_data)
        self.setAcceptDrops(True)
        action = drag.exec_()
​
​
    def mouseReleaseEven(self, event):
        self.setAcceptDrop(True)
        event.accept()
​
​
    def dragMoveEvent(self, event):
        if event.mimeData().hasFormat("application/x-ltreedata"):
            event.accept()
        else:
            event.ignore()
​
​
    def dragEnterEvent(self, event):
        if event.mimeData().hasFormat("application/x-ltreedata"):
            event.accept()
        else:
            event.ignore()
​
​
    def dropEvent(self, event):
        if event.source() == self:
            event.ignore()
        else:
            for item in event.source().selectedItems():
                print item.text(0)
​
    def addItems(self, itemList):
        """
        Take a list of path-like strings
        """
        for item in itemList:
            self.addItem(item)
​
​
    def addItem(self, item):
        """
        Convert each item to a tree item
        """
        joints = item.strip('/').split('/')
​
        joint = None
        while joints:
            joint = self.addTreeJoint(joints.pop(0), joint)
​
​
    def addTreeJoint(self, jointName, parent=None):
        """
        Add item to the tree widget
        """
        returnItem = None
        #If it the top of the tree
        if not parent:
            #Find existing item
            for item in self.findItems(QtCore.QString(jointName),QtCore.Qt.MatchExactly):
                if jointName == item.text(0):
​
                    return item
            #Create new top level item
            returnItem = MyTreeItem(self)
            returnItem.setText(0, jointName)
​
        #We search all the children of this tree level and figure out if
        #we need to create a new tree item.
        else:
            for idx in range(parent.childCount()):
                if parent.childCount() == 0:
                    break
                if jointName == parent.child(idx).text(0):
                    return parent.child(idx)
​
            #Create new item
            returnItem = MyTreeItem(parent)
            returnItem.setText(0, jointName)
​
        return returnItem
​
​
class GeometrySelector(QtGui.QWidget):
    accepted = QtCore.pyqtSignal(list)
    def __init__(self, parent = None):
        super(GeometrySelector, self).__init__(parent)
​
​
        self.treeWidget = MyTreeWidget(self)
        self.treeWidget.addItems(get_objs())
​
        button = QtGui.QPushButton('Add Members', self)
        button.setFocusPolicy(QtCore.Qt.NoFocus)
        button.clicked.connect(self.cb_accept)
​
        filterLabel = QtGui.QLabel(self)
        filterLabel.setText('Filter:')
        filterField = QtGui.QLineEdit(self)
        filterField.textChanged.connect(self.filterChanged)
​
​
        filterBox = QtGui.QHBoxLayout()
        filterBox.addWidget(filterLabel)
        filterBox.addWidget(filterField)
​
        mainLayout = QtGui.QGridLayout()
        mainLayout.addWidget(self.treeWidget,0,0)
        mainLayout.addLayout(filterBox,1,0)
        mainLayout.addWidget(button,2,0)
        self.setLayout(mainLayout)
​
        pal = self.palette()
        pal.setColor(QtGui.QPalette.Base, QtGui.QColor(80, 80, 80))
        pal.setColor(QtGui.QPalette.Text, QtGui.QColor(230, 230, 230))
        self.setPalette(pal)
        button.setPalette(pal)
        self.treeWidget.setPalette(pal)
​
​
    def filterChanged(self, filterStr):
        showedItem = []
​
        matchFlag = QtCore.Qt.MatchFlags(QtCore.Qt.MatchContains | QtCore.Qt.MatchRecursive | QtCore.Qt.MatchRegExp)
        allItems = self.treeWidget.findItems(QtCore.

过这些调整,通常可以解决 PyQt QTreeWidget 中的拖放和点击行为异常问题。

0 人点赞