문제

이틀 동안의 TreeView/Model 광란 후에 제가 완성한 코드는 다음과 같습니다.생각했던 것보다 주제가 훨씬 광범위했던 것 같습니다.단일 위젯을 만드는 데 너무 많은 시간을 소비할 수 없습니다.그래도.TreeView 항목의 끌어서 놓기 기능이 활성화되었습니다.그러나 몇 가지 흥미로운 인쇄물 외에는 그다지 많지 않습니다.항목을 두 번 클릭하면 사용자는 선택되지 않는 새 항목 이름을 입력할 수 있습니다.

수정된 코드로 하루 후에 편집되었습니다.

이제 90% 기능적인 도구가 되었습니다.

사용자는 드래그 앤 드롭, 생성/복제/삭제 및 이름 변경을 통해 TreeView 항목을 조작할 수 있습니다.TreeView 항목은 '인쇄' 버튼을 눌러 드라이브에 생성되기 전에 계층적 방식으로 디렉터리 또는 폴더를 나타냅니다(os.makedirs() 대신 도구는 여전히 각 디렉터리를 문자열로 인쇄합니다.
나는 그 결과에 매우 만족한다고 말하고 싶습니다.덕분에 해키데이 그리고 내 질문에 응답하고 도움을 주신 모든 분들께 감사드립니다.

마지막 소원 몇 가지...

소원 번호 01:

  1. PrintOut() 메서드가 더 우아하고 스마트한 함수를 사용하여 TreeView 항목을 반복하여 make_dirs_from_dict() 메서드에 전달되는 사전을 구축했으면 좋겠습니다.

소원 번호 02:

  1. 항목을 삭제하는 것이 더 안정적이기를 바랍니다.알 수 없는 이유로 세 번째/네 번째 삭제 버튼을 클릭하면 도구가 충돌합니다.지금까지는 문제를 추적할 수 없었습니다.

소원 번호 03:삼.모두에게 최선을 다하고 도움을 주셔서 감사합니다.

import sys, os
from PyQt4 import QtGui, QtCore
from PyQt4.QtGui import *
from PyQt4.QtCore import *
from copy import deepcopy
import cPickle    

class TreeItem(object):
    def __init__(self, name, parent=None):

        self.name = QtCore.QString(name)       
        self.parent = parent
        self.children = []       
        self.setParent(parent)

    def setParent(self, parent):
        if parent != None:
            self.parent = parent
            self.parent.appendChild(self)
        else:     self.parent = None

    def appendChild(self, child):
        self.children.append(child)

    def childAtRow(self, row):
        if len(self.children)>row: 
            return self.children[row]

    def rowOfChild(self, child):       
        for i, item in enumerate(self.children):
            if item == child:  return i
        return -1

    def removeChild(self, row):
        value = self.children[row]
        self.children.remove(value)
        return True

    def __len__(self):
        return len(self.children) 

class TreeModel(QtCore.QAbstractItemModel):
    def __init__(self):

        QtCore.QAbstractItemModel.__init__(self)

        self.columns = 1
        self.clickedItem=None

        self.root = TreeItem('root', None) 
        levelA = TreeItem('levelA', self.root)
        levelB = TreeItem('levelB', levelA)
        levelC1 = TreeItem('levelC1', levelB)
        levelC2 = TreeItem('levelC2', levelB)
        levelC3 = TreeItem('levelC3', levelB)
        levelD = TreeItem('levelD', levelC3)

        levelE = TreeItem('levelE', levelD)
        levelF = TreeItem('levelF', levelE)

    def nodeFromIndex(self, index):
        return index.internalPointer() if index.isValid() else self.root

    def index(self, row, column, parent):        
        node = self.nodeFromIndex(parent)
        return self.createIndex(row, column, node.childAtRow(row))

    def parent(self, child):
        # print '\n parent(child)', child  # PyQt4.QtCore.QModelIndex
        if not child.isValid():  return QModelIndex()
        node = self.nodeFromIndex(child)       
        if node is None:   return QModelIndex()
        parent = node.parent           
        if parent is None:      return QModelIndex()       
        grandparent = parent.parent

        if grandparent==None:    return QModelIndex()

        row = grandparent.rowOfChild(parent)    
        assert row != - 1

        return self.createIndex(row, 0, parent)

    def rowCount(self, parent):
        node = self.nodeFromIndex(parent)
        if node is None: return 0
        return len(node)

    def columnCount(self, parent):
        return self.columns

    def data(self, index, role):
        if role == Qt.DecorationRole:
            return QVariant()               
        if role == Qt.TextAlignmentRole:
            return QVariant(int(Qt.AlignTop | Qt.AlignLeft))       
        if role != Qt.DisplayRole:   
            return QVariant()                   
        node = self.nodeFromIndex(index)       
        if index.column() == 0:     
            return QVariant(node.name)       
        elif index.column() == 1:   
            return QVariant(node.state)       
        elif index.column() == 2:   
            return QVariant(node.description)
        else:   return QVariant()

    def supportedDropActions(self):
        return Qt.CopyAction | Qt.MoveAction

    def flags(self, index):
        defaultFlags = QAbstractItemModel.flags(self, index)       
        if index.isValid():  return Qt.ItemIsEditable | Qt.ItemIsDragEnabled | Qt.ItemIsDropEnabled | defaultFlags           
        else:   return Qt.ItemIsDropEnabled | defaultFlags  

    def setData(self, index, value, role):
        if role == Qt.EditRole:
            if value.toString() and len(value.toString())>0: 
                self.nodeFromIndex(index).name = value.toString()
                self.dataChanged.emit(index, index)
            return True

    def mimeTypes(self):
        return ['bstream', 'text/xml']

    def mimeData(self, indexes):

        mimedata = QtCore.QMimeData()
        bstream = cPickle.dumps(self.nodeFromIndex(indexes[0]))
        mimedata.setData('bstream', bstream)
        return mimedata

    def dropMimeData(self, mimedata, action, row, column, parentIndex):

        if action == Qt.IgnoreAction: return True  

        droppedNode=cPickle.loads(str(mimedata.data('bstream')))

        droppedIndex = self.createIndex(row, column, droppedNode)

        parentNode = self.nodeFromIndex(parentIndex)

        newNode = deepcopy(droppedNode)
        newNode.setParent(parentNode)

        self.insertRow(len(parentNode)-1, parentIndex)

        self.emit(SIGNAL("dataChanged(QModelIndex,QModelIndex)"), parentIndex, parentIndex)

        return True

    def insertRow(self, row, parent):
        return self.insertRows(row, 1, parent)
    def insertRows(self, row, count, parent):
        self.beginInsertRows(parent, row, (row + (count - 1)))
        self.endInsertRows()
        return True

    def removeRow(self, row, parentIndex):
        return self.removeRows(row, 1, parentIndex)

    def removeRows(self, row, count, parentIndex):
        self.beginRemoveRows(parentIndex, row, row)
        node = self.nodeFromIndex(parentIndex)
        node.removeChild(row)
        self.endRemoveRows()       
        return True


class GUI(QtGui.QDialog):
    def build(self, myWindow):
        myWindow.resize(600, 400)
        self.myWidget = QWidget(myWindow)        
        self.boxLayout = QtGui.QVBoxLayout(self.myWidget)

        self.treeView = QtGui.QTreeView()

        self.treeModel = TreeModel()
        self.treeView.setModel(self.treeModel)
        self.treeView.expandAll()
        self.treeView.setDragDropMode(QtGui.QAbstractItemView.InternalMove)
        self.treeView.connect(self.treeView.model(), SIGNAL("dataChanged(QModelIndex,QModelIndex)"), self.onDataChanged)
        QtCore.QObject.connect(self.treeView, QtCore.SIGNAL("clicked (QModelIndex)"),  self.treeItemClicked)
        self.boxLayout.addWidget(self.treeView)


        self.PrintButton= QtGui.QPushButton("Print")  
        self.PrintButton.clicked.connect(self.PrintOut) 
        self.boxLayout.addWidget(self.PrintButton)

        self.DeleteButton= QtGui.QPushButton("Delete")  
        self.DeleteButton.clicked.connect(self.DeleteLevel) 
        self.boxLayout.addWidget(self.DeleteButton)

        self.insertButton= QtGui.QPushButton("Insert")  
        self.insertButton.clicked.connect(self.insertLevel) 
        self.boxLayout.addWidget(self.insertButton)

        self.duplicateButton= QtGui.QPushButton("Duplicate")  
        self.duplicateButton.clicked.connect(self.duplicateLevel) 
        self.boxLayout.addWidget(self.duplicateButton)

        myWindow.setCentralWidget(self.myWidget)


    def make_dirs_from_dict(self, dirDict, current_dir='/'):
        for key, val in dirDict.items():
            #os.mkdir(os.path.join(current_dir, key))
            print "\t\t Creating directory: ", os.path.join(current_dir, key)
            if type(val) == dict:
                self.make_dirs_from_dict(val, os.path.join(current_dir, key))

    def PrintOut(self):
        result_dict = {}
        for a1 in self.treeView.model().root.children:
            result_dict[str(a1.name)]={}
            for a2 in a1.children:
                result_dict[str(a1.name)][str(a2.name)]={}
                for a3 in a2.children:
                    result_dict[str(a1.name)][str(a2.name)][str(a3.name)]={}
                    for a4 in a3.children:
                        result_dict[ str(a1.name)][str(a2.name)][str(a3.name)][str(a4.name)]={}
                        for a5 in a4.children:
                            result_dict[ str(a1.name)][str(a2.name)][str(a3.name)][str(a4.name)][str(a5.name)]={}
                            for a6 in a5.children:
                                result_dict[str(a1.name)][str(a2.name)][str(a3.name)][str(a4.name)][str(a5.name)][str(a6.name)]={}
                                for a7 in a6.children:
                                    result_dict[str(a1.name)][str(a2.name)][str(a3.name)][str(a4.name)][str(a5.name)][str(a6.name)][str(a7.name)]={}


        self.make_dirs_from_dict(result_dict)                      


    def DeleteLevel(self):
        if len(self.treeView.selectedIndexes())==0: return

        currentIndex = self.treeView.selectedIndexes()[0]
        currentRow=currentIndex.row()
        currentColumn=currentIndex.column()
        currentNode = currentIndex.internalPointer()

        parentNode = currentNode.parent
        parentIndex = self.treeView.model().createIndex(currentRow, currentColumn, parentNode)
        print '\n\t\t\t CurrentNode:', currentNode.name, ', ParentNode:', currentNode.parent.name, ', currentColumn:', currentColumn, ', currentRow:', currentRow 

        # self.treeView.model().removeRow(len(parentNode)-1, parentIndex) 

        self.treeView.model().removeRows(currentRow, 1, parentIndex )

        #self.treeView.model().removeRow(len(parentNode), parentIndex)
        #self.treeView.model().emit(SIGNAL("dataChanged(QModelIndex,QModelIndex)"), parentIndex, parentIndex) 

    def insertLevel(self):
        if len(self.treeView.selectedIndexes())==0: return

        currentIndex = self.treeView.selectedIndexes()[0]
        currentNode = currentIndex.internalPointer()
        newItem = TreeItem('Brand New', currentNode)
        self.treeView.model().insertRow(len(currentNode)-1, currentIndex)
        self.treeView.model().emit(SIGNAL("dataChanged(QModelIndex,QModelIndex)"), currentIndex, currentIndex)   

    def duplicateLevel(self):
        if len(self.treeView.selectedIndexes())==0: return

        currentIndex = self.treeView.selectedIndexes()[0]
        currentRow=currentIndex.row()
        currentColumn=currentIndex.column()
        currentNode=currentIndex.internalPointer()

        parentNode=currentNode.parent
        parentIndex=self.treeView.model().createIndex(currentRow, currentColumn, parentNode)
        parentRow=parentIndex.row()
        parentColumn=parentIndex.column()

        newNode = deepcopy(currentNode)
        newNode.setParent(parentNode)

        self.treeView.model().insertRow(len(parentNode)-1, parentIndex)
        self.treeView.model().emit(SIGNAL("dataChanged(QModelIndex,QModelIndex)"), parentIndex, parentIndex) 

        print '\n\t\t\t CurrentNode:', currentNode.name, ', ParentNode:', parentNode.name, ', currentColumn:', currentColumn, ', currentRow:', currentRow, ', parentColumn:', parentColumn, ', parentRow:', parentRow 
        self.treeView.update()
        self.treeView.expandAll()


    def treeItemClicked(self, index):
        print "\n clicked item ----------->", index.internalPointer().name

    def onDataChanged(self, indexA, indexB):
        print "\n onDataChanged NEVER TRIGGERED! ####################### \n ", index.internalPointer().name
        self.treeView.update(indexA)
        self.treeView.expandAll()
        self.treeView.expanded()



if __name__ == '__main__':

    app = QtGui.QApplication(sys.argv)

    myWindow = QMainWindow()
    myGui = GUI()
    myGui.build(myWindow)
    myWindow.show()
    sys.exit(app.exec_())
도움이 되었습니까?

해결책

나는 당신이 달성하려는 것이 무엇인지 완전히 확신하지 못하지만 놓기 작업에서 드래그된 항목을 검색하고 두 번 클릭하여 새 노드 이름을 저장하려는 것처럼 들립니다.

먼저, 드래그한 항목을 mimeData.현재는 'mimeData' 문자열만 저장하고 있는데, 이는 많은 정보를 제공하지 않습니다.그만큼 마임 유형 저장된 문자열(여기서는 'bstream'을 사용함)은 실제로 무엇이든 될 수 있습니다.데이터를 검색하는 데 사용하는 것과 일치하고 mimeTypes 모델의 방법.객체 자체를 전달하려면 먼저 이를 직렬화해야 합니다(만약 그렇게 할 계획이라면 객체를 xml로 변환할 수도 있습니다). 이는 MIME 데이터에 대한 표준 유형이 아니기 때문입니다.

입력한 데이터를 저장하려면 모델의 setData 메소드를 다시 구현하고 이에 대한 동작을 정의해야 합니다. EditRole.

관련 방법:

def setData(self, index, value, role):
    if role == Qt.EditRole:
        self.nodeFromIndex(index).name = value
        self.dataChanged.emit(index, index)
        return True

def mimeTypes(self):
    return ['bstream', 'text/xml']

def mimeData(self, indexes):
    mimedata = QtCore.QMimeData()
    # assuming single dragged item ...
    # only pass the node name
    # mimedata.setData('text/xml', str(self.nodeFromIndex(indexes[0]).name))
    # pass the entire object
    bstream = cPickle.dumps(self.nodeFromIndex(indexes[0]))
    mimedata.setData('bstream', bstream)
    return mimedata

def dropMimeData(self, mimedata, action, row, column, parentIndex):

    if action == Qt.IgnoreAction: return True        

    parentNode = self.nodeFromIndex(parentIndex)

    # data = mimedata.data('text/xml')
    data = cPickle.loads(str(mimedata.data('bstream')))
    print '\n\t incoming row number:', row, ', incoming column:', column, \
        ', action:', action, ' mimedata: ', data.name

    print "\n\t Item's name on which drop occurred: ", parentNode.name, \
        ', number of its childred:', len(parentNode.children)

    if len(parentNode.children)>0: print '\n\t zero indexed child:', parentNode.children[0].name

    return True

편집하다:
귀하가 업데이트한 코드가 너무 많지만 귀하가 강조한 사항은 꼭 준수하겠습니다.전화를 피하세요 createIndex 모델 클래스 외부.이것은 Qt에서 보호되는 방법입니다.Python은 비공개/보호 변수나 메서드를 적용하지 않지만, 이를 적용하는 다른 언어의 라이브러리를 사용할 때는 클래스의 의도된 구성을 존중하고 이에 액세스하려고 노력합니다.

모델의 목적은 데이터에 대한 인터페이스를 제공하는 것입니다.다음을 사용하여 액세스해야 합니다. index, data, parent 등.모델의 공개 기능.특정 인덱스의 상위를 얻으려면 해당 인덱스(또는 모델)를 사용하십시오. parent 이 함수는 QModelIndex도 반환합니다.이렇게 하면 데이터의 내부 구조를 살펴볼 필요가 없습니다(또는 실제로 알 필요도 없습니다).이것이 내가 에서 했던 일이다. deleteLevel 방법.

Qt에서 문서:

데이터 표현이 액세스되는 방식과 별도로 유지되도록 하기 위해 모델 인덱스라는 개념이 도입되었습니다.모델을 통해 얻을 수 있는 각 정보는 모델 인덱스로 표시됩니다.모델만이 데이터를 얻는 방법을 알면 되며 모델이 관리하는 데이터 유형은 상당히 일반적으로 정의될 수 있습니다.

또한 재귀를 사용하여 인쇄 방법을 단순화할 수 있습니다.

def printOut(self):
    result_dict = dictify(self.treeView.model().root)
    self.make_dirs_from_dict(result_dict)  

def deleteLevel(self):
    if len(self.treeView.selectedIndexes()) == 0: 
        return

    currentIndex = self.treeView.selectedIndexes()[0]
    self.treeView.model().removeRow(currentIndex.row(), currentIndex.parent())

수업시간이랑 따로 있었는데

def dictify(node):
    kids = {}
    for child in node.children:
        kids.update(dictify(child)) 
    return {str(node.name): kids}
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top