PyQt5 崩溃与 QFileSystemModel 和 QSortFilterProxyModel ..做错了什么? [英] PyQt5 Crash with QFileSystemModel and QSortFilterProxyModel..doing something wrong?

查看:71
本文介绍了PyQt5 崩溃与 QFileSystemModel 和 QSortFilterProxyModel ..做错了什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在 Ubuntu 上使用 pyqt5 浏览目录结构时,我遇到了间歇性崩溃问题.有时只是导航到文件夹崩溃,其他时候它是由正在显示的目录的外部更改触发的.以下代码是后者的示例.

I've had an intermittent problem with a crash when navigating a directory structure with pyqt5 on Ubuntu. Sometimes just navigating to a folder crashes, other times it is triggered by external changes to the directory being displayed. The following code is an example of the latter.

我正在构建的应用程序会检查激活程序时显示的目录.如果它不再是一个有效目录,它会向上移动树到下一个有效目录.下面的代码通过创建一个目录结构,向下导航到一个文件并选择它,重命名树,然后从不再有效的目录移动到一个有效的目录来模拟.如果我多次运行这段代码,我会看到它崩溃,有时在第一次执行时,有时在运行几次后.

The application I'm building checks the directory being displayed when the program is activated. If it is no longer a valid directory, it moves up the tree to next valid directory. The code below simulates that by creating a directory structure, navigating down to a file and selecting it, renames the tree, then moves from the no longer valid directory up to one that is. If I run this code a few times I will see it crash, sometimes on the first execution, sometimes after a few runs.

我尝试根据在 gdb 堆栈跟踪中看到的代理模型函数来更新模式.这似乎解决了我的问题,尽管这对我来说似乎是错误的.

I tried updating the mode based on seeing proxy model function in the gdb stack trace. This seemed to fix my problem, though this seems wrong to me.

尽管我对解决方案不满意,但我已将其写为已修复,但今天即使更新了模型,此代码也再次崩溃.

I had written it off as fixed, even though I wasn't happy with the solution, but today I got crashes in this code again even with the model update.

没有很多示例代码展示了如何一起使用这些类.有人看到我哪里做错了吗?

There's not a lot of sample code out there showing how to use these classes together. Anyone see where I'm doing something wrong?

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._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)

        header = self._view.horizontalHeader()
        header.setSortIndicator(0, QtCore.Qt.AscendingOrder)
        self._proxy.sort(header.sortIndicatorSection(), header.sortIndicatorOrder())

        # Create a temporary directory structure (tmpxyz/a/b/c) starting at the location of this file
        # This is oddly very specific..less nesting or no file to select doesn't exhibit the problem!?
        path = os.path.dirname(os.path.abspath(__file__))
        self.temp_dir = tempfile.TemporaryDirectory(dir=path)
        print('created temporary directory', self.temp_dir.name)
        foo_dir = os.path.join(self.temp_dir.name, 'foo')
        os.mkdir(foo_dir)
        self.a_dir = os.path.join(foo_dir, 'a')
        os.mkdir(self.a_dir)
        b_dir = os.path.join(self.a_dir, 'b')
        os.mkdir(b_dir)
        c_dir = os.path.join(b_dir, 'c')
        os.mkdir(c_dir)
        self.temp_file_name = os.path.join(c_dir, 'foo.txt')
        with open(self.temp_file_name, 'w') as txtfile:
            print('foo', file=txtfile)

        self._model.setRootPath(c_dir)

        # moved the following to loaded..didn't seem to help
        # source_index = self._model.index(c_dir)
        # index = self._proxy.mapFromSource(source_index)
        # self._view.setRootIndex(index)

        QtCore.QTimer.singleShot(4000, self._select_temp_file)
        QtCore.QTimer.singleShot(5000, self._rename_test_dir)

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

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

    def _select_temp_file(self):
        source_index = self._model.index(self.temp_file_name)
        if source_index.isValid():
            index = self._proxy.mapFromSource(source_index)
            self._view.setCurrentIndex(index)

    # Simulate an external application moving the current directory
    def _rename_test_dir(self):
        os.rename(self.a_dir, self.a_dir+'x')
        path = self._model.rootPath()
        print('_rename_test_dir', path)
        if not os.path.isdir(path):
            def _make_valid_path(path_):
                if os.path.isdir(path_):
                    return path_
                while path_:
                    path_, _ = os.path.split(path_)
                    if os.path.isdir(path_):
                        return path_
                return '/'

            print('Path not valid:', path)
            path = _make_valid_path(path)
            print('New Path:', path)

            self._model.setRootPath(path)
            print('_rename_test_dir 5')
            # Using gdb showed a crash in the proxy model..so update it here...decreases crash frequency
            # self._proxy.setSourceModel(self._model)
            # print('_rename_test_dir 6')
            # self._view.setModel(self._proxy)
            print('_rename_test_dir 7')

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

