Search code examples
pythonpyqtqt-designerpyside2

How to get self.close to work, when using Qt designer?


thanks for reading!

The problem generally is, that i do not really know where to properly code signals and slots when using a converted .py file from the QtDesigner, and I don't seem to find any useful answers on this particular problem. My initial code consists of three files, which I mostly did not create by myself btw, all credit goes to "rebelCoder" (https://www.youtube.com/user/JurisL85) and "Alan D Moore Codes" (https://www.youtube.com/channel/UCj7i-mmOjLV17YTPIrCPkog).

The first file is just running methods of the DNAEngine class (see third file) and the second file is a converted .py file from the QtDesigner (engine_ui in third file). I don't edit both of these files.

The third file initially looked like this (copied from rebelCoder):

from engine_ui import Ui_MainWindow
import sys
from PySide2 import QtWidgets as qtw
from PySide2 import QtCore as qtc


class DNAEngine:
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.app = qtw.QApplication(sys.argv)
        self.MainWindow = qtw.QMainWindow()

    def setup(self):
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self.MainWindow)

    def run(self):
        sys.exit(self.app.exec_())

    def display(self):
        self.MainWindow.show()

My idea now was to create a subclass of Ui_MainWindow (the class from the converted file), where I theoretically would be able to add signals, which leads to this:

from engine_ui import Ui_MainWindow
from PySide2 import QtWidgets as qtw
from PySide2 import QtCore as qtc
from PySide2 import QtGui as qtg
import sys


class DNASignal(Ui_MainWindow):
    def __init__(self, window):
        self.setupUi(window)
        # signal code starts here
        self.actionQuit.triggered.connect(self.close) # does not work
        self.actionSave.triggered.connect(self.onActionSaveTriggered)
        self.actionOpen.triggered.connect(self.onActionOpenTriggered)
        # signal code ends here

    def onActionSaveTriggered(self):
        text = self.textedit.toPlainText()
        filename, _ = qtw.QFileDialog.getSaveFileName()
        if filename:
            with open(filename, 'w') as handle:
                handle.write(text)
                self.statusBar().showMessage(f'Saved to {filename}')

    def onActionOpenTriggered(self):
        filename, _ = qtw.QFileDialog.getOpenFileName()
        if filename:
            with open(filename, 'r') as handle:
                text = handle.read()
            self.textEditInput.clear()
            self.textEditInput.insertPlainText(text)
            self.textEditInput.moveCursor(qtg.QTextCursor.Start)
            self.statusBar().showMessage(f'Editing {filename}')


class DNAEngine:
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.app = qtw.QApplication(sys.argv)
        self.MainWindow = qtw.QMainWindow()

    def setup(self):
        self.ui = DNASignal(self.MainWindow)

    def run(self):
        sys.exit(self.app.exec_())

    def display(self):
        self.MainWindow.show()

I am trying to connect three actions of the menubar, a Quit, Save, and Open button. While the Open and Save actions work perfectly fine, the Quit action does not work. When I start the app without commenting the Quit signal out, it throws this error:

AttributeError: 'DNASignal' object has no attribute 'close'

How to get the quit action to work?

Also, while this approach seems to work for the other actions, it is quite messy and surely not optimal, so how to optimise this? However, Id like to keep the first file (the one running the DNAEngine class) separately.


Solution

  • Subclassing the "form class" (the one created from the ui) is pointless, as it's just a python class that does almost nothing on its own.
    What you need is to implement the methods provided by the QWidget you're using (QMainWindow, in your case), so you need to inherit from both the widget and the form classes:

    class DNAMainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
        def __init__(self):
            super().__init__()
            self.setupUi(self)
            # ...
    
    
    class DNAEngine:
        def __init__(self, *args, **kwargs):
            super().__init__(*args, **kwargs)
            self.app = qtw.QApplication(sys.argv)
    
        def setup(self):
            self.mainWindow = DNAMainWindow()
    
        def display(self):
            self.mainWindow.show()