1

Im trying to access GUI Elements from a QThread, but without success so far. im using this example because i want to find an easy and clean way to access the elements, help?

class UI(QMainWindow):
    def __init__(self):
        super(UI, self).__init__()


        uic.loadUi("GUI.ui", self)
        self.thread = {}

        self.listbox_devices = self.findChild(QListWidget, "listbox_devices")

        self.btn_start = self.findChild(QPushButton, "btn_start")
        self.btn_start.clicked.connect(self.Worker_Registration)

   def Worker_Registration(self):
    self.thread = ThreadClass(parent=None)
    self.thread.start()


class ThreadClass(QtCore.QThread):

    any_signal = QtCore.pyqtSignal(int)

    def __init__(self,parent=None):
        super(ThreadClass, self).__init__(parent)

        self.is_running = True
    def run(self):
        devices = UI.listbox_devices.count()
        if not devices > 0: UI.textbox_log.append("No Device Connected"); return

app = QApplication(sys.argv)
UIWindow = UI()
UIWindow.show()
app.exec_()

I get this error:

    py", line 149, in run
    devices = UI.listbox_devices.count()
AttributeError: type object 'UI' has no attribute 'listbox_devices'
Xox Xox
  • 23
  • 2
  • 1
    UI elements are ***NOT*** thread safe. In almost *any* ui framework, not just Qt. You should never directly access them, and **especially** to set their properties (like you're attempting with `textbox_log.append()`). It *may* work right now, just because the current code is quite simple, but it **will** fail: in the best case, you'll only get graphics issues in the widget or some error in the debug log, but such things could even make the program just crash. The ***ONLY***, proper, safe, easy and correct way to access widgets from external threads in Qt is to use *signals*. – musicamante Sep 01 '22 at 08:02

1 Answers1

1

using signals to communicate with the main thread

class UI(QMainWindow):
    def __init__(self):
        super(UI, self).__init__()


        uic.loadUi("GUI.ui", self)
        self.thread = {}

        self.listbox_devices = self.findChild(QListWidget, "listbox_devices")

        self.btn_start = self.findChild(QPushButton, "btn_start")
        self.btn_start.clicked.connect(self.Worker_Registration)

   def Worker_Registration(self):
       self.thread = ThreadClass(parent=None)
       number_devices = self.listbox_devices.count()
       self.thread.any_signal.connect(number_devices, self.append_log)
       self.thread.start()
    
   def append_log(self, value):
       if value == 0: 
           self.textbox_log.append("No Device Connected")


class ThreadClass(QtCore.QThread):

    any_signal = QtCore.pyqtSignal(int)

    def __init__(self, number_devices,  parent=None):
        super(ThreadClass, self).__init__(parent)
        self.number_devices = number_devices
        self.is_running = True
    def run(self):
        if not self.number_devices > 0: self.any_signal.emit(0)
Lucas M. Uriarte
  • 2,403
  • 5
  • 19
  • This answer is very bad advice. Qt does not support gui operations of any kind outside the main thread. Sooner or later, code like this will crash because it calls methods which are *not thread-safe*. You should always use signals and slots to communicate between threads. – ekhumoro Sep 04 '22 at 11:05
  • @ekhumoro thanks for the comment always good to learn. I have update the answer base on your advice – Lucas M. Uriarte Sep 04 '22 at 15:29