更新:

如果从代码中看不清楚,程序不需要交互,交互会干扰测试.创建目录结构后,它将自动转到结构的叶级.4 秒后,它将选择 foo.txt 文件.再过一秒钟,它将重命名树中的a"目录,然后将树向上导航到a"曾经所在的位置.如果一切顺利,将显示 foo 目录,其中可以看到 'ax' 目录,并且可以退出并重试程序.

In case it is not clear from the code, the program requires no interaction and interaction can interfere with the test. After the directory structure is created it will automatically go to the leaf level of the structure. After 4 seconds it will select the foo.txt file. Another second later it will rename the 'a' directory in the tree and then navigate back up the tree to where 'a' used to be. If all goes well the foo directory will be displayed where the 'ax' directory can be seen and the program can be exited and retried.

在得到以下堆栈跟踪之前,在 gbd 下执行大约需要 8 次.编辑到最后 2 次运行:

Executing under gbd took about 8 times before getting the following stack trace..edited down to last 2 runs:

gdb --args python3 test_qfilesystemmodel_crash2.py  
(gdb) run
Starting program: /usr/bin/python3 test_qfilesystemmodel_crash2.py
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
[New Thread 0x7fffe9ea5700 (LWP 21128)]
[New Thread 0x7fffdb5e5700 (LWP 21129)]
[New Thread 0x7fffdade4700 (LWP 21130)]
created temporary directory /home/myuser/Desktop/haiqu/src/tmp8dl0_uny
_loaded
_loaded /home/myuser/Desktop/haiqu/src/tmp8dl0_uny/foo/a/b/c
_rename_test_dir /home/myuser/Desktop/haiqu/src/tmp8dl0_uny/foo/a/b/c
Path not valid: /home/myuser/Desktop/haiqu/src/tmp8dl0_uny/foo/a/b/c
New Path: /home/myuser/Desktop/haiqu/src/tmp8dl0_uny/foo
_rename_test_dir 5
_rename_test_dir 7
_loaded
_loaded /home/myuser/Desktop/haiqu/src/tmp8dl0_uny/foo
[Thread 0x7fffdb5e5700 (LWP 21129) exited]
[Thread 0x7fffe9ea5700 (LWP 21128) exited]
[Thread 0x7ffff7fbb700 (LWP 21127) exited]
[Inferior 1 (process 21127) exited normally]
(gdb) run
Starting program: /usr/bin/python3 test_qfilesystemmodel_crash2.py
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
[New Thread 0x7fffe9ea5700 (LWP 21132)]
[New Thread 0x7fffdb5e5700 (LWP 21133)]
[New Thread 0x7fffdade4700 (LWP 21134)]
created temporary directory /home/myuser/Desktop/haiqu/src/tmpwr560n5l
_loaded
_loaded /home/myuser/Desktop/haiqu/src/tmpwr560n5l/foo/a/b/c
_rename_test_dir /home/myuser/Desktop/haiqu/src/tmpwr560n5l/foo/a/b/c
Path not valid: /home/myuser/Desktop/haiqu/src/tmpwr560n5l/foo/a/b/c
New Path: /home/myuser/Desktop/haiqu/src/tmpwr560n5l/foo
_rename_test_dir 5
_rename_test_dir 7

Thread 1 "python3" received signal SIGSEGV, Segmentation fault.
0x00007ffff5062e89 in QSortFilterProxyModel::sibling(int, int, QModelIndex const&) const ()
   from /usr/lib/x86_64-linux-gnu/libQt5Core.so.5
(gdb) bt
#0  0x00007ffff5062e89 in QSortFilterProxyModel::sibling(int, int, QModelIndex const&) const ()
   from /usr/lib/x86_64-linux-gnu/libQt5Core.so.5
#1  0x00007ffff568d2d4 in ?? () from /usr/lib/python3/dist-packages/PyQt5/QtCore.cpython-35m-x86_64-linux-gnu.so
#2  0x00007ffff0af35ff in ?? () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#3  0x00007ffff00f9779 in QAccessible::updateAccessibility(QAccessibleEvent*) ()
   from /usr/lib/x86_64-linux-gnu/libQt5Gui.so.5
#4  0x00007ffff0b028cc in ?? () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#5  0x00007ffff0b07486 in ?? () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#6  0x00007ffff50cb4a9 in QMetaObject::activate(QObject*, int, int, void**) ()
   from /usr/lib/x86_64-linux-gnu/libQt5Core.so.5
