после того, как я не смог найти приличного общего иерархического переупорядочиваемого примера перетаскивания для QtreeView Qt5, я попытался соответствующим образом преобразовать пример кода Editable Tree Model.
Есть связанный вопрос, записанный в:
QTreeView с поддержкой перетаскивания в PyQt, но хотя это PyQt4, который сам по себе не является проблемой (в любом случае я собираюсь преобразовать его в PyQt;)), модель дерева + абстрактная модель не работает должным образом. По крайней мере, он не меняет порядок здесь.
Этот пример кода работает не так хорошо: он позволяет перемещать элементы, но удаление их приводит к пустой строке, но запись не перемещается.
diff -up editabletreemodel.orig/mainwindow.cpp editabletreemodel/mainwindow.cpp
--- editabletreemodel.orig/mainwindow.cpp 2016-06-10 08:48:56.000000000 +0200
+++ editabletreemodel/mainwindow.cpp 2016-10-25 23:20:09.909671875 +0200
@@ -67,6 +67,7 @@ MainWindow::MainWindow(QWidget *parent)
file.close();
view->setModel(model);
+ view->setDragDropMode(QAbstractItemView::InternalMove);
for (int column = 0; column < model->columnCount(); ++column)
view->resizeColumnToContents(column);
diff -up editabletreemodel.orig/treemodel.cpp editabletreemodel/treemodel.cpp
--- editabletreemodel.orig/treemodel.cpp 2016-06-10 08:48:56.000000000 +0200
+++ editabletreemodel/treemodel.cpp 2016-10-25 23:23:47.408024344 +0200
@@ -96,10 +96,12 @@ QVariant TreeModel::data(const QModelInd
//! [3]
Qt::ItemFlags TreeModel::flags(const QModelIndex &index) const
{
- if (!index.isValid())
- return 0;
+ Qt::ItemFlags defaultFlags = Qt::ItemIsEditable | QAbstractItemModel::flags(index);
- return Qt::ItemIsEditable | QAbstractItemModel::flags(index);
+ if (index.isValid())
+ return Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | defaultFlags;
+ else
+ return Qt::ItemIsDropEnabled | defaultFlags;
}
//! [3]
@@ -295,3 +297,8 @@ void TreeModel::setupModelData(const QSt
++number;
}
}
+
+Qt::DropActions TreeModel::supportedDropActions() const
+{
+ return Qt::MoveAction;
+}
diff -up editabletreemodel.orig/treemodel.h editabletreemodel/treemodel.h
--- editabletreemodel.orig/treemodel.h 2016-06-10 08:48:56.000000000 +0200
+++ editabletreemodel/treemodel.h 2016-10-25 23:19:18.884870266 +0200
@@ -95,6 +95,7 @@ public:
const QModelIndex &parent = QModelIndex()) Q_DECL_OVERRIDE;
bool removeRows(int position, int rows,
const QModelIndex &parent = QModelIndex()) Q_DECL_OVERRIDE;
+ Qt::DropActions supportedDropActions() const Q_DECL_OVERRIDE;
private:
void setupModelData(const QStringList &lines, TreeItem *parent);
Теоретически это все, что нужно, чтобы иметь возможность переупорядочивать товары.
Вот версия PyQt5:
— editabletreemodel.py.orig 2015-07-17 13: 39: 33.000000000 +0200
+++ editabletreemodel.py 2016-10-26 00: 24: 51.857176297 +0200
@@ -44,7 +44,7 @@
from PyQt5.QtCore import (QAbstractItemModel, QFile, QIODevice,
QItemSelectionModel, QModelIndex, Qt)
-from PyQt5.QtWidgets import QApplication, QMainWindow
+from PyQt5.QtWidgets import QApplication, QMainWindow, QAbstractItemView
import editabletreemodel_rc
from ui_mainwindow import Ui_MainWindow
@@ -151,10 +151,12 @@ class TreeModel(QAbstractItemModel):
return item.data(index.column())
def flags(self, index):
- if not index.isValid():
- return 0
+ defaultFlags = Qt.ItemIsEditable | super(TreeModel, self).flags(index)
- return Qt.ItemIsEditable | Qt.ItemIsEnabled | Qt.ItemIsSelectable
+ if index.isValid():
+ return defaultFlags | Qt.ItemIsDragEnabled | Qt.ItemIsDropEnabled
+ else:
+ return defaultFlags | Qt.ItemIsDropEnabled
def getItem(self, index):
if index.isValid():
@@ -296,6 +298,9 @@ class TreeModel(QAbstractItemModel):
number += 1
+ def supportedDropActions(self):
+ return Qt.MoveAction
+
class MainWindow(QMainWindow, Ui_MainWindow):
def __init__(self, parent=None):
@@ -311,6 +316,7 @@ class MainWindow(QMainWindow, Ui_MainWin
file.close()
self.view.setModel(model)
+ self.view.setDragDropMode(QAbstractItemView.InternalMove)
for column in range(model.columnCount()):
self.view.resizeColumnToContents(column)
Мне удалось продлить ответ из упомянутого вопроса, чтобы сделать элементы модели переупорядочиваемыми путем перетаскивания. Тем не менее, я должен указать, что приведенный пример и мой ответ на самом деле имеют дело с моделью, напоминающей список модель больше, чем дерево один, потому что drag-n-drop не влияет на отношения родитель-потомок между элементами модели, вместо этого реализована обработка удаления, чтобы всегда выполнять переупорядочение элементов.
Поскольку drag-n-drop требует сериализации некоторых данных о перемещаемом элементе, переупорядочение с помощью drag-n-drop может быть реализовано следующим образом:
removeRows
метод.Вот полный код решения для PyQt4:
import sys
from PyQt4 import QtGui, QtCore
class TreeModel(QtCore.QAbstractItemModel):
def __init__(self):
QtCore.QAbstractItemModel.__init__(self)
self.nodes = ['node0', 'node1', 'node2', 'node3', 'node4', 'node5']
def index(self, row, column, parent):
if row < 0 or row >= len(self.nodes):
return QtCore.QModelIndex()
return self.createIndex(row, column, self.nodes[row])
def parent(self, index):
return QtCore.QModelIndex()
def rowCount(self, index):
if index.isValid():
return 0
if index.internalPointer() in self.nodes:
return 0
return len(self.nodes)
def columnCount(self, index):
if index.isValid():
return 0
return 1
def data(self, index, role):
if not index.isValid():
return None
if role == 0:
return index.internalPointer()
else:
return None
def supportedDropActions(self):
return QtCore.Qt.CopyAction | QtCore.Qt.MoveAction
def flags(self, index):
if not index.isValid():
return QtCore.Qt.ItemIsEnabled
return QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable | \
QtCore.Qt.ItemIsDragEnabled | QtCore.Qt.ItemIsDropEnabled
def insertRows(self, row, count, index):
if index.isValid():
return False
if count <= 0:
return False
# inserting 'count' empty rows starting at 'row'
self.beginInsertRows(QtCore.QModelIndex(), row, row + count - 1)
for i in range(0, count):
self.nodes.insert(row + i, '')
self.endInsertRows()
return True
def removeRows(self, row, count, index):
if index.isValid():
return False
if count <= 0:
return False
num_rows = self.rowCount(QtCore.QModelIndex())
self.beginRemoveRows(QtCore.QModelIndex(), row, row + count - 1)
for i in range(count, 0, -1):
self.nodes.pop(row - i + 1)
self.endRemoveRows()
return True
def setData(self, index, value, role):
if not index.isValid():
return False
if index.row() < 0 or index.row() > len(self.nodes):
return False
self.nodes[index.row()] = str(value)
self.dataChanged.emit(index, index)
def mimeTypes(self):
return ['application/vnd.treeviewdragdrop.list']
def mimeData(self, indexes):
mimedata = QtCore.QMimeData()
encoded_data = QtCore.QByteArray()
stream = QtCore.QDataStream(encoded_data, QtCore.QIODevice.WriteOnly)
for index in indexes:
if index.isValid():
text = self.data(index, 0)
stream << QtCore.QString(text)
mimedata.setData('application/vnd.treeviewdragdrop.list', encoded_data)
return mimedata
def dropMimeData(self, data, action, row, column, parent):
if action == QtCore.Qt.IgnoreAction:
return True
if not data.hasFormat('application/vnd.treeviewdragdrop.list'):
return False
if column > 0:
return False
num_rows = self.rowCount(QtCore.QModelIndex())
begin_row = 0
if row != -1:
begin_row = row
elif parent.isValid():
begin_row = parent.row()
else:
begin_row = num_rows
if begin_row != num_rows and begin_row != 0:
begin_row += 1
encoded_data = data.data('application/vnd.treeviewdragdrop.list')
stream = QtCore.QDataStream(encoded_data, QtCore.QIODevice.ReadOnly)
new_items = []
rows = 0
while not stream.atEnd():
text = QtCore.QString()
stream >> text
new_items.append(text)
rows += 1
# insert the new rows for the dropped items and set the data to these items appropriately
self.insertRows(begin_row, rows, QtCore.QModelIndex())
for text in new_items:
idx = self.index(begin_row, 0, QtCore.QModelIndex())
self.setData(idx, text, 0)
self.dataChanged.emit(idx, idx)
begin_row += 1
return True
class MainForm(QtGui.QMainWindow):
def __init__(self, parent=None):
super(MainForm, self).__init__(parent)
self.treeModel = TreeModel()
self.view = QtGui.QTreeView()
self.view.setModel(self.treeModel)
self.view.setDragDropMode(QtGui.QAbstractItemView.InternalMove)
self.setCentralWidget(self.view)
def main():
app = QtGui.QApplication(sys.argv)
form = MainForm()
form.show()
app.exec_()
if __name__ == '__main__':
main()
Upd.На всякий случай, если кому-то интересно, я расширил эту демонстрацию, чтобы правильно обработать выделение после перетаскивания, можно найти код для PyQt5 и Python 3 Вот.
Других решений пока нет …