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

时间:2017-06-27 23:23:29

标签: python python-3.x pyqt pyqt5

在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) 

1 个答案:

答案 0 :(得分:0)

我把它写成旧的库问题。更新到pyqt5.8和python3.6后,我再也无法重现崩溃。谢谢你的帮助。