1

Based on code from here and here I made my first steps with multithreading in PyQt5. After starting the program, the graphical user interface appears, but after several seconds it crashes with the following error

Process finished with exit code -1073740791 (0xC0000409)

After googling the error message, I am relatively sure that something is wrong with multithreading. Unfortunately I don't know what and how the error can be fixed.

Code

import sys
from datetime import datetime

import wmi

from PyQt5. QtCore import QObject, QRunnable, QThreadPool, pyqtSignal, pyqtSlot
from PyQt5.QtWidgets import QApplication, QMainWindow, QTableWidget, QTableWidgetItem, QHeaderView


class KeyboardDetectorSignals(QObject):
    keyboard_changed = pyqtSignal(str)


class KeyboardDetector(QRunnable):

    def __init__(self):
        super().__init__()

        self.signals = KeyboardDetectorSignals()

    def run(self):

        device_connected_wql = "SELECT * FROM __InstanceCreationEvent WITHIN 2 WHERE TargetInstance ISA \'Win32_Keyboard\'"
        device_disconnected_wql = "SELECT * FROM __InstanceDeletionEvent WITHIN 2 WHERE TargetInstance ISA \'Win32_Keyboard\'"

        c = wmi.WMI()
        connected_watcher = c.watch_for(raw_wql=device_connected_wql)
        disconnected_watcher = c.watch_for(raw_wql=device_disconnected_wql)

        while True:
            try:
                connected = connected_watcher(timeout_ms=10)
            except wmi.x_wmi_timed_out:
                pass
            else:
                if connected:
                    self.signals.keyboard_changed.emit("Keyboard connected.")

            try:
                disconnected = disconnected_watcher(timeout_ms=10)
            except wmi.x_wmi_timed_out:
                pass
            else:
                if disconnected:
                    self.signals.keyboard_changed.emit("Keyboard disconnected.")


class MainWindow(QMainWindow):

    def __init__(self):
        super().__init__()

        self.setGeometry(100, 100, 500, 500)
        self.setWindowTitle("Keyboard Logger")

        self.log_table = QTableWidget()
        self.log_table.setColumnCount(2)
        self.log_table.setShowGrid(True)
        self.log_table.setHorizontalHeaderLabels(["Time", "Event"])
        self.log_table.horizontalHeader().setStretchLastSection(True)
        self.log_table.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeToContents)
        self.setCentralWidget(self.log_table)
        self.show()

        self.threadpool = QThreadPool()
        keyboard_detector = KeyboardDetector()
        keyboard_detector.signals.keyboard_changed.connect(self.add_row)
        self.threadpool.start(keyboard_detector)

    def add_row(self, event: str):
        now = datetime.now()
        datetime_string = now.strftime("%Y-%m-%d %H:%M:%S")

        row_count = self.log_table.rowCount()
        self.log_table.insertRow(row_count)
        self.log_table.setItem(row_count, 0, QTableWidgetItem(datetime_string))
        self.log_table.setItem(row_count, 1, QTableWidgetItem(event))


def main():
    app = QApplication(sys.argv)
    window = MainWindow()
    sys.exit(app.exec_())


if __name__ == '__main__':
    main()

Update 1: Removed @pyqtSlot().

Got the following error when running the script from the CMD:

Traceback (most recent call last):
  File "c:\users\ata\pycharmprojects\test\src\wmi\wmi.py", line 1340, in connect
    obj = GetObject(moniker)
  File "C:\Users\ata\AppData\Local\Programs\Python\Python37\lib\site-packages\win32com\client\__init__.py", line 72, in GetObject
    return Moniker(Pathname, clsctx)
  File "C:\Users\ata\AppData\Local\Programs\Python\Python37\lib\site-packages\win32com\client\__init__.py", line 87, in Moniker
    moniker, i, bindCtx = pythoncom.MkParseDisplayName(Pathname)
pywintypes.com_error: (-2147221020, 'Ung³ltige Syntax', None, None)

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "c:\users\ata\pycharmprojects\test\src\wmi\wmi.py", line 1354, in connect
    handle_com_error()
  File "c:\users\ata\pycharmprojects\test\src\wmi\wmi.py", line 258, in handle_com_error
    raise klass(com_error=err)
wmi.x_wmi_uninitialised_thread: <x_wmi: Unexpected COM Error (-2147221020, 'Ung³ltige Syntax', None, None)>

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "main.py", line 26, in run
    c = wmi.WMI()
  File "c:\users\ata\pycharmprojects\test\src\wmi\wmi.py", line 1357, in connect
    raise x_wmi_uninitialised_thread("WMI returned a syntax error: you're probably running inside a thread without first calling pythoncom.CoInitialize[Ex]")
wmi.x_wmi_uninitialised_thread: <x_wmi: WMI returned a syntax error: you're probably running inside a thread without first calling pythoncom.CoInitialize[Ex] (no underlying exception)>
PS C:\Users\ata\PycharmProjects\Snippets\KeyboardLogger>
Atalanttore
  • 349
  • 5
  • 22

1 Answers1

1

PyQt is thread-safe to the same extent that Qt is thread-safe. The Qt docs will tell you which parts of their API are guaranteed to be so, and under what circumstances.

Cross-thread signals are thread-safe.

A thread-safe way of writing this would be to use a mutex

Divyessh
  • 2,540
  • 1
  • 7
  • 24