Concerned about race conditions while accessing SQLite3 database connection that's accessed in thread invoked by Pynput listener inside a QThread

前端 未结 1 1913
青春惊慌失措
青春惊慌失措 2021-01-24 23:56

I\'m writing a Windows application with Pyside2. Due to the nature of how I\'m using multithreading, I\'m having to interact with the same Sqlite3 database in multiple threads.

1条回答
  •  抹茶落季
    2021-01-25 00:59

    First of all a QThread is not a Qt thread, that is, it is not a new type of thread, QThread is a class that manages the native threads of each platform. so the thread that handles QThread has the same characteristics of threading.Thread.

    On the other hand the goal of using threads in a GUI is not to block the main thread called GUI thread, in your pynput it already has its thread so there would be no problems. The other task that is blocking is that of the OCR, so we must execute it in a new thread. The task of the database is not expensive, so it is not necessary to create a thread.

    keymonitor.py

    from pynput import keyboard
    import time
    from PySide2 import QtCore
    
    class KeyMonitor(QtCore.QObject):
        letterPressed = QtCore.Signal(str)
    
        def __init__(self, parent=None):
            super().__init__(parent)
            self.listener = keyboard.Listener(on_release = self.on_release)
    
        def on_release(self,key):
            if type(key) == keyboard._win32.KeyCode:
                self.letterPressed.emit(key.char.lower())
    
        def stop_monitoring(self):
            self.listener.stop()
    
        def start_monitoring(self):
            self.listener.start()
    

    imageprocess.py

    import uuid
    import pytesseract
    
    from PIL import ImageGrab
    
    from PySide2 import QtCore
    
    class ProcessWorker(QtCore.QObject):
        processSignal = QtCore.Signal(str, str)
    
        def doProcess(self):
            screenshot = ImageGrab.grab()
            screenshot_path =  QtCore.QDir.current().absoluteFilePath(uuid.uuid4().hex+".jpg")
            screenshot.save(screenshot_path )
            print("start ocr")
            ocr_string = pytesseract.image_to_string(screenshot)
            print(ocr_string, screenshot_path)
            self.processSignal.emit(ocr_string, screenshot_path)
            self.thread().quit()
    

    main.py

    from keymonitor import KeyMonitor
    from imageprocess import ProcessWorker
    from PySide2 import QtCore, QtWidgets
    
    import sqlite3
    
    class Widget(QtWidgets.QWidget):
        def __init__(self, parent=None):
            super().__init__(parent)
    
            self.last_letter = ""
            self.current_letter = ""
    
            lay = QtWidgets.QVBoxLayout(self)
            button = QtWidgets.QPushButton("Start")
            button.clicked.connect(self.onClicked)
            lay.addWidget(button)
    
            self.keymonitor = KeyMonitor()
            self.keymonitor.letterPressed.connect(self.onLetterPressed)
    
            self.db_connection = sqlite3.connect("testdababase.db")
            self.cursor = self.db_connection.cursor()
            self.cursor.execute("CREATE TABLE IF NOT EXISTS testdb (OCRstring text, filepath text)")
            self.threads = []
    
        def onClicked(self):
            self.keymonitor.start_monitoring()
    
        def onLetterPressed(self, letter):
            if self.last_letter:
                if self.current_letter:
                    self.last_letter = self.current_letter
                self.current_letter = letter
            else:
                self.last_letter = letter
    
            if self.last_letter == "j" and self.current_letter == "k":
                print("j+k")
                self.start_processing()
    
        def start_processing(self):
            thread = QtCore.QThread()
            self.worker = ProcessWorker()
            self.worker.processSignal.connect(self.onProcessSignal)
            self.worker.moveToThread(thread)
            thread.started.connect(self.worker.doProcess)
            thread.finished.connect(self.worker.deleteLater)
            thread.finished.connect(lambda th=thread: self.threads.remove(th))
            thread.start()
            self.threads.append(thread)
    
        def onProcessSignal(self, ocr, path):
            print(ocr, path)
            self.cursor.execute("INSERT INTO testdb (OCRstring, filepath) VALUES (?,?)",(ocr, path))
            self.db_connection.commit()
    
    if __name__ == "__main__":
        import sys
        app = QtWidgets.QApplication(sys.argv)
        w = Widget()
        w.show()
        sys.exit(app.exec_())
    

    0 讨论(0)
提交回复
热议问题