0

I'm trying to display some data by using QTextBrowser. However it freezes because of I dont use threading in a right way. I searched some QThread questions on stackoverflow, but could'nt figure out why my code is not working...

I want to print "Hello" five times with sleeping 1 sec in every process

I also want to get input with QLineEdit to set time.sleep(cooldown), but i can do it after I solve my Qthread problem.

The solutions that i searched and tried to implement to my code are;

PyQt5: Update labels inrun time

PyQt5: Updating Label?

probably, i used the wrong implementation method. But im a newbie, please help me with that

# -*- coding: utf-8 -*-

# Form implementation generated from reading ui file 'denn.ui'
#
# Created by: PyQt5 UI code generator 5.12.3
#
# WARNING! All changes made in this file will be lost!

from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
from PyQt5 import QtCore, QtGui, QtWidgets
from time import *

class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(800, 600)
        MainWindow.setStyleSheet("")
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.verticalLayout = QtWidgets.QVBoxLayout(self.centralwidget)
        self.verticalLayout.setObjectName("verticalLayout")
        self.label = QtWidgets.QLabel(self.centralwidget)
        font = QtGui.QFont()
        font.setFamily("Open Sans")
        font.setPointSize(20)
        self.label.setFont(font)
        self.label.setAlignment(QtCore.Qt.AlignCenter)
        self.label.setObjectName("label")
        self.verticalLayout.addWidget(self.label)
        self.lineEdit = QtWidgets.QLineEdit(self.centralwidget)
        font = QtGui.QFont()
        font.setFamily("Open Sans")
        font.setPointSize(20)
        self.lineEdit.setFont(font)
        self.lineEdit.setAlignment(QtCore.Qt.AlignCenter)
        self.lineEdit.setObjectName("lineEdit")
        self.verticalLayout.addWidget(self.lineEdit)
        self.textBrowser = QtWidgets.QTextBrowser(self.centralwidget)
        font = QtGui.QFont()
        font.setFamily("Open Sans")
        font.setPointSize(20)
        self.textBrowser.setFont(font)
        self.textBrowser.setObjectName("textBrowser")
        self.verticalLayout.addWidget(self.textBrowser)
        self.pushButton = QtWidgets.QPushButton(self.centralwidget)
        font = QtGui.QFont()
        font.setFamily("Open Sans")
        font.setPointSize(20)
        self.pushButton.setFont(font)
        self.pushButton.setStyleSheet("QPushButton {\n"
"    color: white;\n"
"    border: 2px solid #46963f;\n"
"    border-radius: 20px;\n"
"    border-style: outset;\n"
"    /*background: qradialgradient(\n"
"        cx: 0.3, cy: -0.4, fx: 0.3, fy: -0.4,\n"
"        radius: 1.35, stop: 0 #fff, stop: 1 #46963f\n"
"        );*/\n"
"    background:#46963f;\n"
"    padding: 5px;\n"
"    }\n"
"\n"
"QPushButton:hover {\n"
"    border: 2px solid #07121b;\n"
"    /*background: qradialgradient(\n"
"        cx: 0.3, cy: -0.4, fx: 0.3, fy: -0.4,\n"
"        radius: 1.35, stop: 0 #fff, stop: 1 #1b486d\n"
"        );*/\n"
"    background: #1b486d;\n"
"    }\n"
"\n"
"QPushButton:pressed {\n"
"    border: 2px solid #07121b;\n"
"    border-style: inset;\n"
"    /*background: qradialgradient(\n"
"        cx: 0.4, cy: -0.1, fx: 0.4, fy: -0.1,\n"
"        radius: 1.35, stop: 0 #fff, stop: 1 #1b486d\n"
"        );*/\n"
"    background: #1b486d;\n"
"\n"
"    }")
        self.pushButton.setObjectName("pushButton")
        self.verticalLayout.addWidget(self.pushButton)
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 21))
        self.menubar.setObjectName("menubar")
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
        self.label.setText(_translate("MainWindow", "Deneme Programı"))
        self.lineEdit.setPlaceholderText(_translate("MainWindow", "Bekleme Süresi"))
        self.pushButton.setText(_translate("MainWindow", "Başlat"))

        try:
            self.pushButton.clicked.connect(self.x)
        except Exception as e:
            print(e)

    def x(self):
        cd = int(self.lineEdit.text())
        for i in range(5):
            self.thread = DummyThread(self, cd)
            self.thread.start()
            self.thread.finished.connect(self.a)

    def a(self):
        self.textBrowser.setText(self.textBrowser.toPlainText() + "\nHello")

