【问题标题】:Troubleshooting Crash within PyQt5 Application (QDialog -> QMainWindow)PyQt5 应用程序中的故障排除 (QDialog -> QMainWindow)
【发布时间】:2020-10-27 05:43:03
【问题描述】:

我目前正在尝试解决我在 Python 中开发的 Qt 应用程序遇到的问题。我只有一个简单的主窗口和一个模式 QDialog 设置来在主窗口上触发菜单操作后收集用户输入。 QDialog 的行为符合预期,因为我已经能够确认在 QDialog 返回 Accepted 时执行了测试打印,但是在打印语句之后,应用程序将完全崩溃,没有错误消息。当 QDialog 返回 Rejected 时会发生相同的行为。为了澄清,主窗口显示了几秒钟,应用程序崩溃,没有错误消息。在从下面的 Process 函数接收到任一结果后,我希望该函数将焦点返回到主窗口(它仍处于打开状态以进行操作)。

我还尝试使 QDialog 无模式(使用 show),并且 QDialog 中的接受/拒绝函数似乎按预期返回到主窗口,但调用再次启动 QDialog 的函数会使应用程序崩溃。我在这个项目中使用了 pyqt 5.9,但是我在 pyqt 5.6 中的许多其他项目中使用了与下面的代码基本相同的设置,而没有发生这种情况。我试图弄清楚 pyqt 5.9 是否存在任何可能导致此问题的已知问题,或者我的代码中是否存在导致此崩溃发生的错误。我正在考虑回滚到 5.6 以查看是否可以解决此问题,但我觉得可能是我误解了导致这种情况发生的原因。

我在 Windows 10 上从 Anaconda 提示符(Anaconda 4.8.3、Python 3.7)运行代码,因为 Qt 应用程序仍然存在在 Spyder 中重复运行的问题。我正在使用 Anaconda 附带的 pyqt,并且没有对 pyqt 进行任何额外的 pip 安装。

主窗口代码

import sys
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget, QGridLayout, QHBoxLayout, QVBoxLayout, QAction, QLabel
from PyQt5.QtWidgets import QDialog, QListWidget, QListWidgetItem, QAbstractItemView, QPushButton, QLineEdit, QSpacerItem, QSizePolicy


class FirstWindow(QMainWindow):
    def __init__(self):
        QMainWindow.__init__(self)
        
        self.title = 'Window 1'
        self.left = 350
        self.top = 150
        self.width = 800
        self.height = 500
        
        self.setWindowTitle(self.title)
        self.setGeometry(self.left,self.top,self.width,self.height)
        
        widget = QWidget()
        self.setCentralWidget(widget)
        grid = QGridLayout()
        widget.setLayout(grid)
        
        mainMenu = self.menuBar()
        mainMenu.setNativeMenuBar(False)
        subMenu = mainMenu.addMenu('File')
        
        modifyDB = QAction('Test',self)
        subMenu.addAction(modifyDB)
        modifyDB.triggered.connect(self.Process)
        
        self.statusBar().showMessage('Ready')

    def Process(self):
        dialog = DialogWin()
        if dialog.exec_() == QDialog.Accepted:
            print('test passed')

if __name__ == '__main__':
    if not QApplication.instance():
        app = QApplication(sys.argv)
    else:
        app = QApplication.instance()
    mainWin = FirstWindow()
    mainWin.show()
    app.exec_()

对话代码

class DialogWin(QDialog):
    def __init__(self,parent=None):
        super(DialogWin,self).__init__(parent)

        self.title = 'Database Table Name Selection'
        self.left = 350
        self.top = 150
        self.width = 300
        self.height = 300
        
        self.setWindowTitle(self.title)
        self.setGeometry(self.left,self.top,self.width,self.height)

        vbox = QVBoxLayout()
        spacer = QSpacerItem(10,10,QSizePolicy.Expanding,QSizePolicy.Expanding)

        self.list_exp = QLabel('Select Existing Table to Overwrite')
        vbox.addWidget(self.list_exp)

        self.table_list = QListWidget()
        self.table_list.setSelectionMode(QAbstractItemView.SingleSelection)
        vbox.addWidget(self.table_list)
        
        hbox = QHBoxLayout()
        hbox.addItem(spacer)
        self.option_exp = QLabel('<span style="font-size:8pt; font-weight:600; color:#aa0000;">OR</span>')
        hbox.addWidget(self.option_exp)
        hbox.addItem(spacer)
        vbox.addLayout(hbox)
       
        self.new_name = QLineEdit(placeholderText='Enter New Source Name')       
        vbox.addWidget(self.new_name)

        hbox = QHBoxLayout()
        self.okButton = QPushButton('OK')
        hbox.addWidget(self.okButton)
        self.okButton.clicked.connect(self.accept)
        
        self.cancelButton = QPushButton('Cancel')
        hbox.addWidget(self.cancelButton)
        self.cancelButton.clicked.connect(self.reject)
        
        vbox.addLayout(hbox)
        self.setLayout(vbox)

        item = QListWidgetItem('No Tables Available...')
        item.setFlags(item.flags() ^ Qt.ItemIsSelectable)
        self.table_list.addItem(item)

关于这个问题的任何意见都会非常有帮助。我花了很多时间试图了解此设置如何在一个应用程序中为我工作,而不是在另一个应用程序中。

【问题讨论】:

    标签: python pyqt pyqt5 qmainwindow qdialog


    【解决方案1】:

    说明:

    问题是你使用了同一个 QSpacerItem 2 次,当 QDialog 因为它是一个局部变量而关闭时,它将被删除,并且 Qt 也会消除内部对象,在这种情况下 QSpacerItem 将有导致“分段错误”的双重消除。

    解决办法:

    你必须创建 2 个 QSpacerItem:

    # ...
    hbox = QHBoxLayout()
    hbox.addItem(spacer)
    self.option_exp = QLabel('OR')
    hbox.addWidget(self.option_exp)
    hbox.addItem(QSpacerItem(10,10,QSizePolicy.Expanding,QSizePolicy.Expanding))
    vbox.addLayout(hbox)
    # ...

    另一种选择是不使用 QSpacerItem 而是设置拉伸因子:

    # ...
    hbox = QHBoxLayout()
    hbox.addStretch()
    self.option_exp = QLabel('OR')
    hbox.addWidget(self.option_exp)
    hbox.addStretch()
    vbox.addLayout(hbox)
    # ...

    或者不使用QHBoxLayout,通过设置对齐方式直接将QLabel设置为QVBoxLayout:

    # ...
    self.table_list = QListWidget()
    self.table_list.setSelectionMode(QAbstractItemView.SingleSelection)
    vbox.addWidget(self.table_list)
    
    self.option_exp = QLabel('OR')
    vbox.addWidget(self.option_exp, alignment=Qt.AlignHCenter)
       
    self.new_name = QLineEdit(placeholderText='Enter New Source Name')       
    vbox.addWidget(self.new_name)
    # ...

    【讨论】:

    • 非常感谢您的帮助!通过正确处理该间隔,我能够摆脱分段错误。感谢您提供替代解决方案以及对齐。我还想感谢您在社区中如此活跃。我的大部分 Qt 知识来自阅读您和其他用户发布的解决方案,以解决人们在此论坛上使用 Qt 时遇到的问题。
    猜你喜欢
    • 1970-01-01
    • 2011-01-23
    • 2022-12-15
    • 1970-01-01
    • 1970-01-01
    • 2018-02-25
    • 1970-01-01
    • 2012-03-24
    • 1970-01-01
    相关资源
    最近更新 更多