【问题标题】:How to update file info in a QFileSystemModel?如何更新 QFileSystemModel 中的文件信息?
【发布时间】:2018-04-08 13:49:27
【问题描述】:

首先,这个问题与另一个QFileSystemModel not updating when files change 的问题相似,主要区别在于我不想在每次更新我的一个文件时都替换整个模型。

在现实世界的例子中,我的应用程序将打开几个文件,所以我基本上只是想了解如何更新一个特定 QFileSystemModel 项目的信息(大小、修改日期),下面你有一点 mcve 可以玩与,正如您在该代码中看到的那样,我尝试使用 setData 失败:

import sys
import os

from PyQt5.Qt import *  # noqa


class DirectoryTreeWidget(QTreeView):

    def __init__(self, path=QDir.currentPath(), *args, **kwargs):
        super().__init__(*args, **kwargs)

        self.init_model(path)
        self.expandsOnDoubleClick = False
        self.header().setSectionResizeMode(0, QHeaderView.ResizeToContents)
        self.setAutoScroll(True)

    def init_model(self, path):
        self.extensions = ["*.*"]
        self.path = path
        model = QFileSystemModel(self)
        model.setRootPath(QDir.rootPath())
        model.setReadOnly(False)
        model.setFilter(QDir.AllDirs | QDir.NoDot | QDir.AllEntries)
        self.setModel(model)
        self.set_path(path)

    def set_path(self, path):
        self.path = path
        model = self.model()
        index = model.index(str(self.path))

        if os.path.isfile(path):
            self.setRootIndex(model.index(
                os.path.dirname(str(self.path))))
            self.scrollTo(index)
            self.setCurrentIndex(index)
        else:
            self.setRootIndex(index)


class Foo(QWidget):

    def __init__(self, path):
        super().__init__()

        self.path = path

        self.tree_view = DirectoryTreeWidget(path=".")
        self.tree_view.show()
        bt = QPushButton(f"Update {path}")
        bt.pressed.connect(self.update_file)

        layout = QVBoxLayout()
        layout.addWidget(self.tree_view)
        layout.addWidget(bt)

        self.setLayout(layout)

        # New file will automatically refresh QFileSystemModel
        self.create_file()

    def create_file(self):
        with open(self.path, "w") as f:
            data = "This new file contains xx bytes"
            f.write(data.replace("xx", str(len(data))))

    def update_file(self):
        model = self.tree_view.model()

        # Updating a file won't refresh QFileSystemModel, the question is,
        # how can we update that particular item to be refreshed?
        data = "The file updated is much larger, it contains xx bytes"
        with open(self.path, "w") as f:
            f.write(data.replace("xx", str(len(data))))

        # file_info = self.obj.model.fileInfo(index)
        # file_info.refresh()
        index = model.index(self.path)
        model.setData(index, model.data(index))
        QMessageBox.about(None, "Info", f"{self.path} updated, new size is {len(data)}")


def main():
    app = QApplication(sys.argv)
    foo = Foo("foo.txt")
    foo.setMinimumSize(640, 480)
    foo.show()
    sys.exit(app.exec_())


if __name__ == "__main__":
    main()

所以问题是,我怎样才能实现update_file 更新该特定文件foo.txt 的信息?

我们的目标是只更新该文件而不替换整个模型,如 here 所示,一旦模型项目更新,该项目在视图中根本不会排序/移动会很好。

【问题讨论】:

  • 这与链接的问题确实没有什么不同。如果有一种方法可以强制更新一个文件,那么可以对所有文件执行相同的操作。一个非常粗糙和错误的黑客将是对文件进行双重重命名以强制更新。但是这样做时没有办法避免竞争条件,所以我不推荐它。 (PS:调用setData会尝试重命名文件,但如果名称没有改变,则没有影响)。

标签: python pyqt pyqt5 qfilesystemmodel qfilesystemwatcher


【解决方案1】:

Qt v5.9.4 引入了环境变量QT_FILESYSTEMMODEL_WATCH_FILES 以寻址QTBUG-46684,您可以在changelog 中阅读更多相关信息:

QTBUG-46684 现在可以通过以下方式启用每个文件的监视 设置环境变量 QT_FILESYSTEMMODEL_WATCH_FILES, 允许跟踪例如文件大小的变化。

因此,为了使示例正常运行,您只需将 envar 设置为一次非空值,即:

