halflings
halflings

Reputation: 1538

Qt : Updating UI (with signals) from another class and thread

I'm developping a simple UI for a TCP Server, and need to update the UI with log messages sent from the server, but the problem is that any connection to that server is in a separate thread, so I can't touch directly the UI's widgets (as they are not reentrant).

I've read some articles on the subject and chose what seems like the most elegant (and easy) way to do it: signals.

I've done the following :

class Ui_Dialog(QtCore.QObject):

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

    def setupUi(self, Dialog):        
        Dialog.setObjectName(_fromUtf8("Dialog"))
        Dialog.resize(800, 580)
        self.serverLog = QtGui.QTextEdit(Dialog)
        self.serverLog.setGeometry(QtCore.QRect(0, 0, 800, 480))
        self.serverLog.setObjectName(_fromUtf8("serverLog"))
        #OTHER WIDGETS AND CODE DEFINED HERE

        self.server = None

        #SIGNALS
        self.clearLogButton.clicked.connect(self.serverLog.clear)
        self.stopButton.clicked.connect(self.connectServer)
        self.startButton.clicked.connect(self.connectServer)

    def connectServer(self):
        if (self.server is None):

            host = str(self.ipEdit.text())
            port = int(self.portEdit.text())

            try:
                self.server = server.MainServer((host, port), server.MyHTTPHandler)
                self.server.setup(self)

                self.printLog( "Hooray ! Server connected at {}:{}".format(host, port) )

                # Start a thread with the server -- that thread will then start one more thread for each request
                MainServer_thread = threading.Thread(target=self.server.serve_forever)
                MainServer_thread.daemon = True
                self.server.trigger.connect(QtCore.QObject(self.printLog))
                MainServer_thread.start()         

                self.server.trigger.connect(self.printLog)

            except errno.EADDRINUSE:
                self.printLog("#ERROR: Address already in use. Try a different port.")
            except errno.EADDRNOTAVAIL:
                self.printLog("#ERROR: Address not available.")


    def printLog(self, string):
        self.serverLog.append(_fromUtf8(string))

And in the server class:

class MainServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer):
    trigger = QtCore.pyqtSignal(str)

    def printLog(self, message):
        print message
        self.trigger.emit(message)

But when I run my app, I get this message error :

TypeError: pyqtSignal must be bound to a QObject, not 'instance'

If there's any other (easy) way to update the UI from another thread (and class) or you know how to fix this error, please let me know !

Upvotes: 2

Views: 2325

Answers (2)

halflings
halflings

Reputation: 1538

Just solved it using a medium object (of class QObject) contained in my server class:

class Logger(QtCore.QObject):

    trigger = QtCore.pyqtSignal(str)
    def __init__(self):
        super(Logger,self).__init__()
    def send(self, string):
        self.trigger.emit(string)

Upvotes: 1

user1006989
user1006989

Reputation:

That's pretty much this:

class Signals(QObject):
    trigger = pyqtSignal(str)

To use them in MainServer:

class MainServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer):
   def __init__(self, parent=None):
       self.signals = Signals()
       self.signals.trigger.emit("triggered")

Upvotes: 3

Related Questions