QFileSystemModel ไม่อัปเดตเมื่อไฟล์มีการเปลี่ยนแปลง

ฉันมีปัญหากับ 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_())

person shao.lo    schedule 17.12.2017    source แหล่งที่มา
comment
คุณอาจลองใช้ QFileSystemWatcher กับไฟล์ของคุณโดยตรง แต่อาจทำให้ประสิทธิภาพช้าลงหากคุณรับชมไฟล์จำนวนมาก   -  person Dmitry Sazonov    schedule 26.12.2017


คำตอบ (2)


อัปเดต:

ตั้งแต่ Qt-5.9.4 เป็นต้นไป ตัวแปรสภาพแวดล้อม QT_FILESYSTEMMODEL_WATCH_FILES สามารถใช้เพื่อเปิดการรับชมต่อไฟล์ได้ (ดู QTBUG-46684) สิ่งนี้จะต้องตั้งค่า หนึ่งครั้ง ให้เป็นค่าที่ไม่ว่างเปล่าก่อนที่โมเดลจะเริ่มแคชข้อมูลเกี่ยวกับไฟล์ แต่โปรดทราบว่าการดำเนินการนี้จะเพิ่มตัวเฝ้าดูไฟล์ลงในทุกไฟล์ที่พบ ดังนั้นนี่อาจทำให้เป็น โซลูชันราคาแพงในบางระบบ

คำตอบเดิมอยู่ด้านล่างเพื่อเป็นคำอธิบายปัญหา


ปัญหานี้เกิดจากข้อบกพร่องของ Qt ที่มีมายาวนาน: QTBUG-2276 น่าเสียดายที่ในปัจจุบันดูไม่น่าจะได้รับการแก้ไขในเร็วๆ นี้ ตามที่ระบุในความคิดเห็นเกี่ยวกับรายงานข้อผิดพลาด ประเด็นหลักของปัญหาน่าจะเป็นดังนี้:

มันเป็นข้อจำกัดของระบบปฏิบัติการ การเปลี่ยนแปลงไฟล์ไม่ได้หมายความว่าไดเร็กทอรีถูกแก้ไข

วิธีแก้ปัญหาที่แท้จริงเพียงอย่างเดียวคือการแนบ QFileSystemWatcher เข้ากับ ทุกไฟล์ ซึ่งเห็นได้ชัดว่าเป็น มีราคาแพงอย่างห้าม (ในบางแพลตฟอร์ม)

นอกจากปัญหานี้แล้ว คลาส 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)

นี่เป็นสถานการณ์ที่ไม่น่าพอใจมาก แต่ดูเหมือนจะไม่มีวิธีแก้ไขที่ชัดเจนในขณะนี้

person ekhumoro    schedule 23.12.2017
comment
ขอบคุณมากสำหรับคำอธิบาย ลิงก์ไปยังปัญหา และการแก้ไขคำเตือนที่ฉันได้รับจากความพยายามในการเริ่มต้นโมเดลใหม่! - person shao.lo; 26.12.2017

เนื่องจาก Qt v5.9.4 คุณสามารถตั้งค่าตัวแปรสภาพแวดล้อม QT_FILESYSTEMMODEL_WATCH_FILES ได้ คุณสามารถอ่านเพิ่มเติมเกี่ยวกับตัวแปรนี้ได้ใน บันทึกการเปลี่ยนแปลง:

[QTBUG-46684] ขณะนี้สามารถเปิดใช้งานการรับชมต่อไฟล์ได้โดยการตั้งค่าตัวแปรสภาพแวดล้อม QT_FILESYSTEMMODEL_WATCH_FILES เพื่อให้สามารถติดตามตัวอย่างการเปลี่ยนแปลงขนาดไฟล์ได้

สองสิ่ง:

  • ในขณะนี้คุณต้องตั้งค่า ก่อนที่จะเริ่มต้นโมเดล หลังจากนั้นคุณสามารถตั้งค่าเป็นโฟลเดอร์อื่นได้โดยไม่มีปัญหาใดๆ
  • โปรดทราบว่าคุณลักษณะนี้มาพร้อมกับภาระงานหนักที่อาจเกิดขึ้น
person BPL    schedule 10.04.2018