【问题标题】:QFileSystemModel not updating when files change文件更改时 QFileSystemModel 不更新
【发布时间】:2018-04-11 04:00:15
【问题描述】:

我遇到了 QFileSystemModel 不显示文件更改的问题。首次创建文件时,它会立即显示出来。但是当文件本身发生变化时,大小和时间戳不会更新。我已经多次尝试强制模型更新但没有真正成功。我取得的最好成绩是完全替换模型。虽然这会导致此错误:

QSortFilterProxyModel: index from wrong model passed to mapToSource

下面的测试代码创建了一个空目录的表格视图。单击左侧按钮会创建一个文件 (foo.txt)。连续单击将数据附加到文件中。我的理解是 QFileSystemModel 不需要刷新,但第二个按钮是我的尝试。

任何关于我做错了什么的帮助将不胜感激!

# Testing with python3.6.3 and pip installed pyqt5 5.9.2 in virtualenv on Ubuntu
import os, sys, tempfile
from PyQt5 import QtCore, QtWidgets


class Widget(QtWidgets.QWidget):
    def __init__(self, parent=None):
        QtWidgets.QWidget.__init__(self, parent)

        layout = QtWidgets.QVBoxLayout()
        self.setLayout(layout)
        self._view = QtWidgets.QTableView()
        layout.addWidget(self._view)

        self._modify_button = QtWidgets.QPushButton('Create')
        layout.addWidget(self._modify_button)
        self._refresh_button = QtWidgets.QPushButton('Refresh')
        layout.addWidget(self._refresh_button)

        self._modify_button.clicked.connect(self._modify)
        self._refresh_button.clicked.connect(self._refresh)

        self._model, self._proxy = None, None
        self.temp_dir = tempfile.TemporaryDirectory(dir=os.path.dirname(os.path.abspath(__file__)))
        self.init_model(self.temp_dir.name)

    def init_model(self, path):
        self._model = QtWidgets.QFileSystemModel()
        self._model.setFilter(QtCore.QDir.AllDirs | QtCore.QDir.AllEntries)

        self._proxy = QtCore.QSortFilterProxyModel(self)
        self._proxy.setSourceModel(self._model)
        self._view.setModel(self._proxy)
        # self._view.setModel(self._model)

        self._model.directoryLoaded.connect(self._loaded)
        self._model.setRootPath(path)

    def _loaded(self):
        path = self._model.rootPath()
        source_index = self._model.index(path)
        index = self._proxy.mapFromSource(source_index)
        self._view.setRootIndex(index)
        # self._view.setRootIndex(source_index)

    def _modify(self):
        """Create or modify foo.txt..model should see and update"""
        self._modify_button.setText('Modify')
        file_name = os.path.join(self.temp_dir.name, 'foo.txt')
        with open(file_name, 'a') as txt_file:
            print('foo', file=txt_file)

    # def _refresh(self):
    #     # This only seems to work once..and its a flawed approach since it requires permission to write
    #     temp = tempfile.NamedTemporaryFile(dir=self.temp_dir.name)

    # def _refresh(self):
    #     self._model.beginResetModel()
    #     self._model.endResetModel()

    # def _refresh(self):
    #     self._proxy.setFilterRegExp('foo')
    #     self._proxy.setFilterRegExp(None)
    #     self._proxy.invalidate()
    #     self._proxy.invalidateFilter()
    #     self._proxy.reset()
    #
    #     root_index = self._model.index(self._model.rootPath())
    #     rows = self._model.rowCount(root_index)
    #     proxy_root_index = self._proxy.mapFromSource(root_index)
    #     topLeft = self._proxy.index(0, 0, proxy_root_index)
    #     bottomRight = self._proxy.index(rows - 1, self._model.columnCount(proxy_root_index) - 1, proxy_root_index)
    #     # self._proxy.dataChanged.emit(topLeft, bottomRight)
    #     self._model.dataChanged.emit(topLeft, bottomRight)

    # def _refresh(self):
    #     # This only seems to work once
    #     self._model.setRootPath('')
    #     self._model.setRootPath(self.temp_dir.name)

    def _refresh(self):
        # This seems heavy handed..but seems to work
        # ..though generates "QSortFilterProxyModel: index from wrong model passed to mapToSource" spam in console
        self.init_model(self.temp_dir.name)


if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    widget = Widget()
    widget.show()
    sys.exit(app.exec_())

【问题讨论】:

  • 您可以尝试直接在您的文件上使用QFileSystemWatcher。但是如果你会看很多文件,它可能会降低性能。

标签: python qt pyqt updates qfilesystemmodel


【解决方案1】:

更新

从 Qt-5.9.4 开始,QT_FILESYSTEMMODEL_WATCH_FILES 环境变量可用于打开每个文件的监视(请参阅QTBUG-46684)。在模型开始缓存有关文件的信息之前,需要将其设置为 once 为非空值。但请注意,这将为遇到的每个文件添加一个文件监视程序,因此这可能使其成为expensive solution on some systems

原始答案留在下面作为问题的解释。


这个问题是由一个长期存在的 Qt 错误引起的:QTBUG-2276。不幸的是,目前看来,它不太可能很快得到修复。如错误报告 cmets 所示,问题的核心似乎是这样的:

这是一个操作系统限制。对文件的更改并不意味着目录 已修改。

唯一真正的解决方法是将QFileSystemWatcher 附加到每个文件,这显然可能是prohibitively expensive(无论如何,在某些平台上)。

除了这个问题,QFileSystemModel 类目前不提供用于强制刷新的 API,而且,正如您所发现的,似乎没有任何可靠的解决方法。 SO和其他地方提供的大多数“解决方案”都提出了一些变体:

root = fsmodel.rootPath()
fsmodel.setRootPath('')
fsmodel.setRootPath(root)

但如您所知,这似乎只工作一次 - 可能是由于当前实现文件信息缓存的方式存在一些怪癖。

目前看来,强制更新的唯一方法是替换整个模型。可以通过重构 init_model 方法来防止当前实现产生的错误消息,如下所示:

def init_model(self, path):
    if self._proxy is None:
        self._proxy = QtCore.QSortFilterProxyModel(self)
    else:
        # remove the current source model
        self._proxy.setSourceModel(None)
    self._model = QtWidgets.QFileSystemModel()
    self._model.setFilter(QtCore.QDir.AllDirs | QtCore.QDir.AllEntries)
    self._proxy.setSourceModel(self._model)
    self._view.setModel(self._proxy)
    self._model.directoryLoaded.connect(self._loaded)
    self._model.setRootPath(path)

这是一个非常不令人满意的情况,但目前似乎没有任何明显的解决方法。

【讨论】:

  • 非常感谢您的解释、问题链接以及我在尝试重新初始化模型时收到的警告的修复!
【解决方案2】:

从Qt v5.9.4开始可以设置环境变量QT_FILESYSTEMMODEL_WATCH_FILES,更多相关内容可以阅读changelog

[QTBUG-46684] 现在可以通过 设置环境变量 QT_FILESYSTEMMODEL_WATCH_FILES, 允许跟踪例如文件大小的变化。

几件事:

  • 暂时需要设置before initializing the model,之后设置到其他文件夹没有问题。
  • 但请注意,此功能是以潜在的重负载为代价的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-01-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多