I am looking for a way to change the variables between classes in different threads.
As I had a question about threading recently, I'd like to use that example code for this question again.
Main-File:
#PROGRAM/SCRIPT
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWidgets import QApplication
import sys
import GUI
import datetime
import pyqtgraph
import time
plotsize = 20
class Worker2(QtCore.QThread):
def __init__(self):
print("Thread2 has been started")
def run(self):
#now get access to variable of class Worker
# =============================================================================
# Threading for not freezing the GUI while running
# =============================================================================
class Worker(QtCore.QThread):
progress = QtCore.pyqtSignal(int)
finished = QtCore.pyqtSignal()
widgetplot = QtCore.pyqtSignal(list, list)
def __init__(self, plot_time, plot_value):
print("Thread has been started")
QtCore.QThread.__init__(self, objectName='WorkerThread')
self.plot_time = plot_time
self.plot_value = plot_value
def run(self):
#now get access to variable of Worker2
_count = 0
_start_time = datetime.datetime.now()
while 0 <= _count < 100:
# Use local variable!
_count_prev = _count
QtCore.QThread.usleep(10000)
_diff = datetime.datetime.now() - _start_time
_count = int((_diff.total_seconds() * 10))
if(_count != _count_prev):
print(_count)
x = self.plot_time[:_count]
y = self.plot_value[:_count]
self.widgetplot.emit(x, y)
class my_class(QtWidgets.QMainWindow, GUI.Ui_MainWindow):
def __init__(self, parent=None):
super(my_class, self).__init__(parent)
self.setupUi(self)
self.WidgetPlot.setXRange(0, 105, padding=0)
self.WidgetPlot.setYRange(0, 105, padding=0)
self.second_line = pyqtgraph.PlotDataItem(pen=pyqtgraph.mkPen('w', width=plotsize*2))
self.plot_time = []
self.plot_value = []
self.worker_thread = Worker(self.plot_time, self.plot_value)
self.worker_thread.widgetplot.connect(self.update_second_line_plot)
self.worker_thread2 = Worker2()
self.pushButton.clicked.connect(self.my_function)
self.WidgetPlot.setMouseEnabled(x=False, y=False)
font=QtGui.QFont()
font.setPixelSize(20)
font.setBold(True)
self.WidgetPlot.getAxis("bottom").setTickFont(font)
self.WidgetPlot.getAxis("left").setTickFont(font)
def my_function(self):
_l = list(range(100))
self.plot_time.extend(_l)
self.plot_value.extend(_l)
self.start()
def update_second_line_plot(self, plot_time, plot_value):
self.second_line.setData(plot_time, plot_value)
def start(self):
self.WidgetPlot.plot(self.plot_time, self.plot_value, pen=pyqtgraph.mkPen('r', width=plotsize))
self.WidgetPlot.addItem(self.second_line)
self.worker_thread.start()
self.worker_thread2.start()
def main():
app = QApplication(sys.argv)
form = my_class()
form.show()
app.exec_()
if __name__ == '__main__':
main()
GUI:
#GUI.py
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(739, 532)
MainWindow.setStyleSheet("")
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.WidgetPlot = PlotWidget(self.centralwidget)
self.WidgetPlot.setGeometry(QtCore.QRect(100, 40, 541, 341))
self.WidgetPlot.setObjectName("WidgetPlot")
self.pushButton = QtWidgets.QPushButton(self.centralwidget)
self.pushButton.setGeometry(QtCore.QRect(330, 420, 93, 28))
self.pushButton.setObjectName("pushButton")
MainWindow.setCentralWidget(self.centralwidget)
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", "Main Window"))
self.pushButton.setText(_translate("MainWindow", "Start"))
from pyqtgraph import PlotWidget
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_())
I'd like to change variable in class Worker2 from class Worker and vice versa.
Many thanks in advance!
Andrew
but for the other class there is no way to call/change variables...
Member variables* in Python are public by default.** Suppose you have
class P:
def __init__(self):
self.q = "whatever"
def printit(self):
print(self.q)
You can do this in the repl:
>>> p = P()
p = P()
>>> p.printit()
p.printit()
whatever
>>> p.q
p.q
'whatever'
>>> p.q = 'new value'
p.q = 'new value'
>>> p.printit()
p.printit()
new value
>>> p.q
p.q
'new value'
You can do the same from anywhere in Python code that has a reference to an object belonging to class P.
* "Attributes?" "fields?" I wrote a lot of Python programs that were ancillary to projects that I worked on—mostly for my own use. I haven't talked a lot with other Python programmers, and I don't really know the lingo.
** See https://towardsdatascience.com/private-protected-attributes-in-python-demystified-once-and-for-all-9456d4e56414 for more about "public" vs. "private."
Related
I'm looking for a solution to solve the following issue: My program starts with a plot of all data, later when I start a function a worker is plotting the same graph according to it times. So there are two lines, first a red one that shows how the plot will look like, later the plot that follows the first graph, done by a worker.
Unfortunately, the second plot is very thin. I've created a variable called "plotsize" in my example. This can change the first plot, but I have no idea how to change the characteristics of the second one within the threading with a worker. I'm using QThread.
Here my example codes, two data. Name of the GUI file is just GUI.py
#GUI.py
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(739, 532)
MainWindow.setStyleSheet("")
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.WidgetPlot = PlotWidget(self.centralwidget)
self.WidgetPlot.setGeometry(QtCore.QRect(100, 40, 541, 341))
self.WidgetPlot.setObjectName("WidgetPlot")
self.pushButton = QtWidgets.QPushButton(self.centralwidget)
self.pushButton.setGeometry(QtCore.QRect(330, 420, 93, 28))
self.pushButton.setObjectName("pushButton")
MainWindow.setCentralWidget(self.centralwidget)
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", "Main Window"))
self.pushButton.setText(_translate("MainWindow", "Start"))
from pyqtgraph import PlotWidget
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_())
And here the script:
#PROGRAM/SCRIPT
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWidgets import QApplication
import sys
import GUI
import datetime
import pyqtgraph
import time
plotsize = 2
# =============================================================================
# Threading for not freezing the GUI while running
# =============================================================================
class Worker(QtCore.QObject):
progress = QtCore.pyqtSignal(int)
finished = QtCore.pyqtSignal()
widgetplot = QtCore.pyqtSignal(list, list)
def __init__(self):
super().__init__()
def start(self):
self._run()
def _run(self):
self._count = 0
self.x = plot_time[:0]
self.y = plot_value[:0]
self.widgetplot.emit(self.x, self.y)
self._start_time = datetime.datetime.now()
while 0 <= self._count < 100:
self._count_prev = self._count
QtCore.QThread.usleep(10000)
self._diff = datetime.datetime.now() - self._start_time
self._count = int((self._diff.total_seconds() * 10))
if(self._count != self._count_prev):
print(self._count)
self.x = plot_time[:self._count]
self.y = plot_value[:self._count]
self.widgetplot.emit(self.x, self.y)
class my_class(QtWidgets.QMainWindow, GUI.Ui_MainWindow):
def __init__(self, parent=None):
super(my_class, self).__init__(parent)
self.setupUi(self)
self.thread = QtCore.QThread(self)
self.worker = Worker()
self.worker.moveToThread(self.thread)
self.thread.started.connect(self.worker.start)
self.worker.finished.connect(self.thread.quit)
self.worker.widgetplot.connect(self.WidgetPlot.plot)
self.pushButton.clicked.connect(self.my_function)
self.WidgetPlot.setMouseEnabled(x=False, y=False)
font=QtGui.QFont()
font.setPixelSize(20)
font.setBold(True)
self.WidgetPlot.getAxis("bottom").setTickFont(font)
self.WidgetPlot.getAxis("left").setTickFont(font)
def my_function(self):
global plot_time
plot_time = []
global plot_value
plot_value = []
for i in range(100):
plot_time.append(i)
plot_value.append(i)
self.start()
def preview_plot(self):
self.WidgetPlot.clear()
self.WidgetPlot.setXRange(0, 105, padding=0)
self.WidgetPlot.setYRange(0, 105, padding=0)
self.preview_line = self.WidgetPlot.plot(plot_time, plot_value, pen=pyqtgraph.mkPen('r', width=plotsize))
def start(self):
self.preview_plot()
self.thread.start()
def main():
app = QApplication(sys.argv)
form = my_class()
form.show()
app.exec_()
if __name__ == '__main__':
Many Thanks in advance!
Andrew
Based on what I have inferred from your request (i.e. you want just to use two different size for overlapping plot lines) I've rewritten your code with a series of improvements (in my humble opinion).
At the very core of the problem, what I've done is to create a separate pyqtgraph.PlotDataItem curve, plot it on top of your preview_line and handle a different size for it (completely randomic, please adjust the size as you desire). Thus, it's all about using
self.second_line.setData(x, y)
instead of recreating it every time with self.WidgetPlot.plot(...)
All the details are into the comments.
#PROGRAM/SCRIPT
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWidgets import QApplication
import sys
import GUI
import datetime
import pyqtgraph
import time
plotsize = 20
# =============================================================================
# Threading for not freezing the GUI while running
# =============================================================================
class Worker(QtCore.QThread):
progress = QtCore.pyqtSignal(int)
finished = QtCore.pyqtSignal()
widgetplot = QtCore.pyqtSignal(list, list)
def __init__(self, plot_time, plot_value):
QtCore.QThread.__init__(self, objectName='WorkerThread')
# AS LONG AS YOU DON'T MODIFY plot_time and plot_value INTO THIS THREAD, they are thread-safe.
# If you intend to modify them in the future (into this thread), you need to implement a mutex/lock system to avoid races
# Note that lists are mutable: you're storing the reference to the actual object that will always be edited into the MAIN THREAD
self.plot_time = plot_time
self.plot_value = plot_value
def run(self):
# This is naturally a LOCAL variable
# self._count = 0
_count = 0
# --- This is useless, it plots ([], [])
# self.widgetplot.emit(self.x, self.y)
# self.x = plot_time[:0]
# self.y = plot_value[:0]
# -----------------------------------
_start_time = datetime.datetime.now()
while 0 <= _count < 100:
# Use local variable!
_count_prev = _count
QtCore.QThread.usleep(10000)
_diff = datetime.datetime.now() - _start_time
_count = int((_diff.total_seconds() * 10))
if(_count != _count_prev):
print(_count)
x = self.plot_time[:_count]
y = self.plot_value[:_count]
# Since plot_time and plot_value are managed by main thread, you would just need to emit _count variable.
# But I'll stick with your code
self.widgetplot.emit(x, y)
class my_class(QtWidgets.QMainWindow, GUI.Ui_MainWindow):
def __init__(self, parent=None):
super(my_class, self).__init__(parent)
self.setupUi(self)
# In your version, the range is constant irrespective of the data being updated. It can be moved here
self.WidgetPlot.setXRange(0, 105, padding=0)
self.WidgetPlot.setYRange(0, 105, padding=0)
# Create and store ONE line, you will change just the underlying data, not the object itself. WAY MORE efficient
# Different pen with different plot size. Is this what you're seeking?
self.second_line = pyqtgraph.PlotDataItem(pen=pyqtgraph.mkPen('w', width=plotsize*2))
# Here class variable initialization goes
self.plot_time = []
self.plot_value = []
# You don't need `moveToThread`. You can just subclass QThread
self.worker_thread = Worker(self.plot_time, self.plot_value)
# I changed the name just to highlight the fact it is just an update and not a plot rebuilt
self.worker_thread.widgetplot.connect(self.update_second_line_plot)
self.pushButton.clicked.connect(self.my_function)
self.WidgetPlot.setMouseEnabled(x=False, y=False)
font=QtGui.QFont()
font.setPixelSize(20)
font.setBold(True)
self.WidgetPlot.getAxis("bottom").setTickFont(font)
self.WidgetPlot.getAxis("left").setTickFont(font)
def my_function(self):
# Use class variable instead, see the __init__
# ...Not efficient
# for i in range(100):
# plot_time.append(i)
# plot_value.append(i)
# Better:
_l = list(range(100))
self.plot_time.extend(_l)
self.plot_value.extend(_l)
# DON'T DO self.plot_time = list(range(100)) --> it will recreate a new object, but this one is shared with the worker thread!
self.start()
def update_second_line_plot(self, plot_time, plot_value):
# Just update the UNDERLYING data of your curve
self.second_line.setData(plot_time, plot_value)
def start(self):
# First plot preview_plot. done ONCE
self.WidgetPlot.plot(self.plot_time, self.plot_value, pen=pyqtgraph.mkPen('r', width=plotsize))
# Add NOW the new line to be drawn ON TOP of the preview one
self.WidgetPlot.addItem(self.second_line)
# It automatically will do the job. You don't need any plotfunction
self.worker_thread.start()
def main():
app = QApplication(sys.argv)
form = my_class()
form.show()
app.exec_()
if __name__ == '__main__':
main()
The result:
You currently have a signal connected to the plot method through self.worker.widgetplot.connect(self.WidgetPlot.plot), but you'll have a better time if you make a persistent plot - with a specific pen set, at your desired thickness - and then connect your widgetplot signal to that plot's setData method.
# ... Ui_MainWindow.__init__
self.plot_curve = self.WidgetPlot.plot(pen=mkPen("w", width=plotsize))
# ... in my_class.__init__
self.worker.widgetplot.connect(self.plot_curve.setData)
I have a very simple program and the structure must be preserved (it is a condition of the exercise).
We start with an interface formed by a button and a Canvas, like shown in the figure above.
Once the button is clicked, a background task is initiated, which calls a function called animation. In here we start a process that generates random data every time waiting_time.
We want to update the plot everytime there is a new x and y variable.
The code is the following:
from PyQt5 import QtCore, QtWidgets
from mplwidget import MplWidget
import threading
import time
import numpy as np
import sys
class RandomDataGeneration():
"""
Mandatory Class. This Class must exist.
"""
def __init__(self):
pass
def data_generation(self):
while True:
waiting_time = np.random.randint(1,4) # waiting time is given by a random number.
print(waiting_time)
time.sleep(waiting_time)
self.x = np.random.rand(10)
self.y = np.random.rand(10)
print(self.x)
print(self.y)
#self.update_plot()
def update_plot(self):
self.MplWidget.canvas.axes.clear()
self.MplWidget.canvas.axes.set_title('GRAPH')
self.MplWidget.canvas.axes.plot(x, y, marker='.', linestyle='')
self.MplWidget.canvas.axes.legend(('random'), loc='upper right')
self.MplWidget.canvas.draw()
def animation():
"""
This function initiates the RandomDataGeneration
"""
app = RandomDataGeneration()
app.data_generation()
class Ui_MainWindow():
def __init__(self):
super().__init__()
def start_download(self):
download_info = threading.Thread(target=animation)
download_info.start()
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(1280, 1024)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.pushButton = QtWidgets.QPushButton(self.centralwidget)
self.pushButton.setGeometry(QtCore.QRect(880, 80, 221, 32))
self.pushButton.setObjectName("pushButton")
self.MplWidget = MplWidget(self.centralwidget)
self.MplWidget.setGeometry(QtCore.QRect(49, 39, 771, 551))
self.MplWidget.setObjectName("MplWidget")
MainWindow.setCentralWidget(self.centralwidget)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.pushButton.setText(_translate("MainWindow", "PushButton"))
self.pushButton.clicked.connect(self.start_download)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
MainWindow = QtWidgets.QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec_())
To run the code is necessary to have in the same folder the following code with name mplwidget.py.
# ------------------------------------------------------
# -------------------- mplwidget.py --------------------
# ------------------------------------------------------
from PyQt5.QtWidgets import*
from matplotlib.backends.backend_qt5agg import FigureCanvas
from matplotlib.figure import Figure
class MplWidget(QWidget):
def __init__(self, parent = None):
QWidget.__init__(self, parent)
self.canvas = FigureCanvas(Figure())
vertical_layout = QVBoxLayout()
vertical_layout.addWidget(self.canvas)
self.canvas.axes = self.canvas.figure.add_subplot(111)
self.setLayout(vertical_layout)
Since you are creating a pyqt5 application I guess your best bet is to use a QThread for the blocking part and emit a signal every time new data is generated. One way would be to make RandomDataGeneration a subclass of QThread and implement run, e.g.
class RandomDataGeneration(QtCore.QThread):
"""
Mandatory Class. This Class must exist.
"""
new_data = QtCore.pyqtSignal()
def __init__(self, parent = None):
super().__init__(parent)
def data_generation(self):
while True:
waiting_time = np.random.randint(1,4) # waiting time is given by a random number.
print(waiting_time)
time.sleep(waiting_time)
self.x = np.random.rand(10)
self.y = np.random.rand(10)
print(self.x)
print(self.y)
self.new_data.emit()
def run(self):
self.data_generation()
To use the thread you could subclass QMainWindow and create an instance of RandomDataGeneration in there. Subclassing QMainWindow has an additional advantage that you can move the gui setup and the signal-slot connections in there as well, e.g.
class MyMainWindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
self.download_thread = RandomDataGeneration(self)
self.download_thread.new_data.connect(self.plot_data)
self.ui.pushButton.clicked.connect(self.start_download)
def start_download(self):
if not self.download_thread.isRunning():
self.download_thread.start()
def plot_data(self):
self.ui.MplWidget.update_plot(self.download_thread.x, self.download_thread.y)
The main part then becomes
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
MainWindow = MyMainWindow()
MainWindow.show()
sys.exit(app.exec_())
I'm trying to create a GUI that shows machine data taken from a Raspberry.
Unfortunately, I can't get my QT-Desinger surface updated.
So Im trying it now on this "test class" but sadly not successful
That ist was i allready have. Something is missing... but i dont now what
x = 0
class Ui_Form(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
def setupUi(self, Form):
Form.setObjectName("Form")
Form.resize(400, 300)
self.lcdNumber = QtWidgets.QLCDNumber(Form)
self.lcdNumber.setGeometry(QtCore.QRect(10, 50, 361, 191))
self.lcdNumber.setObjectName("lcdNumber")
self.lcdNumber.display(x)
self.retranslateUi(Form)
QtCore.QMetaObject.connectSlotsByName(Form)
def retranslateUi(self, Form):
_translate = QtCore.QCoreApplication.translate
Form.setWindowTitle(_translate("Form", "Form"))
def run(self):
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
Form = QtWidgets.QWidget()
ui = Ui_Form()
ui.setupUi(Form)
Form.show()
sys.exit(app.exec_())
class Test(threading.Thread):
global x
def __init__(self):
threading.Thread.__init__(self)
def runs(self):
while x <= 20:
print(x)
x = x + 1
time.sleep(2)
t = Ui_Form()
t1 = Test()
t.start()
t1.start()
the counter shows 0 and the loop dont start at all..
my goal was for the LCD to constantly update itself an schow x
is that possible ?
thanks
For update the value of x, QTimer is the best way when using PyQt, you do not need to use the threading module
from PyQt5.Qt import QLCDNumber, QDialog, QPushButton, QVBoxLayout, QApplication,QTimer
import sys
class LCD(QDialog):
x = 0
def __init__(self):
super(LCD, self).__init__()
self.lcdNumber = QLCDNumber()
self.pushStart = QPushButton("Start")
self.pushStart.clicked.connect(self.update)
vBox = QVBoxLayout()
vBox.addWidget(self.lcdNumber)
vBox.addWidget(self.pushStart)
self.setLayout(vBox)
self.timer = QTimer()
self.timer.timeout.connect(self.update)
def update(self):
self.lcdNumber.display(str(self.x))
self.x += 1
self.timer.start(1000)
if __name__ == "__main__":
app = QApplication(sys.argv)
lcd = LCD()
lcd.show()
sys.exit(app.exec_())
This question already has answers here:
Simple python inheritance
(3 answers)
QtDesigner changes will be lost after redesign User Interface
(2 answers)
Closed 4 years ago.
I am trying to add a custom widget to a layout. I can successfully add many PushButtons to my GridLayout, but when I attempt to add the custom widget it does not show.
I have attempted to provide a minimal example:
from PyQt5 import QtCore, QtGui, QtWidgets
import sys
class moduleForm(QtWidgets.QWidget):
def __init__(self, parent = None):
self.parent = parent
self.setObjectName("moduleForm")
self.resize(300, 400)
self.fModule = QtWidgets.QPushButton("Test")
self.fModule.setGeometry(QtCore.QRect(0, 0, 80, 20))
self.retranslateUi(self.parent)
QtCore.QMetaObject.connectSlotsByName(self)
def retranslateUi(self, moduleForm):
_translate = QtCore.QCoreApplication.translate
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("Rb Controller")
MainWindow.resize(900, 600)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.loMainTab = QtWidgets.QHBoxLayout(self.centralwidget)
self.centralwidget.setLayout(self.loMainTab)
self.saChannels = QtWidgets.QScrollArea(self.centralwidget)
self.saChannels.setWidgetResizable(True)
self.saChannels.setGeometry(QtCore.QRect(10,10,10,10))
self.fButtons = QtWidgets.QFrame(self.centralwidget)
self.fButtons.setFrameShadow(QtWidgets.QFrame.Sunken)
self.pbAddModule = QtWidgets.QPushButton(self.fButtons)
self.pbAddModule.setGeometry(QtCore.QRect(10, 10, 80, 20))
self.pbAddModule.setObjectName("pbAddModule")
self.loButtons = QtWidgets.QHBoxLayout(self.fButtons)
self.loButtons.addWidget(self.pbAddModule)
self.loButtons.addStretch()
self.fButtons.setLayout(self.loButtons)
self.hlwChannelsContents = QtWidgets.QWidget()
self.hlwChannelsContents.setObjectName("hlwChannelsContents")
self.hloChannelsContents = QtWidgets.QHBoxLayout(self.hlwChannelsContents)
self.hloChannelsContents.setObjectName("hloChannelsContents")
self.gloChannelsContents = QtWidgets.QGridLayout()
self.hloChannelsContents.addLayout(self.gloChannelsContents)
self.saChannels.setWidget(self.hlwChannelsContents)
self.loMainTab.addWidget(self.fButtons)
self.loMainTab.addWidget(self.saChannels,1)
for ii in range(10):
for jj in range(10):
self.r_button = QtWidgets.QPushButton("Element %s,%s " % (ii, jj))
self.gloChannelsContents.addWidget(self.r_button,ii,jj)
MainWindow.setCentralWidget(self.centralwidget)
self.retranslateUi(MainWindow)
self.pbAddModule.clicked.connect(self.createModule)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "Rb Controller"))
self.pbAddModule.setText(_translate("MainWindow", "Add Module"))
def createModule(self):
createModule = moduleForm()
self.gloChannelsContents.addWidget(createModule)
createModule.show()
class ApplicationWindow(QtWidgets.QMainWindow):
def __init__(self):
super(ApplicationWindow, self).__init__()
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
def main():
app = QtWidgets.QApplication(sys.argv)
application = ApplicationWindow()
application.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
I have added the super().__init__ function but it is still not working. The PushButton in my custom widget gets displayed if I add it not the custom widget, so all the other code is fine.
If I have:
self.gloChannelsContents.addWidget(createModule.fModule,self.i,self.j)
in createModule then I get a dynamic PushButton, however, if I try to use the custom widget
self.gloChannelsContents.addWidget(createModule,self.i,self.j)
nothing appears.
Try it:
from PyQt5 import QtCore, QtGui, QtWidgets
import sys
class moduleForm(QtWidgets.QWidget):
def __init__(self, row, parent = None): # + row
super().__init__()
self.parent = parent
self.setObjectName("moduleForm")
self.resize(300, 400)
self.fModule = QtWidgets.QPushButton("Test {}".format(row)) # + row
self.fModule.setGeometry(QtCore.QRect(0, 0, 80, 20))
self.retranslateUi(self.parent)
QtCore.QMetaObject.connectSlotsByName(self)
def retranslateUi(self, moduleForm):
_translate = QtCore.QCoreApplication.translate
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
self.i = 11 # + self.i
MainWindow.setObjectName("Rb Controller")
MainWindow.resize(900, 600)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.loMainTab = QtWidgets.QHBoxLayout(self.centralwidget)
self.centralwidget.setLayout(self.loMainTab)
self.saChannels = QtWidgets.QScrollArea(self.centralwidget)
self.saChannels.setWidgetResizable(True)
self.saChannels.setGeometry(QtCore.QRect(10,10,10,10))
self.fButtons = QtWidgets.QFrame(self.centralwidget)
self.fButtons.setFrameShadow(QtWidgets.QFrame.Sunken)
self.pbAddModule = QtWidgets.QPushButton(self.fButtons)
self.pbAddModule.setGeometry(QtCore.QRect(10, 10, 80, 20))
self.pbAddModule.setObjectName("pbAddModule")
self.loButtons = QtWidgets.QHBoxLayout(self.fButtons)
self.loButtons.addWidget(self.pbAddModule)
self.loButtons.addStretch()
self.fButtons.setLayout(self.loButtons)
self.hlwChannelsContents = QtWidgets.QWidget()
self.hlwChannelsContents.setObjectName("hlwChannelsContents")
self.hloChannelsContents = QtWidgets.QHBoxLayout(self.hlwChannelsContents)
self.hloChannelsContents.setObjectName("hloChannelsContents")
self.gloChannelsContents = QtWidgets.QGridLayout()
self.hloChannelsContents.addLayout(self.gloChannelsContents)
self.saChannels.setWidget(self.hlwChannelsContents)
self.loMainTab.addWidget(self.fButtons)
self.loMainTab.addWidget(self.saChannels,1)
for ii in range(10):
for jj in range(10):
self.r_button = QtWidgets.QPushButton("Element %s,%s " % (ii, jj))
self.gloChannelsContents.addWidget(self.r_button, ii, jj)
MainWindow.setCentralWidget(self.centralwidget)
self.retranslateUi(MainWindow)
self.pbAddModule.clicked.connect(self.createModule)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "Rb Controller"))
self.pbAddModule.setText(_translate("MainWindow", "Add Module"))
def createModule(self):
self.createModule = moduleForm(self.i) # +
self.gloChannelsContents.addWidget(self.createModule.fModule, self.i, 0) # +
self.i += 1 # +
# self.createModule.show() # ???
class ApplicationWindow(QtWidgets.QMainWindow):
def __init__(self):
super(ApplicationWindow, self).__init__()
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
def main():
app = QtWidgets.QApplication(sys.argv)
application = ApplicationWindow()
application.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
I'm making a light GUI program with PyQT5.
But now I'm facing some problem about thread.
I just made simple test program like bottom:
the program simply trying to append numbers to textbox, but it crashes.
I don't know why but somehow I can prevent it by removing a comment(time.sleep)
import sys
import threading
import time
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
class Some(QWidget):
e = threading.Event()
def btnfunc(self):
self.e.set()
def __init__(self):
super().__init__()
self.myButton = QPushButton('do next')
self.logs = QTextEdit()
self.mylay = QVBoxLayout()
self.mylay.addWidget(self.myButton)
self.mylay.addWidget(self.logs)
self.setLayout(self.mylay)
self.setGeometry(300, 300, 300, 550)
self.setWindowTitle('mytest')
self.show()
t = threading.Thread(target=self.myfunc, args=( ))
t.start()
self.myButton.clicked.connect(self.btnfunc)
def myfunc(self):
for i in range(300):
# time.sleep(0.4)
self.logs.append(str(i))
if i == 20:
self.e.wait()
app = QApplication(sys.argv)
ex = Some()
sys.exit(app.exec_())
It would be better if sets time higher.
I thought it is because of resource accessing, since it is pyQT5 GUI.
So I've find QThread. and I tried like bottom,
import sys
import time
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
class Some(QWidget):
qw = QWaitCondition()
qm = QMutex()
def btnfunc(self):
self.qw.wakeAll()
def __init__(self):
super().__init__()
self.myButton = QPushButton('do next')
self.logs = QTextEdit()
self.mylay = QVBoxLayout()
self.mylay.addWidget(self.myButton)
self.mylay.addWidget(self.logs)
self.setLayout(self.mylay)
self.setGeometry(300, 300, 300, 550)
self.setWindowTitle('mytest')
self.show()
self.myButton.clicked.connect(self.btnfunc)
self.thread = QThread()
self.thread.started.connect(self.myfunc)
self.thread.start()
def myfunc(self):
for i in range(300):
self.logs.append(str(i))
if i == 20:
self.qw.wait(self.qm)
app = QApplication(sys.argv)
ex = Some()
sys.exit(app.exec_())
But crashes, doesn't work. and tried QThread+threading.Event(). It freezes GUI.
Now I don't know how to proceed it...
Edit:
I just realized about thread. Should not be accessed from other thread except QThread.
Then I will keep find about QWaitCondition
You should not control GUI directly via multithreading. Since two different threads are trying to control the GUI this results to freeze or crash.
I have learnt about this concept from here http://www.xyzlang.com/python/PyQT5/pyqt_multithreading.html
Here is your code that will work perfectly.
import sys
import threading
import time
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
# Added new
class Communicate(QObject):
signal = pyqtSignal(str)
class Some(QWidget):
e = threading.Event()
def btnfunc(self):
self.e.set()
def __init__(self):
super().__init__()
#communicate object
self.comm = Communicate()
self.comm.signal.connect(self.append_data)
self.myButton = QPushButton('do next')
self.logs = QTextEdit()
self.mylay = QVBoxLayout()
self.mylay.addWidget(self.myButton)
self.mylay.addWidget(self.logs)
self.setLayout(self.mylay)
self.setGeometry(300, 300, 300, 550)
self.setWindowTitle('mytest')
self.show()
t = threading.Thread(target=self.myfunc, args=( ))
t.start()
self.myButton.clicked.connect(self.btnfunc)
def myfunc(self):
for i in range(300):
# time.sleep(0.4)
#self.logs.append(str(i))
self.comm.signal.emit(str(i))
if i == 20:
self.e.wait()
def append_data(self, data):
self.logs.append(data)
app = QApplication(sys.argv)
ex = Some()
sys.exit(app.exec_())
You can pause the loop by using while = True and you can stop the loop with break statement
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import (
Qt, QObject, pyqtSignal, pyqtSlot, QRunnable, QThreadPool
)
import time
from time import sleep
import threading
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(655, 589)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.run = QtWidgets.QPushButton(self.centralwidget)
self.run.setGeometry(QtCore.QRect(260, 50, 93, 28))
self.run.setObjectName("run")
self.result = QtWidgets.QTextEdit(self.centralwidget)
self.result.setGeometry(QtCore.QRect(110, 120, 491, 201))
self.result.setObjectName("result")
self.stop = QtWidgets.QPushButton(self.centralwidget)
self.stop.setGeometry(QtCore.QRect(110, 390, 93, 28))
self.stop.setObjectName("stop")
self.pause = QtWidgets.QPushButton(self.centralwidget)
self.pause.setGeometry(QtCore.QRect(300, 390, 93, 28))
self.pause.setObjectName("pause")
self.resume = QtWidgets.QPushButton(self.centralwidget)
self.resume.setGeometry(QtCore.QRect(480, 390, 93, 28))
self.resume.setObjectName("resume")
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 655, 26))
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.run.setText(_translate("MainWindow", "Run"))
self.stop.setText(_translate("MainWindow", "stop"))
self.pause.setText(_translate("MainWindow", "Pause"))
self.resume.setText(_translate("MainWindow", "Resume"))
class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.setupUi(self)
self.is_paused = False
self.is_killed = False
self.run.clicked.connect(self.send_wala)
self.stop.clicked.connect(self.kill_g)
self.pause.clicked.connect(self.pause_g)
self.resume.clicked.connect(self.resume_g)
#QtCore.pyqtSlot()
def send_wala(self):
threading.Thread(target=self.working, daemon=True).start()
def working(self):
for i in range(10):
sleep(3)
self.result.append(str(i))
while self.is_paused:
time.sleep(0)
if self.is_killed:
break
def pause_g(self):
self.is_paused = True
def resume_g(self):
self.is_paused = False
def kill_g(self):
self.is_killed = True
import sys
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())