代码之家  ›  专栏  ›  技术社区  ›  mins

为什么qtreeview.scrollto()最初不工作?

  •  2
  • mins  · 技术社区  · 6 年前

    下面的代码只显示计算机驱动器的树视图。每次选择一个新的文件/文件夹时,视图都会滚动以使新的选择可见。

    问题 :当这项工作时,启动应用程序后的初始选择不会触发滚动。为什么?

    问题 :如果说明:

    self.my_view.scrollTo(index, QAbstractItemView.EnsureVisible)
    self.my_view.resizeColumnToContents(0)
    

    倒置:

    self.my_view.resizeColumnToContents(0)
    self.my_view.scrollTo(index, QAbstractItemView.EnsureVisible)
    

    第一列的大小也不会在初始显示时进行调整,只会在之后进行调整。为什么?

    import sys
    from PyQt5.QtCore import Qt, QModelIndex, QDir
    from PyQt5.QtWidgets import QApplication, QTreeView, QMainWindow, QFileSystemModel, QAbstractItemView
    
    
    class MyWindow(QMainWindow):
        def __init__(self):
            super().__init__()
    
            # Instance variables
            self.my_view = QTreeView()
            self.my_model = QFileSystemModel()
    
            # Init FS model to show all computer drives
            model_root_path = str(self.my_model.myComputer())
            self.my_model.setRootPath(model_root_path)
    
            # Init tree view
            self.my_view.setModel(self.my_model)
            self.my_view.setRootIndex(self.my_model.index(model_root_path))
            self.my_view.setSelectionMode(QAbstractItemView.SingleSelection)
            self.my_view.setSelectionBehavior(QAbstractItemView.SelectRows)
    
            # Connect selection change events to custom slot
            select_model = self.my_view.selectionModel()
            select_model.currentRowChanged.connect(self.current_row_changed)
    
            # Main window
            self.setCentralWidget(self.my_view)
            self.setGeometry(200, 200, 800, 600)
    
            # Select initial row on view
            focus_path = QDir.currentPath()
            focus_index = self.my_model.index(focus_path)
            self.my_view.setCurrentIndex(focus_index)
    
        def current_row_changed(self):
            """Current row of the model has changed"""
    
            # Scroll view to new row
            index = self.my_view.selectionModel().currentIndex()
            self.my_view.scrollTo(index, QAbstractItemView.EnsureVisible)
            self.my_view.resizeColumnToContents(0)
    
            # Show path of current row in window title
            absolute_path = self.my_model.filePath(index)
            self.setWindowTitle(absolute_path)
    
    
    def main():
        a = QApplication(sys.argv)
        mw = MyWindow()
        mw.show()
        sys.exit(a.exec_())
    
    if __name__ == '__main__':
        main()
    

    `


    编辑 :在使用了@ekhometo提供的良好解决方案后,我上面的示例代码工作正常。然而,另一段代码仍然没有:

    import os
    import sys
    
    from PyQt5.QtCore import pyqtSignal, QTimer, QDir, Qt
    from PyQt5.QtWidgets import QMainWindow, QGridLayout, QWidget, QTreeView, QAbstractItemView, QFileSystemModel, \
        QApplication
    
    
    class AppWindow(QMainWindow):
    
        default_folder_path = "."
    
        def __init__(self):
            super().__init__()
            self.folder_view = FolderTreeView()
            self.folder_view.folder_has_changed.connect(self.folder_changed)
            self.build_ui()
            self.show()
    
            # Select initial folder
            self.select_initial_folder()
    
        def build_ui(self):
            main_widget = QWidget()
            layout = QGridLayout(main_widget)
            layout.addWidget(self.folder_view)
            self.setCentralWidget(main_widget)
            self.setGeometry(200, 100, 800, 600)
    
        def select_initial_folder(self):
            folder_index = self.folder_view.get_index(AppWindow.default_folder_path)
            if folder_index.isValid():
                self.folder_view.select_folder(folder_index)
    
        def folder_changed(self, folder_path):
            if not os.path.isdir(folder_path):
                print("Non existing folder:", folder_path)
                return
    
    
    class FolderTreeView(QTreeView):
        folder_has_changed = pyqtSignal(str)
    
        def __init__(self):
            super().__init__()
            self.folder_tree_model = FolderTreeModel()
            self.setModel(self.folder_tree_model)
            self.setSelectionMode(QAbstractItemView.SingleSelection)
            self.setSelectionBehavior(QAbstractItemView.SelectRows)
    
        def select_folder(self, folder_index):
            self.setCurrentIndex(folder_index)
    
        def currentChanged(self, current, previous):
            super(FolderTreeView, self).currentChanged(current, previous)
    
            # Scroll the view to current item and resize folder name column
            QTimer.singleShot(50, lambda: self.delayed_scroll(current))
    
            # Emit signal for other uses
            self.folder_has_changed.emit(self.folder_tree_model.filePath(current))
    
        def delayed_scroll(self, index):
            self.scrollTo(index, QAbstractItemView.EnsureVisible)
            self.resizeColumnToContents(0)
    
        def get_index(self, folder_path):
            return self.folder_tree_model.index(folder_path)
    
    
    class FolderTreeModel(QFileSystemModel):
        def __init__(self):
            super().__init__()
            self.setFilter(QDir.AllDirs | QDir.NoDotAndDotDot)
            self.setRootPath("")
    
    
    def main():
        app = QApplication(sys.argv)
        window = AppWindow()
        sys.exit(app.exec_())
    
    if __name__ == '__main__':
        main()
    
    1 回复  |  直到 6 年前
        1
  •  2
  •   ekhumoro    6 年前

    默认情况下,如果模型将其当前索引初始化到当前目录,则可能会导致第一个问题。这意味着如果你设置它 再一次 对于相同的索引,不会发出行更改信号(因为没有更改)。这可以通过直接调用行更改处理程序来解决:

    class MyWindow(QMainWindow):
        def __init__(self):
            super().__init__()
            ...
            focus_path = QDir.currentPath()
            focus_index = self.my_model.index(focus_path)
            self.my_view.setCurrentIndex(focus_index)
            self.current_row_changed()
    
        def current_row_changed(self):
            index = self.my_view.currentIndex()
            self.my_view.scrollTo(index, QAbstractItemView.EnsureVisible)
            self.my_view.resizeColumnToContents(0)
            ...
    

    关于第二个问题:当你打电话 scrollTo ,它可能需要扩展多个目录才能选择所需的索引。这显然会更改第一列的宽度,因此应始终调用 resizeColumnToContents 之后 为了得到正确的宽度。

    更新 :

    我认为还有一个问题是由时间问题引起的。这个 QFileSystemModel 必须在某种程度上异步工作,因为它必须从操作系统请求资源,然后等待响应。另外,在得到响应之前,它不能提前确切知道它将接收多少数据,因为文件系统可能在等待时被更新了。可能,响应可能包括来自包含数千个文件的大型目录的数据。因此,为了保持图形用户界面的响应性,数据被成批处理,其大小足以填充当前视图。如果设置了当前索引 在窗口显示和所有小部件完全布局之前 ,不能保证视图能够正确调整其列的大小。

    这可以通过一个具有很小延迟的一次性计时器显式地重新调用行更改处理程序来解决。这将允许视图正确地重新计算列宽:

        ...    
        focus_path = QDir.currentPath()
        focus_index = self.my_model.index(focus_path)
        self.my_view.setCurrentIndex(focus_index)
        QTimer.singleShot(50, self.current_row_changed)
    
    def current_row_changed(self):
        index = self.my_view.currentIndex()
        self.my_view.scrollTo(index, QAbstractItemView.EnsureVisible)
        self.my_view.resizeColumnToContents(0)