在 PyQt 的 QTreeWidget
中,如果你遇到 拖放 和 点击 的异常行为,可能是由于信号处理、事件拦截、拖放设置或树结构配置等问题导致的。以下是一些可能的常见问题和解决方案。
1、问题背景
一个 PyQt 应用程序中包含两个 Tree Widget,当用户从一个 Tree Widget 拖动项目并将其释放到另一个 Tree Widget 时,程序运行良好。但是,如果用户将项目拖动并释放到相同的 Tree Widget(这是一种不希望的行为,因此我在代码中禁用了接受拖放操作),Tree Widget 会忽略用户接下来的鼠标点击事件。
当用户拖动一个项目并将其释放到相同的 Tree Widget 时,可以看到以下问题:
- 用户点击左侧 Tree Widget 中的任何项目,而不会发生任何变化。
- 用户再次点击相同或其他项目时,选择才会发生改变。
- 试图点击展开图标,无论用户点击多少次,都不会触发任何事件。
要重现此问题,请运行代码并执行以下步骤:
- 从左侧的树中拖动一个项目,并将其释放到相同的树中。
- 单击左侧树中的任何项目,您将注意到没有任何变化
- 再次单击相同或其他项目,选择就会更改。
2、解决方案
为了解决这个问题,我修改了 MyTreeWidget
的 mousePressEvent
方法,以确保在用户点击 Tree Widget 时鼠标按下位置被正确记录。之前,mousePressEvent
方法只在左键点击时记录鼠标按下位置,这导致了上述异常行为。现在,只要用户点击 Tree Widge,即使没有按下左键,鼠标按下位置都会被记录。
修改后的 mousePressEvent
方法如下:
class MyTreeWidget(QtGui.QTreeWidget):
# ...
def mousePressEvent(self, event):
super(MyTreeWidget, self).mousePressEvent(event)
self.mousePressPos = event.pos()
这样可以确保鼠标按下位置始终被正确记录,从而解决了上述异常行为。
完整的代码如下:
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
中的拖放和点击行为异常问题。