QFileSystemModel rowCount() 在 setNameFilters() 之后出错
QFileSystemModel rowCount() wrong after setNameFilters()
我正在尝试添加标签以显示文件夹中的文件数。看起来很容易,但最近我注意到数字不正确(可能永远错了,我从来没有注意到)。我在 linux.
上的 pyqt5 和 pyqt6 中试过这个
出于演示目的,我创建了一个包含 20 个文件的文件夹,但过滤后仅显示 10 个文件。然后我通过计时器设置过滤器以仅显示 2 个文件,然后再次显示所有文件。您将从下面的输出中看到更新计数需要一秒钟。
import os.path
import sys
import tempfile
# from PyQt6 import QtCore, QtGui, QtWidgets
from PyQt5 import QtCore, QtGui, QtWidgets
def test(path):
view = QtWidgets.QTreeView()
# model = QtGui.QFileSystemModel(view) # pyqt6
model = QtWidgets.QFileSystemModel(view) # pyqt5
view.setModel(model)
model.setRootPath(path)
index = model.index(model.rootPath())
view.setRootIndex(index)
model.setNameFilterDisables(False) # True shows filtered items disabled...False removes them
model.setNameFilters(['*.foo'])
def loaded():
print(f'loaded {model.rowCount(index)=}')
def update_filter2():
model.setNameFilters(['1.*'])
print(f'update_filter {model.rowCount(index)=} expected=2') # TODO: how to get this to report 2
def update_filter20():
model.setNameFilters([])
print(f'update_filter {model.rowCount(index)=} expected=20') # TODO: how to get this to report 20
model.directoryLoaded.connect(loaded)
QtCore.QTimer.singleShot(2000, update_filter2)
QtCore.QTimer.singleShot(3000, update_filter2)
QtCore.QTimer.singleShot(4000, update_filter20)
QtCore.QTimer.singleShot(5000, update_filter20)
return view
if __name__ == '__main__':
app = QtWidgets.QApplication([])
# create test data and clean up afterwards
with tempfile.TemporaryDirectory() as temp_dir_name:
print(temp_dir_name)
for i in range(10):
with open(os.path.join(temp_dir_name, f'{i}.foo'), 'w') as file:
print(i, file=file)
with open(os.path.join(temp_dir_name, f'{i}.bar'), 'w') as file:
print(i, file=file)
w = test(temp_dir_name)
w.show()
sys.exit(app.exec())
如果我等一下,我会看到预期的行数。所以我想问题是,缺少计时器逻辑。你怎么知道什么时候调用 rowCount() 是安全的?我希望看到 directoryLoaded 再次发出信号。还有其他信号吗?
/tmp/tmplwv8nxi4
loaded model.rowCount(index)=10
loaded model.rowCount(index)=10
update_filter model.rowCount(index)=10 expected=2
update_filter model.rowCount(index)=2 expected=2
update_filter model.rowCount(index)=2 expected=20
update_filter model.rowCount(index)=20 expected=20
QFileSystemModel uses a separate thread to populate itself so it will not cause the main thread to hang as the file system is being queried. Calls to rowCount() will return 0 until the model populates a directory.
默认情况下,模型具有最大默认数量的项目获取,并且 QFileSystemModel 使用相同的线程方面在排序和过滤方面延迟 fetchMore()
。这使得瞬时行计数不可靠。
由于您只需要显示可见项目的“静态”计数,解决方案是正确连接到实际改变模型布局的信号:
def updateCount():
print(model.rowCount(view.rootIndex()))
model.layoutChanged.connect(updateCount)
model.rowsRemoved.connect(updateCount)
model.rowsInserted.connect(updateCount)
layoutChanged
对于基本的 sorting/filtering 通常足够了,但是由于您可能还对创建或删除(包括重命名)项目时的目录更改感兴趣,因此应连接其他两个信号也是。
出于性能原因,通常最好创建一个带有 singleShot
标志集和非常低间隔(大于 0,但小于 10-20 毫秒)的 QTimer 实例,然后将这些信号连接到计时器的 start
插槽。这样,如果在短时间内触发了多个信号,计时器最终会重新启动。
不要为此使用静态QTimer.singleShot
。
timer = QTimer(interval=10, timeout=updateCount)
model.layoutChanged.connect(timer.start)
model.rowsRemoved.connect(timer.start)
model.rowsInserted.connect(timer.start)
我正在尝试添加标签以显示文件夹中的文件数。看起来很容易,但最近我注意到数字不正确(可能永远错了,我从来没有注意到)。我在 linux.
上的 pyqt5 和 pyqt6 中试过这个出于演示目的,我创建了一个包含 20 个文件的文件夹,但过滤后仅显示 10 个文件。然后我通过计时器设置过滤器以仅显示 2 个文件,然后再次显示所有文件。您将从下面的输出中看到更新计数需要一秒钟。
import os.path
import sys
import tempfile
# from PyQt6 import QtCore, QtGui, QtWidgets
from PyQt5 import QtCore, QtGui, QtWidgets
def test(path):
view = QtWidgets.QTreeView()
# model = QtGui.QFileSystemModel(view) # pyqt6
model = QtWidgets.QFileSystemModel(view) # pyqt5
view.setModel(model)
model.setRootPath(path)
index = model.index(model.rootPath())
view.setRootIndex(index)
model.setNameFilterDisables(False) # True shows filtered items disabled...False removes them
model.setNameFilters(['*.foo'])
def loaded():
print(f'loaded {model.rowCount(index)=}')
def update_filter2():
model.setNameFilters(['1.*'])
print(f'update_filter {model.rowCount(index)=} expected=2') # TODO: how to get this to report 2
def update_filter20():
model.setNameFilters([])
print(f'update_filter {model.rowCount(index)=} expected=20') # TODO: how to get this to report 20
model.directoryLoaded.connect(loaded)
QtCore.QTimer.singleShot(2000, update_filter2)
QtCore.QTimer.singleShot(3000, update_filter2)
QtCore.QTimer.singleShot(4000, update_filter20)
QtCore.QTimer.singleShot(5000, update_filter20)
return view
if __name__ == '__main__':
app = QtWidgets.QApplication([])
# create test data and clean up afterwards
with tempfile.TemporaryDirectory() as temp_dir_name:
print(temp_dir_name)
for i in range(10):
with open(os.path.join(temp_dir_name, f'{i}.foo'), 'w') as file:
print(i, file=file)
with open(os.path.join(temp_dir_name, f'{i}.bar'), 'w') as file:
print(i, file=file)
w = test(temp_dir_name)
w.show()
sys.exit(app.exec())
如果我等一下,我会看到预期的行数。所以我想问题是,缺少计时器逻辑。你怎么知道什么时候调用 rowCount() 是安全的?我希望看到 directoryLoaded 再次发出信号。还有其他信号吗?
/tmp/tmplwv8nxi4
loaded model.rowCount(index)=10
loaded model.rowCount(index)=10
update_filter model.rowCount(index)=10 expected=2
update_filter model.rowCount(index)=2 expected=2
update_filter model.rowCount(index)=2 expected=20
update_filter model.rowCount(index)=20 expected=20
QFileSystemModel uses a separate thread to populate itself so it will not cause the main thread to hang as the file system is being queried. Calls to rowCount() will return 0 until the model populates a directory.
默认情况下,模型具有最大默认数量的项目获取,并且 QFileSystemModel 使用相同的线程方面在排序和过滤方面延迟 fetchMore()
。这使得瞬时行计数不可靠。
由于您只需要显示可见项目的“静态”计数,解决方案是正确连接到实际改变模型布局的信号:
def updateCount():
print(model.rowCount(view.rootIndex()))
model.layoutChanged.connect(updateCount)
model.rowsRemoved.connect(updateCount)
model.rowsInserted.connect(updateCount)
layoutChanged
对于基本的 sorting/filtering 通常足够了,但是由于您可能还对创建或删除(包括重命名)项目时的目录更改感兴趣,因此应连接其他两个信号也是。
出于性能原因,通常最好创建一个带有 singleShot
标志集和非常低间隔(大于 0,但小于 10-20 毫秒)的 QTimer 实例,然后将这些信号连接到计时器的 start
插槽。这样,如果在短时间内触发了多个信号,计时器最终会重新启动。
不要为此使用静态QTimer.singleShot
。
timer = QTimer(interval=10, timeout=updateCount)
model.layoutChanged.connect(timer.start)
model.rowsRemoved.connect(timer.start)
model.rowsInserted.connect(timer.start)