繁体   English   中英

PyQt5崩溃与QFileSystemModel和QSortFilterProxyModel ..做错什么了吗?

[英]PyQt5 Crash with QFileSystemModel and QSortFilterProxyModel..doing something wrong?

在Ubuntu上使用pyqt5导航目录结构时,出现了间歇性的崩溃问题。 有时,仅浏览到一个文件夹会崩溃,而其他时候,它是由对所显示目录的外部更改触发的。 以下代码是后者的示例。

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

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

尽管我对解决方案不满意,但我已将其固定修复,但是今天即使进行了模型更新,我也再次在代码中崩溃。

那里没有很多示例代码显示如何一起使用这些类。 有人看到我在做什么错吗?

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'目录,并且可以退出和重试该程序。

在获取以下堆栈跟踪之前,在gbd下执行了大约8次。编辑为最后2次运行:

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后,我不再能够重现崩溃。 谢谢您的帮助。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM