2013-06-08 10 views
7

Nel tentativo di creare una vista di directory controllabile, ho scritto il seguente codice. ma nel CheckableDirModel ogni volta che si controlla una cartella, deve attraversare tutte le sottocartelle per controllarle e questo è molto lento. Speravo che qualcuno potesse aiutarmi a risolvere questo problema.Vista directory controllabile PyQt4

Ecco come appare ora. ma è lento soprattutto se si fa clic su una grande cartella.

enter image description here

Il codice è eseguibile ...

from PyQt4 import QtGui, QtCore 


class CheckableDirModel(QtGui.QDirModel): 
    def __init__(self, parent=None): 
     QtGui.QDirModel.__init__(self, None) 
     self.checks = {} 

    def data(self, index, role=QtCore.Qt.DisplayRole): 
     if role != QtCore.Qt.CheckStateRole: 
      return QtGui.QDirModel.data(self, index, role) 
     else: 
      if index.column() == 0: 
       return self.checkState(index) 

    def flags(self, index): 
     return QtGui.QDirModel.flags(self, index) | QtCore.Qt.ItemIsUserCheckable 

    def checkState(self, index): 
     if index in self.checks: 
      return self.checks[index] 
     else: 
      return QtCore.Qt.Unchecked 

    def setData(self, index, value, role): 
     if (role == QtCore.Qt.CheckStateRole and index.column() == 0): 
      self.checks[index] = value 
      for i in range(self.rowCount(index)): 
       self.setData(index.child(i,0),value,role) 
      return True 

     return QtGui.QDirModel.setData(self, index, value, role) 

    def exportChecked(self, acceptedSuffix=['jpg', 'png', 'bmp']): 
     selection=[] 
     for c in self.checks.keys(): 
      if self.checks[c]==QtCore.Qt.Checked and self.fileInfo(c).completeSuffix().toLower() in acceptedSuffix: 
       try: 

        selection.append(self.filePath(c).toUtf8()) 
       except: 
        pass 
     return selection 

if __name__ == '__main__': 
    import sys 

    app = QtGui.QApplication(sys.argv) 

    model = QtGui.QDirModel() 
    tree = QtGui.QTreeView() 
    tree.setModel(CheckableDirModel()) 

    tree.setAnimated(False) 
    tree.setIndentation(20) 
    tree.setSortingEnabled(True) 

    tree.setWindowTitle("Dir View") 
    tree.resize(640, 480) 
    tree.show() 

    sys.exit(app.exec_()) 

risposta

8

Non è possibile memorizzare lo stato casella di controllo per ogni file separatamente. Ce ne possono essere troppi. Vi suggerisco di fare quanto segue:

Si mantiene un elenco di valori di checkbox per gli indici che l'utente ha effettivamente cliccato. Quando l'utente fa clic su qualcosa, aggiungi una voce all'elenco (o la aggiorna se è già presente), quindi rimuove tutte le voci per gli indici figli presenti nell'elenco. È necessario emettere un segnale sui dati dell'indice padre e l'indice di tutti i bambini è stato modificato.

Quando viene richiesto un valore di casella di controllo (chiamando data() del modello), si cerca l'indice richiesto nell'elenco e si restituisce il suo valore. Se l'indice non è presente nell'elenco, si cerca l'indice padre più vicino e si restituisce il suo valore.

Nota: a parte l'esecuzione lenta, c'è un altro problema nel codice. Quando l'albero dei file ha troppi livelli, si verifica l'eccezione "massima profondità di ricorsione superata". Quando si implementa il mio suggerimento, non usare la ricorsione in questo modo. La profondità dell'albero dei file è quasi illimitata.

Ecco l'attuazione:

from collections import deque 

def are_parent_and_child(parent, child): 
    while child.isValid(): 
     if child == parent: 
      return True 
     child = child.parent() 
    return False 


class CheckableDirModel(QtGui.QDirModel): 
    def __init__(self, parent=None): 
     QtGui.QDirModel.__init__(self, None) 
     self.checks = {} 

    def data(self, index, role=QtCore.Qt.DisplayRole): 
     if role == QtCore.Qt.CheckStateRole and index.column() == 0: 
      return self.checkState(index) 
     return QtGui.QDirModel.data(self, index, role) 

    def flags(self, index): 
     return QtGui.QDirModel.flags(self, index) | QtCore.Qt.ItemIsUserCheckable 

    def checkState(self, index): 
     while index.isValid(): 
      if index in self.checks: 
       return self.checks[index] 
      index = index.parent() 
     return QtCore.Qt.Unchecked 

    def setData(self, index, value, role): 
     if role == QtCore.Qt.CheckStateRole and index.column() == 0: 
      self.layoutAboutToBeChanged.emit() 
      for i, v in self.checks.items(): 
       if are_parent_and_child(index, i): 
        self.checks.pop(i) 
      self.checks[index] = value 
      self.layoutChanged.emit() 
      return True 

     return QtGui.QDirModel.setData(self, index, value, role) 

    def exportChecked(self, acceptedSuffix=['jpg', 'png', 'bmp']): 
     selection=set() 
     for index in self.checks.keys(): 
      if self.checks[index] == QtCore.Qt.Checked: 
       for path, dirs, files in os.walk(unicode(self.filePath(index))): 
        for filename in files: 
         if QtCore.QFileInfo(filename).completeSuffix().toLower() in acceptedSuffix: 
          if self.checkState(self.index(os.path.join(path, filename))) == QtCore.Qt.Checked: 
           try: 
            selection.add(os.path.join(path, filename)) 
           except: 
            pass 
    return selection 

non ho trovato un modo per utilizzare dataChanged segnale per comunicare parere che i dati di tutti gli indici bambino è stato cambiato. Non sappiamo quali indici sono attualmente mostrati e non possiamo notificare su ogni indice secondario perché può essere lento. Quindi ho usato layoutAboutToBeChanged e layoutChanged per forzare la visualizzazione per aggiornare tutti i dati. Sembra che questo metodo sia abbastanza veloce.

exportChecked è un po 'complicato. Non è ottimizzato, a volte un indice viene elaborato molte volte. Ho usato set() per filtrare i duplicati. Forse può essere ottimizzato in qualche modo se funzionerà troppo lentamente. Tuttavia, se l'utente ha controllato una directory di grandi dimensioni con molti file e sottodirectory, qualsiasi implementazione di questa funzione sarà lenta. Quindi non ha senso l'ottimizzazione, basta provare a non chiamare questa funzione spesso.

+0

Puoi codificare ciò che hai suggerito ... Aumenterò la taglia – Kiarash

+0

Fatto. Sembra che funzioni bene. –

+0

wow. questo è veramente veloce! molte grazie. solo l'ultima cosa, come otterresti l'elenco degli elementi selezionati? simile all'esportazioneControllato nel codice originale. – Kiarash