2013-06-08 8 views
7

In dem Versuch, eine überprüfbare Verzeichnisansicht zu erstellen, schrieb ich den folgenden Code. aber im CheckableDirModel muss jedes Mal, wenn man einen Ordner überprüft, alle Unterordner durchsucht werden, um sie zu überprüfen, und dies ist sehr langsam. Ich hatte gehofft, dass jemand mir helfen könnte, dieses Problem zu lösen.PyQt4 prüfbare Verzeichnisansicht

So sieht dieser jetzt aus. aber es ist langsam, besonders wenn man auf einen großen Ordner klickt.

enter image description here

Der Code ausführbar ist ...

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

Antwort

8

Sie können nicht speichern Checkbox Zustand für jede Datei einzeln. Es kann zu viele von ihnen geben. Ich empfehle Ihnen, Folgendes zu tun:

Sie behalten eine Liste der Kontrollkästchen Werte für Indizes Benutzer hat tatsächlich geklickt. Wenn ein Benutzer auf etwas klickt, fügen Sie der Liste einen Eintrag hinzu (oder aktualisieren Sie ihn, falls dieser bereits vorhanden ist) und entfernen Sie dann alle Einträge für untergeordnete Indizes, die in der Liste vorhanden sind. Sie müssen ein Signal über die Daten des übergeordneten Index ausgeben, und alle untergeordneten Indizes wurden geändert.

Wenn ein Kontrollkästchenwert angefordert wird (indem Sie data() des Modells aufrufen), suchen Sie in der Liste nach dem angeforderten Index und geben den Wert zurück. Wenn der Index nicht in der Liste vorhanden ist, suchen Sie nach dem nächsten übergeordneten Index und geben seinen Wert zurück.

Beachten Sie, dass neben der langsamen Ausführung ein weiterer Fehler in Ihrem Code auftritt. Wenn die Dateistruktur zu viele Ebenen aufweist, tritt die Ausnahme "maximale Rekursionstiefe überschritten" auf. Verwenden Sie bei der Implementierung meines Vorschlags Rekursion nicht auf diese Weise. Die Tiefe des Dateibaums ist nahezu unbegrenzt. Hier

ist die Umsetzung:

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 

ich nicht einen Weg finden, haben dataChanged Signal zu verwenden Ansicht zu benachrichtigen, dass Daten aller untergeordneten Indizes geändert wurde. Wir wissen nicht, welche Indizes derzeit angezeigt werden, und wir können nicht über jeden untergeordneten Index informieren, da dieser langsam sein kann. Also habe ich layoutAboutToBeChanged und layoutChanged verwendet, um die Ansicht zu erzwingen, um alle Daten zu aktualisieren. Es scheint, dass diese Methode schnell genug ist.

exportChecked ist ein bisschen kompliziert. Es ist nicht optimiert, manchmal wird ein Index viele Male verarbeitet. Ich habe set() verwendet, um Duplikate zu filtern. Vielleicht kann es irgendwie optimiert werden, wenn es zu langsam arbeitet. Wenn Benutzer jedoch ein großes Verzeichnis mit vielen Dateien und Unterverzeichnissen überprüft haben, ist die Implementierung dieser Funktion langsam. Es hat also keinen Sinn in der Optimierung, versuchen Sie einfach, diese Funktion nicht oft aufzurufen.

+0

Können Sie Code, was Sie vorgeschlagen ... Ich werde die Prämie erhöhen – Kiarash

+0

Fertig. Es scheint gut zu funktionieren. –

+0

wow. das ist wirklich schnell! Danke vielmals. nur das letzte, wie würdest du die Checkliste bekommen? ähnlich wie im Originalcode exportiert. – Kiarash