3

I am currently trying to troubleshoot an issue I am running into with a Qt application I am developing in Python. I only have a simple Main Window and a modal QDialog setup to collect user input after triggering a menu action on the Main Window. The QDialog is behaving as expected, since I've been able to confirm the test print is executed upon the QDialog returning Accepted, but the application will completely crash, without an error message, following the print statement. The same behavior occurs when QDialog returns Rejected. To clarify, the Main Window is shown for a couple seconds and the application crashes with no error messages. I would expect the function to return focus to the Main Window (with it still being open to operate on), after receiving either result from the Process function below.

I have also tried making the QDialog modeless (using show) and the accept/reject functions from the QDialog seem to return back to the main window as expected, but calling the function that brings up the QDialog again crashes the application. I am using pyqt 5.9 for this project, but I have used an essentially identical setup to the code below in a number of other projects in pyqt 5.6 without this happening. I am trying to figure out if there are any known issues with pyqt 5.9 that could be contributing to this issue, or if I have an error in my code somewhere that is causing this crash to occur. I was considering rolling back to 5.6 to see if that would get rid of the issue, but I feel like there may be something I am misunderstanding that is causing this to occur.

I am running the code out of Anaconda prompt (Anaconda 4.8.3, Python 3.7) on Windows 10, since Qt applications still have issues being run repeatedly within Spyder. I am using the pyqt that comes with Anaconda and haven't done any additional pip installs of pyqt.

Main Window Code

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

Dialog Code

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)

Any input on this issue would be tremendously helpful. I've spent a lot of hours trying to understand how this setup could be working for me in one application and not in another.

eyllanesc
  • 235,170
  • 19
  • 170
  • 241
soprano82
  • 33
  • 4

1 Answers1

1

Explanation:

The problem is that you are using the same QSpacerItem 2 times, and when the QDialog is closed as it is a local variable it will be deleted, and Qt will also eliminate the internal objects, and in that case the QSpacerItem will have a double elimination that causes the "Segmentation fault".

Solution:

You must create 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)
# ...

Another alternative is not to use QSpacerItem but to set a stretch factor:

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

Or don't use QHBoxLayout and set the QLabel directly to the QVBoxLayout by setting the alignment:

# ...
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)
# ...
eyllanesc
  • 235,170
  • 19
  • 170
  • 241
  • Thank you so much for your help! I was able to get rid of the segmentation fault by dealing with that spacer properly. Thanks for providing an alternative solution as well with alignment. I also wanted to thank you for being so active in the community. The majority of my Qt knowledge has come from reading solutions you and another user have posted to problems people have come across working with Qt on this forum. – soprano82 Jul 07 '20 at 16:03