首页 > 解决方案 > QTreeView/QFileSystemModel - 比较文件和显示颜色

问题描述

我有 2 个 QTreeViews 都附有 QFileSystemModel。一个是本地目录,一个指向服务器。

我希望根据文件与相反位置的关系来更改文件上文本的颜色。例如: 如果服务器和本地具有相同的修改时间 - 设置为绿色 如果服务器是最新的 - 将本地设置为红色,服务器设置为橙色。等等等等。

我有多个这样的工作示例,但我一直遇到相同的障碍,缓慢的 UI 响应。我在两个 QFileSystemModels 中都使用 data() 函数来实现这一点,我尝试在 data() 之外线程化操作以返回颜色,但它只会让我的所有文件变成灯光秀。基于我移动鼠标,文件将随机变为绿色/红色/橙色。

我目前将日期修改列作为一种解决方法可见,但它不能帮助用户知道他们是否已经在本地拥有来自服务器的文件。

有没有人在没有 UI 突突的情况下有这样的工作?或者甚至找到了一种从线程返回结果并使其适用于正确条目的方法?

我很欣赏这可能看起来有点乱,但这是我初始化根的地方(其中一些全局变量在其他地方使用)

def initRoots(self, loc):
    '''
    This function sets the starting directories for both server and local
    Does not handle anything except top level
    '''
    global localMask
    global serverMask
    global serverRoot
    global userRoot
    global GserverModel
    global GlocalModel

    if loc == "Server":
        self.serverRoot = self.cb_serverWorkspace.currentText()
        serverRoot = self.serverRoot
        self.serverModel = ServerFileSystemModel()
        GserverModel = self.serverModel
        self.tv_serverFiles.setModel(self.serverModel)
        #self.serverModel.setRootPath(QtCore.QDir.rootPath())
        self.serverModel.setRootPath("")
        self.tv_serverFiles.setRootIndex(self.serverModel.index(self.serverRoot))
        self.tv_serverFiles.setUniformRowHeights(True)
        self.tv_serverFiles.setExpandsOnDoubleClick(True)
        self.tv_serverFiles.hideColumn(1)
        self.tv_serverFiles.hideColumn(2)
        self.tv_serverFiles.setColumnWidth(0,400)

    if loc == "Local":
        self.userRoot = self.cb_localWorkspace.currentText()
        userRoot = self.userRoot
        self.localModel = LocalFileSystemModel()
        GlocalModel = self.localModel
        self.tv_localFiles.setModel(self.localModel)
        #self.localModel.setRootPath(QtCore.QDir.rootPath())
        self.localModel.setRootPath("")
        self.tv_localFiles.setRootIndex(self.localModel.index(self.userRoot))
        self.tv_serverFiles.setUniformRowHeights(True)
        self.tv_localFiles.setExpandsOnDoubleClick(True)
        self.tv_localFiles.hideColumn(1)
        self.tv_localFiles.hideColumn(2)
        self.tv_localFiles.setColumnWidth(0,400)

然后我的两个 QFileSystemModel 差不多就是这个(这是我的非线程示例)

class ServerFileSystemModel(QtWidgets.QFileSystemModel):
    def __init__(self, *args, **kwargs):
        super(ServerFileSystemModel, self).__init__(*args, **kwargs)
        

    def data(self, index, role=QtCore.Qt.DisplayRole):
        
        try:
            global GlocalModel
            global GserverModel
               
                
            serverPath = self.filePath(index)

                
            localMask = userRoot + "/"
            serverMask = serverRoot + "/"
                
            newPath = serverPath.replace(serverMask, localMask)
 
            serverIndex = GserverModel.index(serverPath)
            localIndex = GlocalModel.index(newPath)
                
            if role == QtCore.Qt.TextColorRole and serverPath.endswith(".fbx") and os.path.exists(newPath) and downloading == False:
                  
                  
                if GlocalModel.lastModified(localIndex) == GserverModel.lastModified(serverIndex):
                    return QtGui.QColor("#58cd1c")
                      
                if GlocalModel.lastModified(localIndex) < GserverModel.lastModified(serverIndex):
                    return QtGui.QColor("#ed7011")
                  
                if GlocalModel.lastModified(localIndex) > GserverModel.lastModified(serverIndex):
                    return QtGui.QColor("#ed1111")
              
                       
      
            return super(ServerFileSystemModel, self).data(index, role)
        except:
            return super(ServerFileSystemModel, self).data(index, role)

有其他事情就告诉我。

编辑******

我被要求输入最少的可复制代码,所以我们开始吧

主窗口

