PyQt5 interface unresponsive when waiting too long

1.1k Views Asked by At

I am working on a GUI in python 3.5 with PyQt5 for a small chat bot. The problem i have is that the pre-processing, post-processing and brain are taking too much time to give back the answer for the user provided input.

The GUI is very simple and looks like this: http://prntscr.com/dsxa39 it loads very fast without connecting it to other modules. I mention that using sleep before receiving answer from brain module will still make it unresponsive.

self.conversationBox.append("You: "+self.textbox.toPlainText()) self.textbox.setText("") time.sleep(20) self.conversationBox.append("Chatbot: " + "message from chatbot")

this is a small sample of code, the one that i need to fix.

And this is the error I encounter: http://prnt.sc/dsxcqu

I mention that I've searched for the solution already and everywhere I've found what I've already tried, to use sleep. But again, this won't work as it makes the program unresponsive too.

2

There are 2 best solutions below

3
On BEST ANSWER

Slow functions, such as sleep, will always block unless they are running asynchronously in another thread.

If you want to avoid threads a workaround is to break up the slow function. In your case it might look like:

for _ in range(20):
    sleep(1)
    self.app.processEvents()

where self.app is a reference to your QApplication instance. This solution is a little hacky as it will simply result in 20 short hangs instead of one long hang.

If you want to use this approach for your brain function then you'll need it to break it up in a similar manner. Beyond that you'll need to use a threaded approach.

0
On
import sys
from PyQt5 import QtCore, QtGui
from PyQt5.QtWidgets import QMainWindow, QGridLayout, QLabel, QApplication, QWidget, QTextBrowser, QTextEdit, \
    QPushButton, QAction, QLineEdit, QMessageBox
from PyQt5.QtGui import QPalette, QIcon, QColor, QFont
from PyQt5.QtCore import pyqtSlot, Qt

import threading
import time

textboxValue = ""
FinalAnsw = ""

class myThread (threading.Thread):
    print ("Start")
    def __init__(self):
        threading.Thread.__init__(self)


    def run(self):
        def getAnswer(unString):
            #do brain here
            time.sleep(10)
            return unString

        global textboxValue
        global FinalAnsw
        FinalAnsw = getAnswer(textboxValue)

class App(QWidget):
    def __init__(self):
        super().__init__()
        self.title = 'ChatBot'
        self.left = 40
        self.top = 40
        self.width = 650
        self.height = 600
        self.initUI()

    def initUI(self):
        self.setWindowTitle(self.title)
        self.setGeometry(self.left, self.top, self.width, self.height)

        pal = QPalette();
        pal.setColor(QPalette.Background, QColor(40, 40, 40));
        self.setAutoFillBackground(True);
        self.setPalette(pal);

        font = QtGui.QFont()
        font.setFamily("FreeMono")
        font.setBold(True)
        font.setPixelSize(15)

        self.setStyleSheet("QTextEdit {color:#3d3838; font-size:12px; font-weight: bold}")

        historylabel = QLabel('View your conversation history here: ')
        historylabel.setStyleSheet('color: #82ecf9')
        historylabel.setFont(font)
        messagelabel = QLabel('Enter you message to the chat bot here:')
        messagelabel.setStyleSheet('color: #82ecf9')
        messagelabel.setFont(font)

        self.conversationBox = QTextBrowser(self)

        self.textbox = QTextEdit(self)

        self.button = QPushButton('Send message', self)
        self.button.setStyleSheet(
            "QPushButton { background-color:#82ecf9; color: #3d3838 }" "QPushButton:pressed { background-color: black }")

        grid = QGridLayout()
        grid.setSpacing(10)
        self.setLayout(grid)
        grid.addWidget(historylabel, 1, 0)
        grid.addWidget(self.conversationBox, 2, 0)
        grid.addWidget(messagelabel, 3, 0)
        grid.addWidget(self.textbox, 4, 0)
        grid.addWidget(self.button, 5, 0)

        # connect button to function on_click
        self.button.clicked.connect(self.on_click)
        self.show()

    def on_click(self):
        global textboxValue
        textboxValue = self.textbox.toPlainText()
        self.conversationBox.append("You: " + textboxValue)
        th = myThread()
        th.start()
        th.join()
        global FinalAnsw
        self.conversationBox.append("Rocket: " + FinalAnsw)
        self.textbox.setText("")



if __name__ == '__main__':
    app = QApplication(sys.argv)
    ex = App()
    app.exec_()

So creating a simple thread solved the problem, the code above will still freeze because of the sleep function call, but if you replace that with a normal function that lasts long it won't freeze anymore. It was tested by the brain module of my project with their functions.

For a simple example of building a thread use https://www.tutorialspoint.com/python/python_multithreading.htm

and for the PyQt GUI I've used examples from this website to learn http://zetcode.com/gui/pyqt5/