class DummyThread(QThread):
    finished = pyqtSignal()
    def __init__(self,cd):
        super(DummyThread, self).__init__()
        self.cd = cd
    def run(self):
        time.sleep(self.cd)
        self.finished.emit()


if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    MainWindow = QtWidgets.QMainWindow()
    ui = Ui_MainWindow()
    ui.setupUi(MainWindow)
    MainWindow.show()
    sys.exit(app.exec_())

3 Answers3

0

DummyThread does not take any arguments, remove self when making the instance. Since you from time import *, you need to remove time. from time.sleep(1). This results in the following code:

    def x(self):
        self.thread = DummyThread()
        self.thread.start()
        self.thread.finished.connect(self.a)
    def a(self):
        self.textBrowser.setText(self.textBrowser.toPlainText() + "\nHello")

class DummyThread(QThread):
    finished = pyqtSignal()
    def run(self):
        sleep(1)
        self.finished.emit()

p.s. As the file introduction suggests, it's not recommended to add code to the file resulting from PyUIC.

RiVer_Py
  • 19
  • 2
0

The reason why your code is failing is because it is not inheriting from QObject. For QThreads to work the parent must inherit from QObject or derived classes not object.

# -*- coding: utf-8 -*-

# Form implementation generated from reading ui file 'denn.ui'
#
# Created by: PyQt5 UI code generator 5.12.3
#
# WARNING! All changes made in this file will be lost!

from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
from PyQt5 import QtCore, QtGui, QtWidgets
import time


class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(800, 600)
        MainWindow.setStyleSheet("")
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.verticalLayout = QtWidgets.QVBoxLayout(self.centralwidget)
        self.verticalLayout.setObjectName("verticalLayout")
        self.label = QtWidgets.QLabel(self.centralwidget)
        font = QtGui.QFont()
        font.setFamily("Open Sans")
        font.setPointSize(20)
        self.label.setFont(font)
        self.label.setAlignment(QtCore.Qt.AlignCenter)
        self.label.setObjectName("label")
        self.verticalLayout.addWidget(self.label)
        self.lineEdit = QtWidgets.QLineEdit(self.centralwidget)
        font = QtGui.QFont()
        font.setFamily("Open Sans")
        font.setPointSize(20)
        self.lineEdit.setFont(font)
        self.lineEdit.setAlignment(QtCore.Qt.AlignCenter)
        self.lineEdit.setObjectName("lineEdit")
        self.verticalLayout.addWidget(self.lineEdit)
        self.textBrowser = QtWidgets.QTextBrowser(self.centralwidget)
        font = QtGui.QFont()
        font.setFamily("Open Sans")
        font.setPointSize(20)
        self.textBrowser.setFont(font)
        self.textBrowser.setObjectName("textBrowser")
        self.verticalLayout.addWidget(self.textBrowser)
        self.pushButton = QtWidgets.QPushButton(self.centralwidget)
        font = QtGui.QFont()
        font.setFamily("Open Sans")
        font.setPointSize(20)
        self.pushButton.setFont(font)
        self.pushButton.setStyleSheet("QPushButton {\n"
"    color: white;\n"
"    border: 2px solid #46963f;\n"
"    border-radius: 20px;\n"
"    border-style: outset;\n"
"    /*background: qradialgradient(\n"
"        cx: 0.3, cy: -0.4, fx: 0.3, fy: -0.4,\n"
"        radius: 1.35, stop: 0 #fff, stop: 1 #46963f\n"
"        );*/\n"
"    background:#46963f;\n"
"    padding: 5px;\n"
"    }\n"
"\n"
"QPushButton:hover {\n"
"    border: 2px solid #07121b;\n"
"    /*background: qradialgradient(\n"
"        cx: 0.3, cy: -0.4, fx: 0.3, fy: -0.4,\n"
"        radius: 1.35, stop: 0 #fff, stop: 1 #1b486d\n"
"        );*/\n"
"    background: #1b486d;\n"
"    }\n"
"\n"
"QPushButton:pressed {\n"
"    border: 2px solid #07121b;\n"
"    border-style: inset;\n"
"    /*background: qradialgradient(\n"
"        cx: 0.4, cy: -0.1, fx: 0.4, fy: -0.1,\n"
"        radius: 1.35, stop: 0 #fff, stop: 1 #1b486d\n"
"        );*/\n"
"    background: #1b486d;\n"
"\n"
"    }")
        self.pushButton.setObjectName("pushButton")
        self.verticalLayout.addWidget(self.pushButton)
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 21))
        self.menubar.setObjectName("menubar")
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
        self.label.setText(_translate("MainWindow", "Deneme Programı"))
        self.lineEdit.setPlaceholderText(_translate("MainWindow", "Bekleme Süresi"))
        self.pushButton.setText(_translate("MainWindow", "Başlat"))

        