#7  0x00007ffff51426e4 in QAbstractItemModel::rowsInserted(QModelIndex const&, int, int, QAbstractItemModel::QPrivateSignal) () from /usr/lib/x86_64-linux-gnu/libQt5Core.so.5
#8  0x00007ffff5046f7b in QAbstractItemModel::endInsertRows() () from /usr/lib/x86_64-linux-gnu/libQt5Core.so.5
#9  0x00007ffff5068246 in ?? () from /usr/lib/x86_64-linux-gnu/libQt5Core.so.5
#10 0x00007ffff506a685 in ?? () from /usr/lib/x86_64-linux-gnu/libQt5Core.so.5
#11 0x00007ffff506e089 in ?? () from /usr/lib/x86_64-linux-gnu/libQt5Core.so.5
#12 0x00007ffff50cb4a9 in QMetaObject::activate(QObject*, int, int, void**) ()
   from /usr/lib/x86_64-linux-gnu/libQt5Core.so.5
#13 0x00007ffff51426e4 in QAbstractItemModel::rowsInserted(QModelIndex const&, int, int, QAbstractItemModel::QPrivateSignal) () from /usr/lib/x86_64-linux-gnu/libQt5Core.so.5
#14 0x00007ffff5046f7b in QAbstractItemModel::endInsertRows() () from /usr/lib/x86_64-linux-gnu/libQt5Core.so.5
#15 0x00007ffff0acf6e6 in ?? () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#16 0x00007ffff0ad51f9 in ?? () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#17 0x00007ffff50cc359 in QObject::event(QEvent*) () from /usr/lib/x86_64-linux-gnu/libQt5Core.so.5
#18 0x00007ffff0ad58b2 in QFileSystemModel::event(QEvent*) () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#19 0x00007ffff131ecdb in ?? () from /usr/lib/python3/dist-packages/PyQt5/QtWidgets.cpython-35m-x86_64-linux-gnu.so
#20 0x00007ffff08b635c in QApplicationPrivate::notify_helper(QObject*, QEvent*) ()
   from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#21 0x00007ffff08bdb11 in QApplication::notify(QObject*, QEvent*) ()
   from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#22 0x00007ffff124cdee in ?? () from /usr/lib/python3/dist-packages/PyQt5/QtWidgets.cpython-35m-x86_64-linux-gnu.so
#23 0x00007ffff509f8a0 in QCoreApplication::notifyInternal2(QObject*, QEvent*) ()
   from /usr/lib/x86_64-linux-gnu/libQt5Core.so.5
#24 0x00007ffff50a202d in QCoreApplicationPrivate::sendPostedEvents(QObject*, int, QThreadData*) ()
   from /usr/lib/x86_64-linux-gnu/libQt5Core.so.5
#25 0x00007ffff50f3b03 in ?? () from /usr/lib/x86_64-linux-gnu/libQt5Core.so.5
#26 0x00007ffff3b35377 in g_main_context_dispatch () from /lib/x86_64-linux-gnu/libglib-2.0.so.0
#27 0x00007ffff3b355e0 in ?? () from /lib/x86_64-linux-gnu/libglib-2.0.so.0
#28 0x00007ffff3b3568c in g_main_context_iteration () from /lib/x86_64-linux-gnu/libglib-2.0.so.0
#29 0x00007ffff50f3f0f in QEventDispatcherGlib::processEvents(QFlags<QEventLoop::ProcessEventsFlag>) ()
   from /usr/lib/x86_64-linux-gnu/libQt5Core.so.5
#30 0x00007ffff509d88a in QEventLoop::exec(QFlags<QEventLoop::ProcessEventsFlag>) ()
   from /usr/lib/x86_64-linux-gnu/libQt5Core.so.5
#31 0x00007ffff50a5ffc in QCoreApplication::exec() () from /usr/lib/x86_64-linux-gnu/libQt5Core.so.5
#32 0x00007ffff1210ddb in ?? () from /usr/lib/python3/dist-packages/PyQt5/QtWidgets.cpython-35m-x86_64-linux-gnu.so
#33 0x000055555571b28f in PyCFunction_Call ()
#34 0x00005555556d21e9 in PyEval_EvalFrameEx ()
#35 0x00005555556d6d16 in ?? ()
#36 0x00005555556d7a1f in PyEval_EvalCode ()
#37 0x00005555557a4b02 in ?? ()
#38 0x00005555557a6f8d in PyRun_FileExFlags ()
#39 0x00005555557a772e in PyRun_SimpleFileExFlags ()
#40 0x00005555557d72e7 in Py_Main ()
#41 0x0000555555667b31 in main ()
(gdb) 

推荐答案

我将此作为一个旧的库问题写下来.更新到 pyqt5.8 和 python3.6 后,我无法再重现崩溃.感谢您的帮助.

I'm writing this off as an old library problem. After updating to pyqt5.8 and python3.6 I am no longer able to reproduce the crash. Thanks for the help.

这篇关于PyQt5 崩溃与 QFileSystemModel 和 QSortFilterProxyModel ..做错了什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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