Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. Qt for Python
  4. [PySide2] Connecting a signal to setValue does not work when Loading UI through QUiLoader
Forum Updated to NodeBB v4.3 + New Features

[PySide2] Connecting a signal to setValue does not work when Loading UI through QUiLoader

Scheduled Pinned Locked Moved Solved Qt for Python
2 Posts 3 Posters 1.7k Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • T Offline
    T Offline
    tyde
    wrote on last edited by tyde
    #1

    Hi, I am facing the following problem: I am loading a UI designed with Qt Designer in PySide and now want to attach some signals to the ui to interact with the data model. Therefore I created a Signal that I am connecting with a setValue function of a QDoubleSpinBox. But everytime I emit some data into the signal, it does not react and the value of the QDoubleSpinBox. In the search for a cause for this unclear behavior, I also created another QDoubleSpinBox manually and also connected the signal to it. Here the value gets changed. The example code is here:

    import sys
    import time
    from PySide2 import QtGui, QtCore, QtWidgets
    
    import pyqtgraph as pg
    import numpy as np
    
    from PySide2.QtCore import *
    from PySide2.QtUiTools import QUiLoader
    from PySide2.QtWidgets import QMainWindow, QDoubleSpinBox
    
    
    class PressureViewModel(QObject):
        _current_pressure = 0.0
        _current_pressure_sig = Signal(object)
    
        def __init__(self,parent):
            QtCore.QObject.__init__(self)
            ui_file = QFile("scratch.ui")
            ui_file.open(QFile.ReadOnly)
            loader = QUiLoader()
            window = loader.load(ui_file,parent)
            self.sb = QDoubleSpinBox(parent)
            window.formLayout.setWidget(1, QtWidgets.QFormLayout.FieldRole, self.sb)
            self.ui = window
    
    
    
        def initialize_signals(self):
            self._current_pressure_sig.connect(self.ui.current_pressure_spin_box.setValue) #does not seem to work
            self._current_pressure_sig.connect(self.sb.setValue) #works
    
    
        @property
        def current_pressure(self):
            return self._current_pressure
    
        @current_pressure.setter
        def current_pressure(self ,value):
            print("Setter called")
            self._current_pressure_sig.emit(value)
            self._current_pressure = value
    
    
    class PressureController:
        def __init__(self, view_model:PressureViewModel):
            self.view_model = view_model
    
        def cycle_controller(self):
            for i in range(20):
                print(self.view_model.current_pressure)
                time.sleep(0.1)
                self.view_model.current_pressure = np.random.normal(loc=10,scale=2)
            return 0.0
    
    
    
    class Worker(QThread):
    
    
        def __init__(self, controller:PressureController):
            QThread.__init__(self)
            self.controller = controller
    
        def run(self):
            while True:
                self.controller.cycle_controller()
    
    
    app = QtGui.QApplication([])
    
    window = QMainWindow()
    pVM = PressureViewModel(window)
    pC = PressureController(pVM)
    
    window.setCentralWidget(pVM.ui)
    pVM.initialize_signals()
    window.show()
    
    worker = Worker(pC)
    worker.start()
    
    sys.exit(app.exec_())
    

    The ui file is the following:

    <?xml version="1.0" encoding="UTF-8"?>
    <ui version="4.0">
     <class>Form</class>
     <widget class="QWidget" name="Form">
      <property name="geometry">
       <rect>
        <x>0</x>
        <y>0</y>
        <width>400</width>
        <height>300</height>
       </rect>
      </property>
      <property name="windowTitle">
       <string>Form</string>
      </property>
      <layout class="QFormLayout" name="formLayout">
       <item row="0" column="0">
        <widget class="QLabel" name="current_pressure_label">
         <property name="text">
          <string>Current Pressure</string>
         </property>
        </widget>
       </item>
       <item row="0" column="1">
        <widget class="QDoubleSpinBox" name="current_pressure_spin_box"/>
       </item>
      </layout>
     </widget>
     <resources/>
     <connections/>
    </ui>
    

    I have the feeling that I somehow "loose connection" to the proper ui elements and am really not connecting the signal to the correct function. Does anybody has an idea where my error could be?

    1 Reply Last reply
    0
    • jazzycamelJ Offline
      jazzycamelJ Offline
      jazzycamel
      wrote on last edited by
      #2

      Hi @tyde,

      I think your issue may be to do with threading. Generally speaking, it's better not to sub-class QThread, but to move objects into a QThread. Also, I think you are making direct calls between threads when you need to use the Signal/Slot mechanism to communicate.

      The following example uses your UI file and groups the functionality together a little. The PressureController is now passed in to a QThread owned by the QMainWindow that also constructs the UI. Everything is connected together via signals and slots.

      import time
      
      from PySide2.QtCore import QFile, QObject, qApp, QThread, Signal, Slot, Property
      from PySide2.QtWidgets import QMainWindow
      from PySide2.QtUiTools import QUiLoader
      import numpy as np
      
      
      class PressureController(QObject):
          currentPressure = Signal(float)
          stopped = Signal()
      
          def __init__(self):
              super().__init__()
              self._looping = False
      
          @Slot()
          def run(self):
              self._looping = True
              try:
                  while self._looping:
                      self.currentPressure.emit(np.random.normal(loc=10, scale=2))
                      qApp.processEvents()
                      time.sleep(0.1)
              except Exception as e:
                  print(e)
      
              self.stopped.emit()
      
          @Slot()
          def stop(self):
              self._looping = False
      
      
      class PressureViewModel(QMainWindow):
          stopThread = Signal()
      
          def __init__(self, parent=None, **kwargs):
              super().__init__(parent, **kwargs)
      
              self._currentPressure = 0.0
      
              ui_file = QFile("./scratch.ui")
              ui_file.open(QFile.ReadOnly)
              ui_loader = QUiLoader()
              self.ui = ui_loader.load(ui_file, parent)
              self.setCentralWidget(self.ui)
      
              self.pressureController = PressureController()
              self.pressureController.currentPressure.connect(self.setCurrentPressure)
              self.thread = QThread()
              self.thread.started.connect(self.pressureController.run)
              self.pressureController.stopped.connect(self.thread.quit)
              self.pressureController.moveToThread(self.thread)
              self.stopThread.connect(self.pressureController.stop)
              self.thread.start()
      
          def readCurrentPressure(self):
              return self._currentPressure
      
          @Slot(float)
          def setCurrentPressure(self, pressure):
              if self._currentPressure == pressure:
                  return
              self._currentPressure = pressure
      
              self.ui.current_pressure_spin_box.setValue(pressure)
      
          currentPressure = Property(float, readCurrentPressure, setCurrentPressure)
      
          def closeEvent(self, event):
              # This stops the thread when the window is closed, otherwise the program 
              # technically crashes because the thread it orphaned.
              if self.thread.isRunning():
                  self.stopThread.emit()
      
                  while not self.thread.isFinished():
                      qApp.processEvents()
      
              event.accept()
      
      
      if __name__ == "__main__":
          from sys import argv, exit
          from PySide2.QtCore import QCoreApplication, Qt
          from PySide2.QtWidgets import QApplication
      
          QCoreApplication.setAttribute(Qt.AA_ShareOpenGLContexts)
          a = QApplication(argv)
          pvm = PressureViewModel()
          pvm.show()
          exit(a.exec_())
      
      

      Please feel free to ask questions about anything that I haven't explained.

      Hope this helps :o)

      For the avoidance of doubt:

      1. All my code samples (C++ or Python) are tested before posting
      2. As of 23/03/20, my Python code is formatted to PEP-8 standards using black from the PSF (https://github.com/psf/black)
      1 Reply Last reply
      1

      • Login

      • Login or register to search.
      • First post
        Last post
      0
      • Categories
      • Recent
      • Tags
      • Popular
      • Users
      • Groups
      • Search
      • Get Qt Extensions
      • Unsolved