Error with python program with pyqt5 and sqlite, Cannot create children for a parent that is in a different thread

I make a program using pyqt5 and sqlite db.

it's randomly select a db's row text and display in textbrowser widget and copy to clipboard each time i pushed a button.

and with keyboard module, hooking the ctrl+v then check a label widget for repeat previous pushed button's function.

But when i push ctrl+v, it's not working in Pycharm with this error message

Process finished with exit code -1073740791 (0xC0000409)

And when i run the code in terminal, again with this error message

QObject: Cannot create children for a parent that is in a different thread.
(Parent is QTextDocument(0x207618a6770), parent's thread is QThread(0x2075f5acef0), current thread is QThread(0x20761d818d0)

i think maybe a thread problem. but i didn't use thread. It's my pyqt5 widget's ui image and my source code.

How can i make this code working?

ui image

import sys
from PyQt5.QtWidgets import *
from PyQt5 import uic
import sqlite3
import random
import pyperclip
import keyboard
from time import sleep

form_class = uic.loadUiType("exam.ui")[0]

class WindowClass(QMainWindow, form_class) :
    def __init__(self) :
        super().__init__()
        self.setupUi(self)
        self.pb1.clicked.connect(self.bt1)
        self.pb2.clicked.connect(self.bt2)
        self.pb3.clicked.connect(self.bt3)
        self.pb4.clicked.connect(self.bt4)
        self.pb5.clicked.connect(self.bt5)
        self.con = sqlite3.connect("exam.db")
        self.cursor = self.con.cursor()
        keyboard.add_hotkey("ctrl+v", self.kt)

    def bt1(self):
        self.lb1.clear()
        self.tb.clear()
        self.lb1.setText("a")
        self.cursor.execute("SELECT max(rowid) FROM a")
        maxrow = self.cursor.fetchone()
        r = random.randrange(1, maxrow[0] + 1)
        self.cursor.execute("SELECT * FROM a WHERE rowid =?", (r,))
        t = self.cursor.fetchone()
        self.tb.setPlainText(t[0])
        pyperclip.copy(t[0] + "\n")

    def bt2(self):
        self.lb1.clear()
        self.tb.clear()
        self.lb1.setText("b")
        self.cursor.execute("SELECT max(rowid) FROM b")
        maxrow = self.cursor.fetchone()
        r = random.randrange(1, maxrow[0] + 1)
        self.cursor.execute("SELECT * FROM b WHERE rowid =?", (r,))
        t = self.cursor.fetchone()
        self.tb.setPlainText(t[0])
        pyperclip.copy(t[0] + "\n")

    def bt3(self):
        self.lb1.clear()
        self.tb.clear()
        self.lb1.setText("c")
        self.cursor.execute("SELECT max(rowid) FROM c")
        maxrow = self.cursor.fetchone()
        r = random.randrange(1, maxrow[0] + 1)
        self.cursor.execute("SELECT * FROM c WHERE rowid =?", (r,))
        t = self.cursor.fetchone()
        self.tb.setPlainText(t[0])
        pyperclip.copy(t[0] + "\n")

    def bt4(self):
        self.lb1.clear()
        self.tb.clear()
        self.lb1.setText("d")
        self.cursor.execute("SELECT max(rowid) FROM d")
        maxrow = self.cursor.fetchone()
        r = random.randrange(1, maxrow[0] + 1)
        self.cursor.execute("SELECT * FROM d WHERE rowid =?", (r,))
        t = self.cursor.fetchone()
        self.tb.setPlainText(t[0])
        pyperclip.copy(t[0] + "\n")

    def bt5(self):
        self.lb1.clear()
        self.tb.clear()
        self.lb1.setText("e")
        self.cursor.execute("SELECT max(rowid) FROM e")
        maxrow = self.cursor.fetchone()
        r = random.randrange(1, maxrow[0] + 1)
        self.cursor.execute("SELECT * FROM e WHERE rowid =?", (r,))
        t = self.cursor.fetchone()
        self.tb.setPlainText(t[0])
        pyperclip.copy(t[0] + "\n")

    def kt(self):
        if self.lb1.text() == "a":
            self.bt1()
        elif self.lb1.text == "b":
            self.bt2()
        elif self.lb1.text == "c":
            self.bt3()
        elif self.lb1.text == "d":
            self.bt4()
        elif self.lb1.text == "e":
            self.bt5()
    if __name__ == "__main__" :
        app = QApplication(sys.argv)
        myWindow = WindowClass()
        myWindow.show()
        app.exec_()

Db file link ui file link

i just add a db and ui file.

1 answer

  • answered 2020-03-26 00:03 eyllanesc

    The callback associated with the shorcut registered with add_hotkey is executed in a secondary thread, and that implies that you are modifying the GUI from another thread which is prohibited by Qt throwing that error. The solution is to use a signal that is emitted from the secondary thread to invoke the kt method:

    # ...
    
    from PyQt5.QtCore import QObject, pyqtSignal
    
    
    class KeyboardManager(QObject):
        pasteSignal = pyqtSignal()
    
        def start(self):
            keyboard.add_hotkey("ctrl+v", self._ctrl_v_callback)
    
        def _ctrl_v_callback(self):
            self.pasteSignal.emit()
    
    
    class WindowClass(QMainWindow, form_class):
        def __init__(self):
            super().__init__()
            self.setupUi(self)
            self.pb2.clicked.connect(self.bt2)
            self.pb3.clicked.connect(self.bt3)
            self.pb4.clicked.connect(self.bt4)
            self.pb5.clicked.connect(self.bt5)
            self.con = sqlite3.connect("exam.db")
            self.cursor = self.con.cursor()
    
            keyboard_manager = KeyboardManager(self)
            keyboard_manager.pasteSignal.connect(self.kt)
            keyboard_manager.start()
    
        def bt1(self):
            # ...

    In addition to the previous error, you also have a trivial error: if you want to get the text, you must use the text() method with parentheses, but you don't do it correctly, the solution is as follows:

    def kt(self):
        if self.lb1.text() == "a":
            self.bt1()
        elif self.lb1.text() == "b":
            self.bt2()
        elif self.lb1.text() == "c":
            self.bt3()
        elif self.lb1.text() == "d":
            self.bt4()
        elif self.lb1.text() == "e":
            self.bt5()