class Window(QtWidgets.QMainWindow):
    def __init__(self, parent=None):
        super(Window, self).__init__(parent)
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)

        self.ui.pushButton.clicked.connect(self.x)
    def x(self):
        cd = int(self.ui.lineEdit.text())
        for i in range(5):
            self.thread = DummyThread(self, cd)
            self.thread.finished.connect(self.a)
            self.thread.start()

    def a(self):
        self.ui.textBrowser.setText(self.ui.textBrowser.toPlainText() + "\nHello")

class DummyThread(QThread):
    finished = pyqtSignal()
    def __init__(self, parent, cd):
        super(DummyThread, self).__init__(parent)
        self.cd = cd

    def run(self):
        time.sleep(self.cd)
        self.finished.emit()


if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    win = Window()
    win.show()
    sys.exit(app.exec_())
max wiklund
  • 103
  • 1
  • 5
  • Hello, it did not give me any error, but also it didn't print hello after every sleep. It printed when the process is finished. I want to see every print after sleeps – Fatih Tüz Nov 01 '20 at 19:02
  • I have not changed your logic. If you give it the number 1 it will start 5 threads and sleep for 1 sec before updating the gui. All of these threads will be running concurrent (while sleeping). – max wiklund Nov 01 '20 at 19:08
0

There are 3 problems in your code.

  1. you are using 2 arguments in the thread constructor (self, cd), while the __init__ of DummyThread only accepts one. Note that even if you add the parent argument in the init, it won't work, as QThread (as all QObjects) only accepts a QObject instance as a parent, while self in that scope is the Ui_MainWindow instance (which is a plain simple python object.
  2. you used from time import *, so you cannot use time.sleep but sleep instead.
  3. you're constantly overwriting the self.thread instance, this means that everytime you try to create a new one using the same reference, the previous one will be instantly garbage collected, which results in a crash as the thread is being destroyed while it's still processing. Note that you'll get the same problem even if you only create one instance and you try to run x again while the previous is still active, or if you don't use a persistent reference for the thread instance (as it will be destroyed as soon as the function returns).

So, in order to solve your issue, you need the following modifications:

class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        # ...
        self.threads = []

    def x(self):
        cd = int(self.lineEdit.text())
        for i in range(5):
            thread = DummyThread(cd)
            thread.start()
            thread.finished.connect(self.a)
            self.threads.append(thread)

Note that if you need to run the same processing more than once concurrently, you could use a QRunnable instead. Also note that, as already pointed out in another answer, in the beginning of your file and in hundreds of other questions, you should never modify files created by pyuic (nor try to mimic their behavior), and one of the many reasons for that is that it can lead to confusion about the object structure (exactly like in your case). I suggest you to read more about the correct way to use those files on the official guidelines about using Designer.

musicamante
  • 41,230
  • 6
  • 33
  • 58