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.
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_())