class WorkspacerUI(QMainWindow, Ui_MainWindow):
    def __init__(self, parent = None):
        super(WorkspacerUI, self).__init__(parent)
        self.setupUi(self)

        self.userRoot = "D:/OCooke_Workspace"
        self.serverRoot = "D:/OCooke_Server"
        
        self.initRoots("Local")
        self.initRoots("Server")

    
    def initRoots(self, loc):
        '''
        This function sets the starting directories for both server and local
        Does not handle anything except top level
        '''
                
        if loc == "Server":
            self.serverModel = ServerFileSystemModel()
            self.tv_serverFiles.setModel(self.serverModel)
            #self.serverModel.setRootPath(QtCore.QDir.rootPath())
            self.serverModel.setRootPath("")
         self.tv_serverFiles.setRootIndex(self.serverModel.index(self.serverRoot))
            self.tv_serverFiles.setUniformRowHeights(True)
            self.tv_serverFiles.setExpandsOnDoubleClick(True)
            self.tv_serverFiles.hideColumn(1)
            self.tv_serverFiles.hideColumn(2)
            self.tv_serverFiles.setColumnWidth(0,400)

        if loc == "Local":
            self.localModel = LocalFileSystemModel()
            self.tv_localFiles.setModel(self.localModel)
            #self.localModel.setRootPath(QtCore.QDir.rootPath())
            self.localModel.setRootPath("")
            self.tv_localFiles.setRootIndex(self.localModel.index(self.userRoot))
            self.tv_serverFiles.setUniformRowHeights(True)
            self.tv_localFiles.setExpandsOnDoubleClick(True)
            self.tv_localFiles.hideColumn(1)
            self.tv_localFiles.hideColumn(2)
            self.tv_localFiles.setColumnWidth(0,400)

带有信号的 QRunnable

class WorkerSignals(QObject):

    finished = Signal()
    result = Signal(object)
    progress = Signal(int)


            
            
class ColourWorker(QRunnable):
    '''
    Worker thread

    Inherits from QRunnable to handler worker thread setup, signals and wrap-up.

    :param callback: The function callback to run on this worker thread. Supplied args and
                     kwargs will be passed through to the runner.
    :type callback: function
    :param args: Arguments to pass to the callback function
    :param kwargs: Keywords to pass to the callback function

    '''

    def __init__(self, fn, *args, **kwargs):
        super(ColourWorker, self).__init__()

        # Store constructor arguments (re-used for processing)
        self.fn = fn
        self.args = args
        self.kwargs = kwargs
        self.signals = WorkerSignals()

        # Add the callback to our kwargs
        self.kwargs['progress_callback'] = self.signals.progress
        self.kwargs['indexy']
        
        
        
    @Slot()
    def run(self):
        '''
        Initialise the runner function with passed args, kwargs.
        '''

        # Retrieve args/kwargs here; and fire processing using them
        try:
            result = self.fn(*self.args, **self.kwargs)
        except:
            traceback.print_exc()
            exctype, value = sys.exc_info()[:2]
            self.signals.error.emit((exctype, value, traceback.format_exc()))
        else:
            self.signals.result.emit(result)  # Return the result of the processing
        finally:
            self.signals.finished.emit()  # Done

最后是 2 个 QSystemFileModels

class ServerFileSystemModel(QFileSystemModel):
    def __init__(self, *args, **kwargs):
        super(ServerFileSystemModel, self).__init__(*args, **kwargs)

        self.threadpool = QThreadPool()
        self.colour = None

    def data(self, index, role=Qt.DisplayRole):

        if role == Qt.TextColorRole:
            worker = ColourWorker(self.execute_this_fn, indexy = index) # Any other args, kwargs are passed to the run function
            worker.signals.result.connect(self.print_output)
            worker.signals.finished.connect(self.thread_complete)
            # Execute
            self.threadpool.start(worker)


            return self.colour
        return super(ServerFileSystemModel, self).data(index, role)
        
#  

 
    def execute_this_fn(self, progress_callback, indexy):

        serverPath = self.filePath(indexy)
              
        localMask = "D:/OCooke_Workspace/"
        serverMask = "D:/OCooke_Server/"
              
        #create an expected path to the local using the server address
        # D:/OCooke_Server/2020/file1 turns into  D:/OCooke_Workspace/2020/file1
        newPath = serverPath.replace(serverMask, localMask)

        #Check the stat time between the two locations
        if os.stat(newPath).st_mtime == os.stat(serverPath).st_mtime:
            return QColor("#58cd1c")
        if os.stat(newPath).st_mtime < os.stat(serverPath).st_mtime:
            return QColor("#ed7011")
        if os.stat(newPath).st_mtime > os.stat(serverPath).st_mtime:
            return QColor("#ed1111")

        else:
            return None
        
 
    def print_output(self, s):
        #return the value of color
        self.colour = s
        
 
    def thread_complete(self):
        #ive tried passing the color once the thread completes, no joy
        pass



    
    
class LocalFileSystemModel(QFileSystemModel):
    def __init__(self, *args, **kwargs):
        super(LocalFileSystemModel, self).__init__(*args, **kwargs)

    def data(self, index, role=Qt.DisplayRole):


        return super(LocalFileSystemModel, self).data(index, role)

主要的

def main():
    app = QApplication(sys.argv)
    MainWindow = WorkspacerUI()
    MainWindow.show()
    sys.exit(app.exec_())

    
if __name__ == '__main__':
    main()

标签: pythonpysidepyside2

解决方案


推荐阅读