1

I'm new to PyQt5 and I can't seem to connect my pyqtSignal and pyqtSlot. The error, "TypeError: connect() failed between worker.newIcon[object] and updateIcon()" pops out. Anyone can guide me to the right path please?

from PyQt5.QtWidgets import QApplication, QSystemTrayIcon, QAction, QMenu
from PyQt5.QtGui import QIcon
from PyQt5.QtCore import QObject, QThread, pyqtSignal, pyqtSlot


class worker(QThread):
    newIcon = pyqtSignal(object)

    def run(self):
        while True:
            self.newIcon.emit(QIcon("shield-off.png"))
            QThread.msleep(1000)


class systemTray():
    def start_tray(self):
        self.app = QApplication([])
        self.app.setQuitOnLastWindowClosed(False)

        icon = QIcon("shield-on.png")
        self.tray = QSystemTrayIcon()
        self.tray.setIcon(icon)
        self.tray.setVisible(True)

        self.menu = QMenu()
        self.quit = QAction("Exit")
        self.quit.triggered.connect(self.app.quit)
        self.menu.addAction(self.quit)
        self.tray.setContextMenu(self.menu)

        self.thread = QThread()
        self.worker = worker()
        self.worker.moveToThread(self.thread)
        self.worker.newIcon.connect(self.updateIcon)
        self.thread.started.connect(self.worker.run)
        self.thread.start()

        # Run tray
        self.app.exec_()

    @pyqtSlot(object)
    def updateIcon(self, icon):
        self.tray.setIcon(icon)


if __name__ == "__main__":
    be_tray = systemTray()
    be_tray.start_tray()
riazufila
  • 17
  • 1
  • 4
  • Please do not use external websites to share code, embed it in your question instead (read how to correctly [format your code](https://meta.stackoverflow.com/a/251362) and check the syntax and indentetion in the preview before saving changes). – musicamante Jan 27 '21 at 10:53
  • I am working on the answer: Do you really need two different threads and join them together? What is your goal with this application? – Jakub Szlaur Jan 27 '21 at 13:20
  • The worker thread in this example is just an overcomplicated way of implementing a timer. Also, since QIcon is pixmap-based, it doesn't seem to be thread safe. A solution using QTimer would be much simpler, safer and more efficient. – ekhumoro Jan 27 '21 at 21:07

1 Answers1

3

The pyqtSlot decorator is intended to be used for QObject subclasses, otherwise it will not work correctly.

So, the solution is pretty simple, as you only need to inherit from QObject:

class systemTray(QObject):
    def start_tray(self):
        # ...

On the other hand, using a pyqtSlot decorator is rarely needed and only for specific situations (see this related answer. In your case, it doesn't seem necessary.

Also, since you're already subclassing from QThread, you can just use its own start(), without using moveToThread() on a new one.

musicamante
  • 41,230
  • 6
  • 33
  • 58