如果 QTreeWidget 设置为 DragDrop 模式,PyQt 项目列丢失
PyQt item column lost if QTreeWidget is set to DragDrop mode
我正在使用 QTreeWidget 创建文件夹树状结构。每个项目有 2 列:
- 文件夹名称。
- 文件夹id,被
tree.hideColumn(1)
设置为隐藏。
目标是在 QTreeWidget 内以及跨小部件启用 drag/drop,这需要设置 tree.setDragDropMode(DragDrop)
。但是,将模式从InternalMove
更改为DrapDrop
后,我发现被拖拽的QTreeWidgetItem只保留了它的第1列,之前存在的第2列丢失了。如果我查询 item.data(1,0)
它给出 None
.
如果我不隐藏第 2 列,它不会在拖动过程中丢失。
我很困惑。感谢任何帮助。
下面是一个工作示例。如果将任何项目拖到另一个项目上,控制台将打印 column counts 1
。与重命名(双击)拖动的项目相同。
import sys
from PyQt5.QtWidgets import QTreeWidget, QVBoxLayout,\
QMainWindow, QWidget, QTreeWidgetItem, QApplication, QAbstractItemView
from PyQt5.QtCore import Qt
class MainWindow(QMainWindow):
def __init__(self):
super(self.__class__, self).__init__()
frame=QWidget()
self.setCentralWidget(frame)
hl=QVBoxLayout()
frame.setLayout(hl)
self.tree=QTreeWidget(self)
self.tree.setColumnCount(2)
# if I don't hide 2nd column, it won't get lost during the drag.
self.tree.hideColumn(1)
self.tree.setDragEnabled(True)
# InternalMove gives 2 columns: name and id.
# DragDrop would only give the 1st column after a drag/drop
#self.tree.setDragDropMode(QAbstractItemView.InternalMove)
self.tree.setDragDropMode(QAbstractItemView.DragDrop)
hl.addWidget(self.tree)
# add treewidgetitems
data=[['Folder 1', '1'],
['Folder 2', '2'],
['Folder 3', '3']
]
for ii in range(3):
item=QTreeWidgetItem(data[ii])
self.tree.addTopLevelItem(item)
self.tree.itemDoubleClicked.connect(self.rename)
self.tree.itemChanged.connect(self.postRename, Qt.QueuedConnection)
self.show()
def rename(self):
item=self.tree.selectedItems()
if item:
item=item[0]
item.setFlags(item.flags() | Qt.ItemIsEditable)
self.tree.scrollToItem(item)
self.tree.editItem(item)
def postRename(self,item,column):
print('postRename: column counts', item.columnCount())
text=item.data(0,0)
itemid=item.data(1,0)
print('postRename: item text=',text, 'item id', itemid)
return
if __name__ == "__main__":
app = QApplication(sys.argv)
form = MainWindow()
form.show()
sys.exit(app.exec_())
在视图的startDrag方法中,仅以selectedIndexes()
返回的索引为基础,selectedIndexes()
仅returns selectionModel()
索引不隐藏,所以隐藏栏不发送信息。所以一个解决方法是 selectedIndexes()
方法不过滤隐藏索引:
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
class TreeWidget(QtWidgets.QTreeWidget):
def selectedIndexes(self):
return self.selectionModel().selectedIndexes()
class MainWindow(QtWidgets.QMainWindow):
def __init__(self):
super(self.__class__, self).__init__()
self.tree = TreeWidget(columnCount=2,
dragDropMode=QtWidgets.QAbstractItemView.DragDrop,
dragEnabled=True)
self.tree.hideColumn(1)
self.tree.itemDoubleClicked.connect(self.rename)
self.tree.itemChanged.connect(self.postRename)
# add treewidgetitems
data=[['Folder 1', '1'],
['Folder 2', '2'],
['Folder 3', '3']
]
for d in data:
item = QtWidgets.QTreeWidgetItem(d)
self.tree.addTopLevelItem(item)
frame = QtWidgets.QWidget()
self.setCentralWidget(frame)
hl = QtWidgets.QVBoxLayout(frame)
hl.addWidget(self.tree)
@QtCore.pyqtSlot()
def rename(self):
item=self.tree.selectedItems()
if item:
item=item[0]
item.setFlags(item.flags() | Qt.ItemIsEditable)
self.tree.scrollToItem(item)
self.tree.editItem(item)
@QtCore.pyqtSlot(QtWidgets.QTreeWidgetItem, int)
def postRename(self, item, column):
print('postRename: column counts', item.columnCount())
text = item.data(0,0)
itemid = item.data(1,0)
print('postRename: item text=',text, 'item id', itemid)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
form = MainWindow()
form.show()
sys.exit(app.exec_())
另一方面,如果你建立第1列的目的只是为了保存数据,最好使用避免隐藏列的角色:
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
IDRole = QtCore.Qt.UserRole + 1000
class MainWindow(QtWidgets.QMainWindow):
def __init__(self):
super(self.__class__, self).__init__()
self.tree = QtWidgets.QTreeWidget(columnCount=1,
dragDropMode=QtWidgets.QAbstractItemView.DragDrop,
dragEnabled=True)
self.tree.hideColumn(1)
self.tree.itemDoubleClicked.connect(self.rename)
self.tree.itemChanged.connect(self.postRename)
# add treewidgetitems
data=[['Folder 1', '1'],
['Folder 2', '2'],
['Folder 3', '3']
]
for d in data:
text, itemid = d
item = QtWidgets.QTreeWidgetItem([text])
item.setData(0, IDRole, itemid)
self.tree.addTopLevelItem(item)
frame = QtWidgets.QWidget()
self.setCentralWidget(frame)
hl = QtWidgets.QVBoxLayout(frame)
hl.addWidget(self.tree)
@QtCore.pyqtSlot()
def rename(self):
item=self.tree.selectedItems()
if item:
item=item[0]
item.setFlags(item.flags() | Qt.ItemIsEditable)
self.tree.scrollToItem(item)
self.tree.editItem(item)
@QtCore.pyqtSlot(QtWidgets.QTreeWidgetItem, int)
def postRename(self, item, column):
print('postRename: column counts', item.columnCount())
text = item.text(0)
itemid = item.data(0, IDRole)
print('postRename: item text=',text, 'item id', itemid)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
form = MainWindow()
form.show()
sys.exit(app.exec_())
我正在使用 QTreeWidget 创建文件夹树状结构。每个项目有 2 列:
- 文件夹名称。
- 文件夹id,被
tree.hideColumn(1)
设置为隐藏。
目标是在 QTreeWidget 内以及跨小部件启用 drag/drop,这需要设置 tree.setDragDropMode(DragDrop)
。但是,将模式从InternalMove
更改为DrapDrop
后,我发现被拖拽的QTreeWidgetItem只保留了它的第1列,之前存在的第2列丢失了。如果我查询 item.data(1,0)
它给出 None
.
如果我不隐藏第 2 列,它不会在拖动过程中丢失。
我很困惑。感谢任何帮助。
下面是一个工作示例。如果将任何项目拖到另一个项目上,控制台将打印 column counts 1
。与重命名(双击)拖动的项目相同。
import sys
from PyQt5.QtWidgets import QTreeWidget, QVBoxLayout,\
QMainWindow, QWidget, QTreeWidgetItem, QApplication, QAbstractItemView
from PyQt5.QtCore import Qt
class MainWindow(QMainWindow):
def __init__(self):
super(self.__class__, self).__init__()
frame=QWidget()
self.setCentralWidget(frame)
hl=QVBoxLayout()
frame.setLayout(hl)
self.tree=QTreeWidget(self)
self.tree.setColumnCount(2)
# if I don't hide 2nd column, it won't get lost during the drag.
self.tree.hideColumn(1)
self.tree.setDragEnabled(True)
# InternalMove gives 2 columns: name and id.
# DragDrop would only give the 1st column after a drag/drop
#self.tree.setDragDropMode(QAbstractItemView.InternalMove)
self.tree.setDragDropMode(QAbstractItemView.DragDrop)
hl.addWidget(self.tree)
# add treewidgetitems
data=[['Folder 1', '1'],
['Folder 2', '2'],
['Folder 3', '3']
]
for ii in range(3):
item=QTreeWidgetItem(data[ii])
self.tree.addTopLevelItem(item)
self.tree.itemDoubleClicked.connect(self.rename)
self.tree.itemChanged.connect(self.postRename, Qt.QueuedConnection)
self.show()
def rename(self):
item=self.tree.selectedItems()
if item:
item=item[0]
item.setFlags(item.flags() | Qt.ItemIsEditable)
self.tree.scrollToItem(item)
self.tree.editItem(item)
def postRename(self,item,column):
print('postRename: column counts', item.columnCount())
text=item.data(0,0)
itemid=item.data(1,0)
print('postRename: item text=',text, 'item id', itemid)
return
if __name__ == "__main__":
app = QApplication(sys.argv)
form = MainWindow()
form.show()
sys.exit(app.exec_())
在视图的startDrag方法中,仅以selectedIndexes()
返回的索引为基础,selectedIndexes()
仅returns selectionModel()
索引不隐藏,所以隐藏栏不发送信息。所以一个解决方法是 selectedIndexes()
方法不过滤隐藏索引:
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
class TreeWidget(QtWidgets.QTreeWidget):
def selectedIndexes(self):
return self.selectionModel().selectedIndexes()
class MainWindow(QtWidgets.QMainWindow):
def __init__(self):
super(self.__class__, self).__init__()
self.tree = TreeWidget(columnCount=2,
dragDropMode=QtWidgets.QAbstractItemView.DragDrop,
dragEnabled=True)
self.tree.hideColumn(1)
self.tree.itemDoubleClicked.connect(self.rename)
self.tree.itemChanged.connect(self.postRename)
# add treewidgetitems
data=[['Folder 1', '1'],
['Folder 2', '2'],
['Folder 3', '3']
]
for d in data:
item = QtWidgets.QTreeWidgetItem(d)
self.tree.addTopLevelItem(item)
frame = QtWidgets.QWidget()
self.setCentralWidget(frame)
hl = QtWidgets.QVBoxLayout(frame)
hl.addWidget(self.tree)
@QtCore.pyqtSlot()
def rename(self):
item=self.tree.selectedItems()
if item:
item=item[0]
item.setFlags(item.flags() | Qt.ItemIsEditable)
self.tree.scrollToItem(item)
self.tree.editItem(item)
@QtCore.pyqtSlot(QtWidgets.QTreeWidgetItem, int)
def postRename(self, item, column):
print('postRename: column counts', item.columnCount())
text = item.data(0,0)
itemid = item.data(1,0)
print('postRename: item text=',text, 'item id', itemid)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
form = MainWindow()
form.show()
sys.exit(app.exec_())
另一方面,如果你建立第1列的目的只是为了保存数据,最好使用避免隐藏列的角色:
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
IDRole = QtCore.Qt.UserRole + 1000
class MainWindow(QtWidgets.QMainWindow):
def __init__(self):
super(self.__class__, self).__init__()
self.tree = QtWidgets.QTreeWidget(columnCount=1,
dragDropMode=QtWidgets.QAbstractItemView.DragDrop,
dragEnabled=True)
self.tree.hideColumn(1)
self.tree.itemDoubleClicked.connect(self.rename)
self.tree.itemChanged.connect(self.postRename)
# add treewidgetitems
data=[['Folder 1', '1'],
['Folder 2', '2'],
['Folder 3', '3']
]
for d in data:
text, itemid = d
item = QtWidgets.QTreeWidgetItem([text])
item.setData(0, IDRole, itemid)
self.tree.addTopLevelItem(item)
frame = QtWidgets.QWidget()
self.setCentralWidget(frame)
hl = QtWidgets.QVBoxLayout(frame)
hl.addWidget(self.tree)
@QtCore.pyqtSlot()
def rename(self):
item=self.tree.selectedItems()
if item:
item=item[0]
item.setFlags(item.flags() | Qt.ItemIsEditable)
self.tree.scrollToItem(item)
self.tree.editItem(item)
@QtCore.pyqtSlot(QtWidgets.QTreeWidgetItem, int)
def postRename(self, item, column):
print('postRename: column counts', item.columnCount())
text = item.text(0)
itemid = item.data(0, IDRole)
print('postRename: item text=',text, 'item id', itemid)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
form = MainWindow()
form.show()
sys.exit(app.exec_())