QFileSystemModel在文件更改时不更新 [英] QFileSystemModel not updating when files change

查看:766
本文介绍了QFileSystemModel在文件更改时不更新的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我遇到了QFileSystemModel不能显示文件更改的问题.首次创建文件时,它会立即显示.但是,当文件本身更改时,大小和时间戳不会更新.我已经尝试过多次尝试强制模型更新而没有真正的成功.我所取得的最好成绩是完全替换了该模型.尽管这会导致此错误:

I'm having trouble with QFileSystemModel not showing changes to files. When a file is first created it immediately shows up. But when the file itself changes, the size and timestamp don't update. I've made multiple attempts at trying to force the model to update with no real success. The best I've achieved is to completely replace the model. Although that results in this error:

QSortFilterProxyModel: index from wrong model passed to mapToSource

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

The test code below creates a table view of an empty directory. The left button creates a file (foo.txt) when clicked. Successive clicks append data to the file. It was my understanding that the QFileSystemModel didn't need a refresh, but the second button is my attempt at that.

任何对我做错事的帮助将不胜感激!

Any help as to what I'm doing wrong would be greatly appreciated!

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

推荐答案

更新:

从Qt-5.9.4开始,QT_FILESYSTEMMODEL_WATCH_FILES envrionment变量可用于打开按文件观看(请参阅 QTBUG-46684 ).在模型开始缓存有关文件的信息之前,需要将一次设置为非空值.但是请注意,这会将文件监视程序添加到 遇到的每个文件 ,因此这可能使其成为

As of Qt-5.9.4, the QT_FILESYSTEMMODEL_WATCH_FILES envrionment variable can be used to switch on per-file watching (see QTBUG-46684). This needs to be set once to a non-empty value before the model starts caching information about files. But note that this will add a file-watcher to every file that is encountered, so this may make it an expensive solution on some systems.

原始答案留在下面作为对该问题的解释.

The original answer is left below as an explanation of the problem.

此问题是由长期存在的Qt错误引起的: QTBUG-2276 .不幸的是,目前看来它不可能在短期内得到修复.如错误报告注释所示,问题的核心似乎是这样:

This problem is caused by a long-standing Qt bug: QTBUG-2276. Unfortunately, at present, it does not look likely that it will be fixed any time soon. As indicated in the bug report comments, the core of the issue seems to be this:

这是操作系统的限制.更改文件并不意味着目录 被修改.

It's an OS limitation. A change to a file does not mean the directory is modified.

唯一真正的解决方法是将QFileSystemWatcher附加到每个单个文件,这显然可能是

The only real work-around for this would be to attach a QFileSystemWatcher to every single file, which could obviously be prohibitively expensive (on some platforms, anyway).

除此问题外, QFileSystemModel 类当前不存在提供强制刷新的API,并且您已经发现,似乎没有任何可靠的解决方法. SO和其他地方提供的大多数解决方案"都暗示了这种变化:

In addition to this problem, the QFileSystemModel class doesn't currently provide an API for forcing a refresh, and, as you have discovered, there does not seem to be any reliable work-around for that. Most "solutions" offered on SO and elsewhere, suggest some variant of this:

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

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

But as you know, this seems to work only once - probably due to some quirk in the way file-info caching is currently implemented.

目前看来,强制进行更新的唯一方法是替换整个模型.可以通过重构如下的init_model方法来防止当前执行此操作所产生的错误消息:

At present it appears the only way to force an update is to replace the entire model. The error messages produced by your current implementation of this can be prevented by refactoring your init_model method like this:

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)

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

This is a very unsatisfactory situation, but there just doesn't seem to be any obvious way around it at the moment.

这篇关于QFileSystemModel在文件更改时不更新的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