Qt 拖放不适用于自定义项目
Qt Drag & Drop doesn't work fine with custom items
我正在使用 PySide2 (Qt5) 和自定义树视图小部件(MyTreeView
,继承自 QTreeView
)构建 GUI。该模型是一个 QStandardItemModel
对象,而项目是自定义的:MyStandardItem
,继承自 QStandardItem
。
问题是:如果我在 拖放 操作后检查移动项目的类型,它已变成 QStandardItem
但它应该是MyStandardItem
。
我认为问题出在 MimeType,经过大量研究后我发现解决方案可能是创建自定义模型并覆盖 MIME 相关函数。
我想弄清楚怎么办,但我做不到。
那么,问题来了:
- 我必须创建自定义模型还是有简单的解决方案?
- 如果我必须创建自定义模型,我应该覆盖哪些函数以及我应该如何覆盖这些函数?
对于它的价值,这里是 MyStandardItem
实施:
class MyStandardItem(QStandardItem):
def __init__(self, text, font, icon_path='', value='', num=0, check_state=None):
super().__init__()
self.setDragEnabled(True)
self.setDropEnabled(True)
self.setText(text)
self.setData({'value': (value, num)})
self.setToolTip(str(self.data()['value']))
self.setFont(font)
self.setIcon(QIcon(icon_path))
self.toggled = check_state
if check_state is not None:
self.setCheckable(True)
self.setCheckState(check_state)
def setCheckState(self, checkState):
super().setCheckState(checkState)
if checkState == Qt.Unchecked:
self.toggled = Qt.Unchecked
else:
self.toggled = Qt.Checked
我找到了无需创建自定义模型即可解决此问题的方法。
在 MyTreeView.dropEvent
函数中:我没有调用 super().dropEvent()
来完成拖放操作,但我通过将项目的数据复制到变量中并创建一个 来自己实现它来自这些数据的 new MyStandardItem
。然后我调用 insertRow()
在给定位置插入新项目,并调用 deleteRow()
删除旧项目。
显然,移动的项目必须在操作开始时存储在 class 属性中 (DragEnterEvent()
)。
一切都很完美。
PS:我以前已经尝试过这种方法,但我总是以空行结束。这里的区别在于我创建了一个新项目而不是 re-inserting 旧项目。
下面是我的代码的某些部分,以阐明我的意思。请注意,这些函数是 MyTreeView 的方法。
def dragEnterEvent(self, event: QDragEnterEvent):
if event.source() == self:
self.dragged_item = self.model.itemFromIndex(self.selectionModel().selectedIndexes()[0])
super().dragEnterEvent(event)
else:
...
def dropEvent(self, event: QDropEvent):
index = self.indexAt(event.pos())
if not index.isValid():
return
over_item = self.model.itemFromIndex(index)
over_value = over_item.data()['value'][0]
if event.source() == self:
item_was_moved = self._move_item(over_value, over_parent_value)
if not item_was_moved:
return
else:
...
def _move_item(self, over_value, over_parent_value):
over_value = self._check_indicator_position(over_value)
if over_value is None:
return False
dragged_parent_value = self.dragged_item.parent().data()['value'][0]
dragged_value = self.dragged_item.data()['value'][0]
row = self.dragged_item.row()
items = guifunc.copy_row_items(self.dragged_item, row)
over_value_num = int(over_value.strip('test'))
self.dragged_item.parent().insertRow(over_value_num, items)
if over_value_num < row:
row += 1
self.dragged_item.parent().removeRow(row)
return True
我正在使用 PySide2 (Qt5) 和自定义树视图小部件(MyTreeView
,继承自 QTreeView
)构建 GUI。该模型是一个 QStandardItemModel
对象,而项目是自定义的:MyStandardItem
,继承自 QStandardItem
。
问题是:如果我在 拖放 操作后检查移动项目的类型,它已变成 QStandardItem
但它应该是MyStandardItem
。
我认为问题出在 MimeType,经过大量研究后我发现解决方案可能是创建自定义模型并覆盖 MIME 相关函数。
我想弄清楚怎么办,但我做不到。
那么,问题来了:
- 我必须创建自定义模型还是有简单的解决方案?
- 如果我必须创建自定义模型,我应该覆盖哪些函数以及我应该如何覆盖这些函数?
对于它的价值,这里是 MyStandardItem
实施:
class MyStandardItem(QStandardItem):
def __init__(self, text, font, icon_path='', value='', num=0, check_state=None):
super().__init__()
self.setDragEnabled(True)
self.setDropEnabled(True)
self.setText(text)
self.setData({'value': (value, num)})
self.setToolTip(str(self.data()['value']))
self.setFont(font)
self.setIcon(QIcon(icon_path))
self.toggled = check_state
if check_state is not None:
self.setCheckable(True)
self.setCheckState(check_state)
def setCheckState(self, checkState):
super().setCheckState(checkState)
if checkState == Qt.Unchecked:
self.toggled = Qt.Unchecked
else:
self.toggled = Qt.Checked
我找到了无需创建自定义模型即可解决此问题的方法。
在 MyTreeView.dropEvent
函数中:我没有调用 super().dropEvent()
来完成拖放操作,但我通过将项目的数据复制到变量中并创建一个 来自己实现它来自这些数据的 new MyStandardItem
。然后我调用 insertRow()
在给定位置插入新项目,并调用 deleteRow()
删除旧项目。
显然,移动的项目必须在操作开始时存储在 class 属性中 (DragEnterEvent()
)。
一切都很完美。
PS:我以前已经尝试过这种方法,但我总是以空行结束。这里的区别在于我创建了一个新项目而不是 re-inserting 旧项目。
下面是我的代码的某些部分,以阐明我的意思。请注意,这些函数是 MyTreeView 的方法。
def dragEnterEvent(self, event: QDragEnterEvent):
if event.source() == self:
self.dragged_item = self.model.itemFromIndex(self.selectionModel().selectedIndexes()[0])
super().dragEnterEvent(event)
else:
...
def dropEvent(self, event: QDropEvent):
index = self.indexAt(event.pos())
if not index.isValid():
return
over_item = self.model.itemFromIndex(index)
over_value = over_item.data()['value'][0]
if event.source() == self:
item_was_moved = self._move_item(over_value, over_parent_value)
if not item_was_moved:
return
else:
...
def _move_item(self, over_value, over_parent_value):
over_value = self._check_indicator_position(over_value)
if over_value is None:
return False
dragged_parent_value = self.dragged_item.parent().data()['value'][0]
dragged_value = self.dragged_item.data()['value'][0]
row = self.dragged_item.row()
items = guifunc.copy_row_items(self.dragged_item, row)
over_value_num = int(over_value.strip('test'))
self.dragged_item.parent().insertRow(over_value_num, items)
if over_value_num < row:
row += 1
self.dragged_item.parent().removeRow(row)
return True