import sys
import os

from PyQt5.Qt import *  # noqa


class DirectoryTreeWidget(QTreeView):

    def __init__(self, path=QDir.currentPath(), *args, **kwargs):
        super().__init__(*args, **kwargs)

        self.init_model(path)
        self.expandsOnDoubleClick = False
        self.header().setSectionResizeMode(0, QHeaderView.ResizeToContents)
        self.setAutoScroll(True)

    def init_model(self, path):
        os.environ["QT_FILESYSTEMMODEL_WATCH_FILES"] = '1'

        self.extensions = ["*.*"]
        self.path = path
        model = QFileSystemModel(self)
        model.setRootPath(QDir.rootPath())
        model.setReadOnly(False)
        model.setFilter(QDir.AllDirs | QDir.NoDot | QDir.AllEntries)
        self.setModel(model)
        self.set_path(path)

    def set_path(self, path):
        self.path = path
        model = self.model()
        index = model.index(str(self.path))

        if os.path.isfile(path):
            self.setRootIndex(model.index(
                os.path.dirname(str(self.path))))
            self.scrollTo(index)
            self.setCurrentIndex(index)
        else:
            self.setRootIndex(index)


class Foo(QWidget):

    def __init__(self, path):
        super().__init__()

        self.path = path

        self.tree_view = DirectoryTreeWidget(path=".")
        self.tree_view.show()
        bt = QPushButton(f"Update {path}")
        bt.pressed.connect(self.update_file)

        layout = QVBoxLayout()
        layout.addWidget(self.tree_view)
        layout.addWidget(bt)

        self.setLayout(layout)
        self.create_file()

    def create_file(self):
        with open(self.path, "w") as f:
            data = "This new file contains xx bytes"
            f.write(data.replace("xx", str(len(data))))

    def update_file(self):
        model = self.tree_view.model()

        data = "The file updated is much larger, it contains xx bytes"
        with open(self.path, "w") as f:
            f.write(data.replace("xx", str(len(data))))

        index = model.index(self.path)
        model.setData(index, model.data(index))
        QMessageBox.about(None, "Info", f"{self.path} updated, new size is {len(data)}")


def main():
    app = QApplication(sys.argv)
    foo = Foo("foo.txt")
    foo.setMinimumSize(640, 480)
    foo.show()
    sys.exit(app.exec_())


if __name__ == "__main__":
    main()

几个cmets:

  • 你需要设置这个 env.var before initializing the model
  • 不过,此功能的代价是潜在的重载。如果此 env.var 处于打开状态,将监视缓存的文件。

【讨论】:

  • 这是一个很好的发现,但遗憾的是它没有正确记录。它似乎也是一个相当重量级的解决方案。真正需要的是一个可以显式刷新单个文件和/或目录的缓存的槽/方法。
  • PS:您的答案是错误的假设这可用于指定将监视文件的特定目录。 envar 只是一个开关,它打开每个文件监视 任何缓存的文件,无论它们碰巧在哪个目录中。所以它才有意义将 envar once 设置为非空值,例如os.environ['QT_FILESYSTEMMODEL_WATCH_FILES'] = '1'.
  • @ekhumoro 我完全同意你的看法,对于我的真实案例,它的工作原理是 +/- 好的。即:我有一个由 2widgets+2models 组成的小部件,一个小部件显示文件夹,另一个小部件从选定的目录中获取文件。每次我更改当前目录时,我都会更新该 env.var,因此它会监视该特定目录......到目前为止,它的性能相当不错,但我刚刚测试了几百个文件的目录也是如此。我的意思是,这并不理想,但编写我自己的 QFileSystemModel 被证明非常耗时。我真正不喜欢的是 QFileSystemModel 公开了一个非常小的公共接口:(
  • @ekhumoro 我之前的评论与您的​​第一条评论有关。关于你的第二条评论,你确定吗?感谢指出,我稍后会在我的机器上测试它,如果是这样我会更新答案
  • 是的,我有 99.9% 的把握。在脚本中将值设置为 '1' 不会影响行为。 qt 源代码目前仅在一个地方使用该 envar,并且它不将该值用作除简单开关之外的任何其他值(即它不将其解释为文件路径)。
猜你喜欢
  • 1970-01-01
  • 2019-04-22
  • 2022-01-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-05-22
  • 2017-05-09
  • 1970-01-01
相关资源
最近更新 更多