1

I'm new to Python and I've searched for an answer but couldn't find it (or rather couldn't properly implement it).

I've generated a window with a few buttons in QtDesigner's file named "arch.ui", converted to arch.py.

As I'll be updating GUI occasionally, I don't want to create functions in arch.py, so I've created a main.py file for that.

I've a problem with linking button click to a function => I try to link "btnSource" (from arch.py) to function "printMe" (in main.py).

Obviously it doesn't work. Any help welcome.

Here is generated Designer file:

# Form implementation generated from reading ui file 'arch.ui'
#
# Created by: PyQt5 UI code generator 5.15.0
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again.  Do not edit this file unless you know what you are doing.


from PyQt5 import QtCore, QtGui, QtWidgets


class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(460, 233)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.btnSource = QtWidgets.QPushButton(self.centralwidget)
        self.btnSource.setGeometry(QtCore.QRect(80, 60, 75, 23))
        self.btnSource.setObjectName("btnSource")
        self.lblSource = QtWidgets.QLabel(self.centralwidget)
        self.lblSource.setGeometry(QtCore.QRect(180, 60, 511, 21))
        self.lblSource.setObjectName("lblSource")
        self.lblTarget = QtWidgets.QLabel(self.centralwidget)
        self.lblTarget.setGeometry(QtCore.QRect(180, 120, 481, 16))
        self.lblTarget.setObjectName("lblTarget")
        self.btnTarget = QtWidgets.QPushButton(self.centralwidget)
        self.btnTarget.setGeometry(QtCore.QRect(80, 120, 75, 23))
        self.btnTarget.setObjectName("btnTarget")
        self.btnGo = QtWidgets.QPushButton(self.centralwidget)
        self.btnGo.setGeometry(QtCore.QRect(280, 120, 75, 23))
        self.btnGo.setObjectName("btnGo")
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 460, 21))
        self.menubar.setObjectName("menubar")
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
        self.btnSource.setText(_translate("MainWindow", "Source"))
        self.lblSource.setText(_translate("MainWindow", "TextLabel"))
        self.lblTarget.setText(_translate("MainWindow", "TextLabel"))
        self.btnTarget.setText(_translate("MainWindow", "Target"))
        self.btnGo.setText(_translate("MainWindow", "Go"))

And here is my main.py file:

from PyQt5 import QtCore, QtGui, QtWidgets
from arch import Ui_MainWindow
import sys 

  
app = QtWidgets.QApplication(sys.argv)
class myWindow(Ui_MainWindow):
    def __init__(self):
        super(myWindow, self).__init__()
        self.btnSource.clicked.connect(self.btnSource.printMe)#

    def printMe(self):
        print('blah blah blah')

MainWindow = QtWidgets.QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec_())

Chris
  • 23
  • 1
  • 5

1 Answers1

1

tl;dr

Subclass from both QMainWindow and Ui_MainWindow, and call setupUi from there; then create an instance of myWindow:

class MyWindow(QtWidgets.QMainWindow, Ui_MainWindow):
    def __init__(self):
        super(MyWindow, self).__init__()
        self.setupUi(self)
        self.btnSource.clicked.connect(self.printMe)

    def printMe(self):
        print('blah blah blah')

if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    mainWindow = MyWindow()
    mainWindow.show()
    sys.exit(app.exec_())

Explanation

Your code doesn't work for many reasons; while the main problem might be that you actually never created an instance of myWindow (about that, you should always use capitalized names for classes), making it completely useless, it wouldn't have worked anyway.

That's because you should not subclass from the ui class object, but from the QWidget descendant (QMainWindow, in your case) you're going to use.

The ui_* objects created from pyuic are only intended as a high level (and unmodified) interface to create the UI on top of a QWidget subclass.
Calling setupUi(something) actually creates all child widgets for the widget something, sets the layout and, possibly, automatically connects to slots with a compatible name, but that's all: in fact, if you closely look at the code from the ui file, it actually does nothing besides setupUi and retranslateUi (nor it should!): there's not even an __init__!

If you need to add interaction and create connections from signals to slot/functions, you should use the single/multiple inheritance approaches as explained in the official guide about using Designer with PyQt; the only other possibility is to use loadUi (while still subclassing from the base class) with the source .ui file:

from PyQt5 import QtWidgets, uic

class MyWindow(QtWidgets.QMainWindow):
    def __init__(self):
        super().__init__()
        uic.loadUi('path/to/gui.ui', self)
        self.someWidget.someSignal.connect(self.someSlot)
        # ...

    def someSlot(self, signalArguments, [...]):
        # do something...

PS: for various reasons, it's usually better to run a QApplication only if the script is the one that's been run (hence the if __name__ ...), mostly because there should be just only one QApplication instance for every running program; in any case, it shouldn't be created before the class declarations (unless, you really know what you're doing); it's not a big deal in your case, but, as usual, better safe than sorry.

musicamante
  • 41,230
  • 6
  • 33
  • 58
  • Thanks for very informative response and explanation. Unfortunately the solution doesn't work -> AttributeError: 'QPushButton' object has no attribute 'printMe' – Chris Aug 15 '20 at 03:07
  • Sorry, I just copied from your code (which was wrong to begin with): it should point to `self.printMe`, since the function is a member of the class instance (`self`). – musicamante Aug 15 '20 at 03:34
  • That works!. Thank you very much! – Chris Aug 15 '20 at 04:18