QThread and data coming from main window - python

I have a main window created with PyQt5, i.e.:
class PrincipalClass(QMainWindow):
def __init__(self):
super().__init__()
self.ui = Ui_PrincipalForm()
self.ui.setupUi(self)
self.list = []
self.filename = ""
self.chars = ""
self.filename_noext = ""
I have also a function inside the class:
def checkchars(self):
self.chars = ""
if self.ui.minuscoleCb.isChecked():
self.chars += self.minuscole
if self.ui.maiuscoleCb.isChecked():
self.chars += self.maiuscole
if self.ui.numeriCb.isChecked():
self.chars += self.numeri
Now I want to create a QThread class with his run function that I want to use in order to do some calculations, but I need to use inside the QThread class the result of self.chars coming from the main window.
class External(QThread):
word = pyqtSignal(str)
def run(self):
How can I have the value of self.chars available inside the QThread class? I know that from QThread to main window I can use the signal way, but viceversa?

Related

data transfer problem between qthread and ui?

I succeeded in creating a gui crawling program using Beautiful Soup and PyQt5.
By the way, I had a problem with gui freeze while the program executed the repeating statement.
So I'm going to use QThread.
But when I bring the elements related to gui on Thread, there is a problem.
(There's no problem with operating code that has nothing to do with gui, so I don't think there's any data transmission between classes.) (Is this right?)
I've created a simple problem. ↓
import sys
import time
from PyQt5 import uic
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
form_class = uic.loadUiType('aaaa.ui')[0]
class Thread1(QThread):
def __init__(self, Main):
super().__init__(Main)
def run(self):
i = 1
while i <= 10:
print(self.lineEdit.text().strip()) #No data transmission between Main and Thread1??
time.sleep(1)
i += 1
class Main(QMainWindow, form_class):
def __init__(self):
super().__init__()
self.setupUi(self)
self.initSetting()
self.initSignal()
def initSetting(self):
self.statusBar().showMessage('Wait')
self.setWindowTitle('aaaa')
def initSignal(self):
self.pushButton.clicked.connect(self.printWord)
def printWord(self):
self.statusBar().showMessage('Threading')
x = Thread1(self)
x.start()
if __name__ == "__main__" :
app = QApplication(sys.argv)
aaaa = Main()
aaaa.show()
app.exec_()
Using QThread, from what I understand, transmission of information between classes is doable, but to transmit information from your Main class to your Thread1 class, you have to pass arguments upon instantiation of your Thread1 class from your Main class.
In other words, your Main class and your Thread1 class do not share any variables or functions. They are separate classes.
Here's how I might do this:
class Main(QMainWindow, form_class):
def __init__(self):
super().__init__()
self.setupUi(self)
self.initSetting()
self.initSignal()
def initSetting(self):
self.statusBar().showMessage("Wait")
self.setWindowTitle("aaaa")
def initSignal(self):
self.pushButton.clicked.connect(self.printWord)
def printWord(self):
self.statusBar().showMessage("Threading")
some_message = self.lineEdit.text().strip()
self.thread_1 = Thread1(some_message)
self.thread_1.start()
class Thread1(QThread):
def __init__(self, input_message, parent=None):
QThread.__init__(self, parent)
self.input_message = input_message
def run(self):
i = 1
while i <= 10:
print(self.input_message)
time.sleep(1)
i += 1
Let me know if this is helpful.

Passing multiple parameters back from PyQt thread

Is there a way to pass multiple parameters from a thread back to the main thread at the same time?
I have started a thread using PyQt5 in which two real time variables are calculated. I want to pass both parameters back to the main thread to be combined and calculated with parameters from another thread.
As attached in the code below, I am able to return each parameter individually and print to the screen. How do I return all parameters into one function so I can proceed with calculations from another thread?
Thank you!
import sys
import time
from PyQt5.QtWidgets import QMainWindow, QPushButton, QVBoxLayout, QFrame, QApplication
from PyQt5.QtCore import pyqtSignal, QObject, QThread
class Counter(QObject):
'''
Class intended to be used in a separate thread to generate numbers and send
them to another thread.
'''
param1 = pyqtSignal(str)
param2 = pyqtSignal(str)
stopped = pyqtSignal()
def __init__(self):
QObject.__init__(self)
def start(self):
'''
Count from 0 to 99 and emit each value to the GUI thread to display.
'''
for x in range(4):
self.param1.emit(str(x))
self.param2.emit(str(x)+'2')
time.sleep(0.7)
self.stopped.emit()
class Application(QMainWindow):
def __init__(self):
QMainWindow.__init__(self)
# Configuring widgets
self.button = QPushButton()
self.button.setText('99')
self.layout = QVBoxLayout()
self.layout.addWidget(self.button)
self.frame = QFrame()
self.frame.setLayout(self.layout)
self.setCentralWidget(self.frame)
# Configuring separate thread
self.counterThread = QThread()
self.counter = Counter()
self.counter.moveToThread(self.counterThread)
# Connecting signals
self.button.clicked.connect(self.startCounting)
self.counter.param1.connect(self.button.setText)
self.counter.param1.connect(self.someFunction1)
self.counter.param2.connect(self.someFunction2)
self.counter.stopped.connect(self.counterThread.quit)
self.counterThread.started.connect(self.counter.start)
# print data from parameter 1
def someFunction1(self, data):
print(data + ' in main')
# print data from parameter 2
def someFunction2(self, data):
print(data + ' in main')
def startCounting(self):
if not self.counterThread.isRunning():
self.counterThread.start()
if __name__ == '__main__':
app = QApplication(sys.argv)
window = Application()
window.show()
sys.exit(app.exec_())
The signals also support the transmission of lists so you can use it to transport several variables:
class Counter(QObject):
"""
Class intended to be used in a separate thread to generate numbers and send
them to another thread.
"""
params = pyqtSignal(list)
stopped = pyqtSignal()
def start(self):
"""
Count from 0 to 99 and emit each value to the GUI thread to display.
"""
for x in range(4):
values = [str(x), str(x) + "2"]
self.params.emit(values)
time.sleep(0.7)
self.stopped.emit()
class Application(QMainWindow):
def __init__(self):
super(Application, self).__init__()
# Configuring widgets
self.frame = QFrame()
self.button = QPushButton("99")
lay = QVBoxLayout(self.frame)
lay.addWidget(self.button)
self.setCentralWidget(self.frame)
# Configuring separate thread
self.counterThread = QThread()
self.counter = Counter()
self.counter.moveToThread(self.counterThread)
# Connecting signals
self.button.clicked.connect(self.startCounting)
self.counter.params.connect(self.someFunction)
self.counter.stopped.connect(self.counterThread.quit)
self.counterThread.started.connect(self.counter.start)
#pyqtSlot(list)
def someFunction(self, params):
print(params)
if params:
self.button.setText(params[0])
def startCounting(self):
if not self.counterThread.isRunning():
self.counterThread.start()

PyQt: nested inheritance setupUi

I'm trying to make a custom widget getting more and more descriptive with each sub class. For instance below, I'm adding to a list of boxes as I make them. I want do_stuff() to only run once but I want it to run on initialization. My problem is that I need all of the __setupUi() functions to run before I finish it. Any help is appreciated.
from PyQt5 import QtCore, QtWidgets
class layer4:
def __init__(self):
self.boxes = []
self.__setupUi()
# I want to run this once AFTER all of the children (regardless of how deep it goes) have run
self.do_stuff()
def __setupUi(self):
self.qsb = QtWidgets.QSpinBox()
self.boxes.append(self.qsb)
def do_stuff(self):
print(self.boxes)
class layer3(layer4):
def __init__(self):
super().__init__()
self.__setupUi()
def __setupUi(self):
self.qsb2 = QtWidgets.QSpinBox()
self.boxes.append(self.qsb2)
class layer2(layer3):
def __init__(self):
super().__init__()
self.__setupUi()
def __setupUi(self):
self.datetimebox = QtWidgets.QDateTimeEdit()
self.boxes.append(self.datetimebox)
class layer1(layer2):
def __init__(self):
super().__init__()
self.__setupUi()
def __setupUi(self):
self.other = QtWidgets.QDateTimeEdit()
self.boxes.append(self.other)
So if I run
t = layer2()
It should return 2 QSpinboxes and 1 datetimeedit

Inactive subwindows in PyQt4

I have been struggling with a problem recently and I cannot get around it. I have a PyQt QMainWindow which contains a subwindow :
As you can figure out, clicking on the GO! button will open a number of subwindows specified by the number in the QLineEdit :
And clicking on the QCheckBox inside each subwindow should display a text.
The problem is that this works only for the last spawned subwindow. The others appear to be inactive.
Is their a way to make them active?
Please find my code below:
from PyQt4 import QtGui
import mainWin
import subWin
import sys
class MainWindowGui():
def __init__(self):
self.w = QtGui.QMainWindow()
self.MainWindow = myWinCls(self)
self.MainWindow.setupUi(self.w)
self.w.showMaximized()
class myWinCls(QtGui.QMainWindow):
def __init__(self, parent = None):
QtGui.QWidget.__init__(self)
self.parent = parent
def setupUi(self,Widget):
self.ui = mainWin.Ui_MainWindow()
self.ui.setupUi(Widget)
self.ui.mdiArea.addSubWindow(self.ui.subwindow)
self.ui.goBtn.clicked.connect(self.show_wins)
def show_wins(self):
N = int(self.ui.nbrEdit.text())
for self.k in xrange(N):
self.show_subwins()
def show_subwins(self):
self.win = QtGui.QWidget()
self.child_window = showSubWinCls(self)
self.child_window.setupUi(self.win)
self.subwin = self.ui.mdiArea.addSubWindow(self.win)
self.win.setWindowTitle("Subwin " + str(self.k))
self.subwin.show()
class showSubWinCls(QtGui.QWidget):
def __init__(self, parent = None):
QtGui.QWidget.__init__(self)
self.parent = parent
def setupUi(self, Widget):
self.ui = subWin.Ui_Form()
self.ui.setupUi(Widget)
self.ui.checkBox.clicked.connect(self.show_msg)
def show_msg(self):
if self.ui.checkBox.isChecked():
self.ui.lineEdit.setText("Yiiiiiihaaaaaa !!!")
else:
self.ui.lineEdit.setText("")
def main():
app = QtGui.QApplication(sys.argv)
app.setStyle(QtGui.QStyleFactory.create('WindowsVista'))
ex = MainWindowGui()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
I am sure this problem is somehow a classic trick but despite searching for some time now, I cannot figure it out.
Thanks for your help!
The problematic part:
def show_wins(self):
N = int(self.ui.nbrEdit.text())
for self.k in xrange(N):
self.show_subwins()
def show_subwins(self):
self.win = QtGui.QWidget()
self.child_window = showSubWinCls(self) #erase refererence to previous one
self.child_window.setupUi(self.win)
self.subwin = self.ui.mdiArea.addSubWindow(self.win)
self.win.setWindowTitle("Subwin " + str(self.k))
self.subwin.show()
You are only keeping reference to one subwindow in self.child_window, the last window openned.
In show_wins, you call show_subwin N time. Each time, you redefine self.child_window as a new instance of the class showSubWinCls. You lose the reference to the previous one.
You need to keep a reference to all the windows, otherwise signals and slots won't work. You can do something like this:
class myWinCls(QtGui.QMainWindow):
def __init__(self, parent = None):
QtGui.QWidget.__init__(self)
self.parent = parent
self.subWindowList=[]
def show_subwins(self):
...
child_window = showSubWinCls(self)
child_window.setupUi(self.win)
self.subWindowList.append(child_window)

PyQt update gui

I'm trying to update the text in a Qt GUI object via a QThread in PyQt but I just get the error QPixmap: It is not safe to use pixmaps outside the GUI thread, then it crashes. I would really appreciate any help, thanks.
class MainWindow(QMainWindow, Ui_MainWindow):
def __init__(self, parent = None):
QMainWindow.__init__(self, parent)
self.setupUi(self)
self.output = Output()
def __del__ (self):
self.ui = None
#pyqtSignature("")
def on_goBtn_released(self):
threadnum = 1
#start threads
for x in xrange(threadnum):
thread = TheThread()
thread.start()
class Output(QWidget, Ui_Output):
def __init__(self, parent = None):
QWidget.__init__(self, parent)
self.setupUi(self)
self.ui = Ui_Output
self.show()
def main(self):
self.textBrowser.append("sdgsdgsgsg dsgdsg dsgds gsdf")
class TheThread(QtCore.QThread):
trigger = pyqtSignal()
def __init__(self):
QtCore.QThread.__init__(self)
def __del__(self):
self.wait()
def run(self):
self.trigger.connect(Output().main())
self.trigger.emit()
self.trigger.connect(Output().main())
This line is problematic. You are instantiating a class in the thread which looks like a widget. This is wrong. You shouldn't use GUI elements in a different thread. All GUI related code should run in the same thread with the event loop.
The above line is also wrong in terms of design. You emit a custom signal from your thread and this is a good way. But the object to process this signal should be the one that owns/creates the thread, namely your MainWindow
You also don't keep a reference to your thread instance. You create it in a method, but it is local. So it'll be garbage collected, you probably would see a warning that it is deleted before it is finished.
Here is a minimal working example:
import sys
from PyQt4 import QtGui, QtCore
import time
import random
class MyThread(QtCore.QThread):
trigger = QtCore.pyqtSignal(int)
def __init__(self, parent=None):
super(MyThread, self).__init__(parent)
def setup(self, thread_no):
self.thread_no = thread_no
def run(self):
time.sleep(random.random()*5) # random sleep to imitate working
self.trigger.emit(self.thread_no)
class Main(QtGui.QMainWindow):
def __init__(self, parent=None):
super(Main, self).__init__(parent)
self.text_area = QtGui.QTextBrowser()
self.thread_button = QtGui.QPushButton('Start threads')
self.thread_button.clicked.connect(self.start_threads)
central_widget = QtGui.QWidget()
central_layout = QtGui.QHBoxLayout()
central_layout.addWidget(self.text_area)
central_layout.addWidget(self.thread_button)
central_widget.setLayout(central_layout)
self.setCentralWidget(central_widget)
def start_threads(self):
self.threads = [] # this will keep a reference to threads
for i in range(10):
thread = MyThread(self) # create a thread
thread.trigger.connect(self.update_text) # connect to it's signal
thread.setup(i) # just setting up a parameter
thread.start() # start the thread
self.threads.append(thread) # keep a reference
def update_text(self, thread_no):
self.text_area.append('thread # %d finished' % thread_no)
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
mainwindow = Main()
mainwindow.show()
sys.exit(app.exec_())

Categories

Resources