python - 为什么 QTreeView.scrollTo() 最初不工作

标签 python windows scroll pyqt5 qtreeview

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

问题1:虽然这可行,但应用程序启动后的初始选择不会触发滚动。为什么?

问题2:如果说明:

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

`


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

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

最佳答案

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

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 必须在某种程度上异步工作,因为它必须从操作系统请求资源,然后等待响应。此外,在收到响应之前,它无法提前准确知道将要接收多少数据,因为文件系统可能在等待期间已更新。潜在地,响应可能包括来自包含数千个文件的巨大目录的数据。因此,为了保持 GUI 响应,数据被分批处理,其大小足以填充当前 View 。如果当前索引在窗口显示且其所有小部件完全布局之前设置,则无法保证 View 能够正确调整其列的大小。

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

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

关于python - 为什么 QTreeView.scrollTo() 最初不工作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54251338/

相关文章:

css - 如何在 Firefox 上使用覆盖滚动?

python - 在 Keras 中构建简单的前馈神经网络时遇到关键错误

python - Wget 和 Python 下载管理器?

windows - 使用 UIElement 创建 Cordova UWP 插件

windows - 隐藏主窗体,但不从任务栏隐藏

javascript - 根据用户在页面中的位置显示或隐藏 HTML

python - 使用 awk 实用程序实现的场景

Python:将大整数写入文件的最快方法

windows - 如何将 Maven 添加到 Path 变量?

javascript - 使用 jQuery 将 <body> 位置设置为固定时避免恢复到页面顶部