2016-10-25 1 views
0

Qt5의 QTreeView에 대해 일반적인 계층 적 재 배열 가능 끌어서 놓기 예제를 찾을 수 없으면 적절하게 편집 가능한 트리 모델 예제 코드를 변형하려고했습니다.편집 가능한 재주문 가능 (드래그 앤 드롭 방식) Qt5 QTreeView 예

가에 기록 된 관련 질문 : QTreeView with drag and drop support in PyQt는하지만 (어쨌든 PyQt는이를 변환하는거야) 그 자체가 문제가되지 않습니다 PyQt4을,있는 동안), 트 리뷰 + 추상적 인 모델은하지 않습니다 올바르게 작동. 적어도 여기서는 항목을 재정렬하지 않습니다.

이 예제 코드는 작동하지 않지만 항목을 이동할 수는 있지만 항목을 삭제하면 빈 행이되지만 항목은 이동하지 않습니다.

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); 

이론상으로 이것은 항목을 재정렬하기 위해 필요한 것입니다. --- editabletreemodel.py.orig 2015년 7월 17일 13 : 39 : 33.000000000 0200 +++ editabletreemodel.py 2016년 10월 26일 00 : 24 : 51.857176297 0200

다음은 PyQt5 버전입니다 @@ -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) 

답변

1

나는 모델 항목을 드래그 앤 드롭으로 재정렬하기 위해 참조 질문에서 answer을 연장하는 데 성공했습니다. 그러나 참조 된 예제와 내 대답은 리스트의 모델 모델과 비슷합니다. 트리보다 모델을 드래그 앤 드롭하면 해당 모델의 부모 - 자식 관계에 영향을 미치지 않기 때문입니다. 모델 항목 대신 드롭 처리가 항상 항목 순서 변경을 수행하도록 구현됩니다.

만큼 드래그 앤 드롭이 항목에 대한 일부 데이터의 직렬화를 필요로 드래그 앤 드롭을 통해 재 배열은 다음과 같은 방법으로 구현 될 수있다 재배치되고 : 우리가 직렬화 드래그

  1. 드롭에 드래그 된 항목
  2. 에 대한 몇 가지 정보를 우리 :
    • 직렬화 다시이 정보
    • 사용이 정보 모델에서 각 드래그 된 항목의 원래 위치를 찾을 수
    • 모델에서 찾은 항목을 제거하여 나머지 항목의 위치가 변경되도록합니다. 모델이 적절하게 removeRows 메소드를 구현한다면 Qt는 우리를 위해 그것을 할 것입니다.
    • 제거한 항목을 다시 모델에 넣되 이번에는 앞에 앞에 놓거나 뒤에 붙이십시오. 전자의 옵션은 첫 번째 항목으로 떨어지는 데 유용하며 후자의 옵션은 다른 경우에 유용합니다.

여기 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의 코드는 here입니다.

관련 문제