Pergunta

Aqui está um código acabei, depois de dois dias de TreeView/Modelo loucura.O assunto parecia ser muito mais amplo do que eu pensava.Eu mal posso gastar muito tempo criando um único widget.De qualquer maneira.O arrastar-e-soltar do TreeView itens tiver sido activada.Mas o que é interessante poucos a impressão de que não há muito lá.O duplo-clique sobre um item permite que o usuário digite um novo nome de item que não vai ser pego.

EDITOU UM DIA MAIS TARDE COM UM CÓDIGO REVISTO.

Agora é 90% funcional da ferramenta.

O usuário pode manipular os itens TreeView por arrastar e largar, a criação/duplicação/eliminar e mudar o nome.O TreeView itens estão representando os diretórios ou pastas em forma hierárquica, antes de que eles são criados em uma unidade clicando no botão 'Imprimir' botão (em vez de os.makedirs() a ferramenta ainda simplesmente imprime cada diretório como uma seqüência de caracteres.
Gostaria de dizer que eu estou muito feliz com o resultado.Graças a hackyday e a todos que responderam e ajudou com as minhas dúvidas.

Algumas últimos desejos...

O desejo de número 01:

  1. Eu gostaria de Impressão() método seria usar um mais elegante, mais inteligente função para percorrer os itens TreeView para a construção de um dicionário que está sendo passado para make_dirs_from_dict() método.

O desejo de número 02:

  1. Eu gostaria de excluir os itens seria mais estável.Por alguma razão desconhecida, uma ferramenta de quedas na terceira/quarta Eliminar cliques de botão.Até então, eu era incapaz de identificar o problema.

O desejo de número 03:3.Desejo a todos o melhor e graças a sua ajuda :

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_())
Foi útil?

Solução

Eu não estou totalmente certo de que você está tentando alcançar, mas parece que você quer para recuperar o item arrastado na queda de operação, e têm duplo clique em salvar um novo nome de nó.

Em primeiro lugar, você precisa salvar o item arrastado para o mimeData.Atualmente, você só salvar a string 'mimeData", que não diz muita coisa.O mimeType seqüência de caracteres que é salvo como (aqui eu usei 'bstream'), na verdade, pode ser qualquer coisa.Desde que coincida com o que você usa para recuperar os dados, e está na lista retornada pelo mimeTypes método do modelo.Para passar o objeto em si, você deve primeiro serializá-lo (você pode converter o objeto para xml em alternativa, se é que isso era algo que você está pensando em fazer), pois ele não é um padrão de tipo de mime de dados.

Para que os dados que você insere para ser salvo, você deve re-implementar o método setData do modelo e definir o comportamento de EditRole.

Os métodos relevantes:

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

EDITAR:
Isso é um monte de código-lo atualizado, mas vou obrigar sobre os pontos que você destacou.Evite chamar createIndex fora da classe model.Este é um método protegido em Qt;Python não impor private/protected variáveis ou métodos, mas quando estiver usando uma biblioteca de outro idioma que não, eu tento respeitar a intenção da organização das classes, e o acesso a eles.

O objetivo do modelo é fornecer uma interface para os seus dados.Você deve acessá-lo usando o index, data, parent etc.funções públicas do modelo.Para obter o principal de um determinado índice, use-o índice (ou do modelo) parent a função, que também irá retornar um QModelIndex.Desta forma, você não tem que ir através de (ou, de fato, saber sobre) a estrutura interna dos dados.Isso é o que eu fiz na deleteLevel o método.

A partir do qt docs:

Para garantir que a representação dos dados é mantido separado da maneira como ele é acessado, o conceito de um modelo de índice é introduzido.Cada pedaço de informação que pode ser obtida através de um modelo é representado por um modelo de índice de...apenas o modelo precisa de saber como obter os dados, e o tipo de dados gerenciado pelo modelo podem ser definidos de forma justa em geral.

Além disso, você pode usar recursão para simplificar o método de impressão.

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

Eu tinha esse separada da classe

def dictify(node):
    kids = {}
    for child in node.children:
        kids.update(dictify(child)) 
    return {str(node.name): kids}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top