Search code examples
python-2.7pyqtpyqt4signals-slotsqt-designer

Connected Signal clicked doesnt execute callback


im trying to make an application in python 2.7 with QtDesigner and PyQT 4.11.

In the application I want to have two QPushButtons that change the value of a QSpinBox when clicked. I've read several pages of StackOverflow answers concerning problems with the Signal Slot mechanic, but couldnt find an answer to my problem.

Im using pyuic4 to generate the Ui_W_Setup.py, here the rellevant part:

class Ui_W_Setup(object):
  def setupUi(self, W_Setup):
    W_Setup.setObjectName(_fromUtf8("W_Setup"))
    self.Motorsteuerung = QtGui.QDockWidget(W_Setup)
    self.Motorsteuerung.setEnabled(True)
    self.Motorsteuerung.setMinimumSize(QtCore.QSize(140, 365))
    self.Motorsteuerung.setFeatures(QtGui.QDockWidget.DockWidgetFloatable|QtGui.QDockWidget.DockWidgetMovable)
    self.Motorsteuerung.setAllowedAreas(QtCore.Qt.LeftDockWidgetArea)
    self.Motorsteuerung.setObjectName(_fromUtf8("Motorsteuerung"))
    self.dockWidgetContents = QtGui.QWidget()
    self.dockWidgetContents.setObjectName(_fromUtf8("dockWidgetContents"))
    self.B_ZuNehm = QtGui.QPushButton(self.dockWidgetContents)
    self.B_ZuNehm.setGeometry(QtCore.QRect(40, 250, 81, 71))
    self.B_ZuNehm.setObjectName(_fromUtf8("B_ZuNehm"))

    self.B_ZuTast = QtGui.QPushButton(self.dockWidgetContents)
    self.B_ZuTast.setGeometry(QtCore.QRect(40, 180, 81, 71))
    self.B_ZuTast.setObjectName(_fromUtf8("B_ZuTast"))

    self.SB_Position = QtGui.QDoubleSpinBox(self.dockWidgetContents)
    self.SB_Position.setGeometry(QtCore.QRect(40, 21, 81, 31))
    font = QtGui.QFont()
    font.setPointSize(10)
    self.SB_Position.setFont(font)
    self.SB_Position.setFrame(True)
    self.SB_Position.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
    self.SB_Position.setButtonSymbols(QtGui.QAbstractSpinBox.NoButtons)
    self.SB_Position.setSpecialValueText(_fromUtf8(""))
    self.SB_Position.setCorrectionMode(QtGui.QAbstractSpinBox.CorrectToNearestValue)
    self.SB_Position.setDecimals(1)
    self.SB_Position.setMaximum(50.0)
    self.SB_Position.setSingleStep(0.2)
    self.SB_Position.setProperty("value", 0.0)
    self.SB_Position.setObjectName(_fromUtf8("SB_Position"))

Then I have a main python file to load the ui.py and execute the application:

import sys
from PyQt4 import QtGui,QtCore
from Setup import Ui_W_Setup

app = QtGui.QApplication(sys.argv)

class MainWindow(QtGui.QMainWindow, Ui_W_Setup):
  def __init__(self):
    QtGui.QMainWindow.__init__(self)
    self.setupUi(self)

    motor = MotorClass()

    self.B_ZuNehm.clicked.connect(motor.zuNehm)
    self.B_ZuTast.clicked.connect(motor.zuTast)

class MotorClass(QtCore.QObject):
  def __init__(self):
    super(MotorClass, self).__init__()

  global window
  confirm = QtGui.QMessageBox()
  confirm.setText('Verfahrweg frei?')
  confirm.setStandardButtons(QtGui.QMessageBox.No | QtGui.QMessageBox.Yes)
  confirm.setDefaultButton(QtGui.QMessageBox.Yes)

  @QtCore.pyqtSlot() #shouldnt be needed with my current knowledge
  def zuNehm(self):
    print("TestNehm")
    response = self.confirm.exec_()
    if response == 16384: #seems to be the return value for "Yes"
      window.SB_Position.setValue(float(1))

  @QtCore.pyqtSlot() #shouldnt be needed aswell with my current knowledge
  def zuTast(self):
    print("TestTast")
    response = self.confirm.exec_()
    if response == 16384:
      window.SB_Position.setValue(float(2))


def main():
  global window   #I dont like declaring it globally , is there a better way
  window = MainWindow()

  window.show()
  sys.exit(app.exec_())

if __name__ == '__main__':
  main()

There is no exeption thrown and the prints don't show up in the console aswell.


Solution

  • The signal connections don't work because you are not keeping a reference to the instance of MotorClass you created. When it gets garbage-collected, the signal connections are automatically removed.

    The code below fixes this problem, as well as some other problems with the MotorClass (e.g. global references to the window variable).

    class MainWindow(QtGui.QMainWindow, Ui_W_Setup):
      def __init__(self):
        QtGui.QMainWindow.__init__(self)
        self.setupUi(self)
        self.motor = MotorClass(self)
        self.B_ZuNehm.clicked.connect(self.motor.zuNehm)
        self.B_ZuTast.clicked.connect(self.motor.zuTast)
    
    class MotorClass(QtCore.QObject):
      def __init__(self, parent):
        super(MotorClass, self).__init__(parent)
        self.confirm = QtGui.QMessageBox()
        self.confirm.setText('Verfahrweg frei?')
        self.confirm.setStandardButtons(QtGui.QMessageBox.No | QtGui.QMessageBox.Yes)
        self.confirm.setDefaultButton(QtGui.QMessageBox.Yes)
    
      def zuNehm(self):
        print("TestNehm")
        response = self.confirm.exec_()
        if response == QtGui.QMessageBox.Yes:
          self.parent().SB_Position.setValue(float(1))
    
      def zuTast(self):
        print("TestTast")
        response = self.confirm.exec_()
        if response == QtGui.QMessageBox.Yes:
          self.parent().SB_Position.setValue(float(2))
    
    def main():
      app = QtGui.QApplication(sys.argv)
      window = MainWindow()
      window.show()
      sys.exit(app.exec_())