Issue
I designed the interface, and the custom component tried to output multiple drawing components, and the program crashed after a while.
The program probably consists of the following:ble. Py
reads the bluetooth values temporarily holding the EMG array. main_plot.py
instantiates the Show_EMG plotting class and outputs the Show_EMG plotting class reading the Bluetooth values of ble.PY
The program crashed itself without reporting any errors, I tried to output errors at different terminals.
pyqtgraph Component Code(Show_EMG.py):
import ble
from pyqtgraph import PlotWidget
import pyqtgraph as pg
import numpy as np
from PyQt5 import QtCore
class Plot_Show(object):
'''
Form,y,x,data,length = 1800, width = 250, high = 120, text = "sEMG Voltage"
'''
def __init__(self,Form,y,x,data,text=""):
# length = 1800, width = 250, high = 120, text = "sEMG Voltage"
self.Form=Form
self.y=y
self.x=x
self.data=ble.EMG[data]
self.length=1800
if(text==""):
self.test="sEMG Voltage"
else:
self.text = text
self.graphicsView = PlotWidget(self.Form)
self.initUI()
def initUI(self):
self.graphicsView.setGeometry(QtCore.QRect(self.y, self.x, 250, 120))
self.graphicsView.hideButtons()
self.graphicsView.setObjectName(self.text)
self.graphicsView.setLabel(axis="left",text=self.text)
self.graphicsView.setLabel(axis='bottom',text='Time')
self.graphicsView.setMouseEnabled(x=False,y=False)
self.graphicsView.setAntialiasing(True)
self.graphicsView.setMenuEnabled(False)
self.graphicsView.hideButtons()
self.data1 = np.zeros(self.length)
self.curve1 = self.graphicsView.plot(self.data1)
self.ptr1 = 0
def update1():
global data1, ptr1
self.graphicsView.setRange(xRange=[self.ptr1,self.ptr1+self.length],yRange=[5,550],padding=0)
self.data1[:-1] = self.data1[1:] # shift data in the array one sample left
self.data1[-1] = self.data
self.ptr1 += 1
self.curve1.setData(self.data1)
self.curve1.setPos(self.ptr1, 0)
self.timer = pg.QtCore.QTimer()
self.timer.timeout.connect(update1)
self.timer.start(10)
main_plot.py Code:
import ble
import sys
import Show_EMG
from PyQt5 import QtCore, QtWidgets
import threading
class Ui_Form(object):
def __init__(self):
super().__init__()
def setupUi(self, Form,**kwargs):
Form.resize(820, 454)
Form.setObjectName("Form")
Show_EMG.Plot_Show(Form=Form, y=10, x=10, data=0, text="sEMG2 Voltage")
Show_EMG.Plot_Show(Form=Form, y=10, x=140, data=1, text="sEMG2 Voltage")
Show_EMG.Plot_Show(Form=Form, y=10, x=270, data=2, text="sEMG3 Voltage")
Show_EMG.Plot_Show(Form=Form, y=280, x=10, data=3, text="sEMG4 Voltage")
Show_EMG.Plot_Show(Form=Form, y=280, x=140, data=4, text="sEMG5 Voltage")
Show_EMG.Plot_Show(Form=Form, y=280, x=270, data=5, text="sEMG6 Voltage")
Show_EMG.Plot_Show(Form=Form, y=550, x=10, data=0, text="sEMG7 Voltage")
Show_EMG.Plot_Show(Form=Form, y=550, x=140, data=0, text="sEMG8 Voltage")
self.gridLayoutWidget = QtWidgets.QWidget(Form)
self.gridLayoutWidget.setGeometry(QtCore.QRect(550, 270, 261, 121))
self.gridLayoutWidget.setObjectName("gridLayoutWidget")
self.gridLayout = QtWidgets.QGridLayout(self.gridLayoutWidget)
self.gridLayout.setContentsMargins(0, 0, 0, 0)
self.gridLayout.setObjectName("gridLayout")
self.pushButton = QtWidgets.QPushButton(Form)
self.pushButton.setGeometry(QtCore.QRect(370, 410, 75, 23))
self.pushButton.setObjectName("pushButton")
self.retranslateUi(Form)
QtCore.QMetaObject.connectSlotsByName(Form)
def retranslateUi(self, Form):
_translate = QtCore.QCoreApplication.translate
self.pushButton.setText(_translate("Form", "开始记录"))
Form.setWindowTitle(_translate("Form", "Form"))
def main():
app = QtWidgets.QApplication(sys.argv)
Form = QtWidgets.QWidget()
ui = Ui_Form()
ui.setupUi(Form)
Form.show()
sys.exit(app.exec_())
if __name__ == "__main__":
thread_main=threading.Thread(target=main)
thread_main.start()
thread_ble=threading.Thread(target=ble.ble)
thread_ble.start()
Ble.EMG array default temporarily to:[200. 0. 0. 0. 0. 0.]
More Ble Code detail: https://gist.github.com/allrobot/1547447f313942f278118cb2e569f59f
I tried to add threads in main_plot.py, but the program still crashes itself...
Perhaps QTimer should be the cause of the problem?
How can I change the code to solve the self-crash problem?I need to fix the custom component classes, but I am novice to PyQT, you have any suggestions?
Solution
As you already know (all) GUIs shouldn't run in separated thread.
But main problem which I had with code is that some elements in PyQt don't work if they are not assigned to global or class variables.
And when Plot
is not assigned to variable then its QTimer
doesn't work for me.
So I put all Plot on list and now all QTimers (without special threads) works for me.
self.plots = [
PlotShow(...),
PlotShow(...),
PlotShow(...),
# ...
]
Full working code.
I used class ble
to simulate module ble.py
and to have all code in one file (for tests).
I made also few small changes: PEP 8 -- Style Guide for Python Code
#import ble
from PyQt5 import QtCore, QtWidgets
import pyqtgraph as pg # pg.PlotWidget
import numpy as np
import threading
class ble:
''' Simulate module `ble.py` '''
EMG = [0,0,0,0,0,0,0,0,0,0,0,0,0]
def ble():
import math
import random
import time
counter = 0
while True:
for i in range(10):
if i in (0, 4, 8):
ble.EMG[i] = random.randint(0, 550)
elif i in (1, 5, 6):
ble.EMG[i] = random.randint(150, 250)
else:
ble.EMG[i] = 200 + math.sin(math.radians(counter//i)) * 200
counter += 1
time.sleep(0.1)
class PlotShow(): # PE8: `CamelNames` for classes
'''
form, y, x, data, length=1800, width=250, high=120, text="sEMG Voltage"
'''
def __init__(self, form, y, x, data_number, text=""): # PEP8: spaces after commans `,`
self.form = form # PE8: `lower_case_names` for variables
self.y = y
self.x = x
self.data_number = data_number
self.length = 1800
self.data = np.zeros(self.length)
self.ptr = 0
if not text:
self.test="sEMG Voltage"
else:
self.text = text
self.initUI()
#print('start:', self.text)
self.timer = pg.QtCore.QTimer()
self.timer.timeout.connect(self.update_plot)
self.timer.start(10)
def initUI(self):
self.graphicsView = pg.PlotWidget(self.form)
self.graphicsView.setGeometry(QtCore.QRect(self.y, self.x, 250, 120))
self.graphicsView.hideButtons()
self.graphicsView.setObjectName(self.text)
self.graphicsView.setLabel(axis="left",text=self.text)
self.graphicsView.setLabel(axis='bottom',text='Time')
self.graphicsView.setMouseEnabled(x=False,y=False)
self.graphicsView.setAntialiasing(True)
self.graphicsView.setMenuEnabled(False)
self.graphicsView.hideButtons()
self.curve = self.graphicsView.plot(self.data)
def update_plot(self):
#print('update:', self.text)
self.data[:-1] = self.data[1:] # shift data in the array one sample left
self.data[-1] = ble.EMG[self.data_number]
self.ptr += 1
self.curve.setData(self.data)
self.curve.setPos(self.ptr, 0)
self.graphicsView.setRange(xRange=[self.ptr, self.ptr+self.length], yRange=[5, 550], padding=0) # PEP8: spaces after commans `,`
class UIForm(): # PE8: `CamelNames` for classes
def setupUI(self, form, **kwargs):
form.resize(820, 454)
form.setObjectName("Form")
self.plots = [
PlotShow(form=form, y=10, x=10, data_number=0, text="sEMG1 Voltage"),
PlotShow(form=form, y=10, x=140, data_number=1, text="sEMG2 Voltage"),
PlotShow(form=form, y=10, x=270, data_number=2, text="sEMG3 Voltage"),
PlotShow(form=form, y=280, x=10, data_number=3, text="sEMG4 Voltage"),
PlotShow(form=form, y=280, x=140, data_number=4, text="sEMG5 Voltage"),
PlotShow(form=form, y=280, x=270, data_number=5, text="sEMG6 Voltage"),
PlotShow(form=form, y=550, x=10, data_number=6, text="sEMG7 Voltage"),
PlotShow(form=form, y=550, x=140, data_number=7, text="sEMG8 Voltage"),
PlotShow(form=form, y=550, x=270, data_number=8, text="sEMG9 Voltage"),
]
self.gridLayoutWidget = QtWidgets.QWidget(form)
self.gridLayoutWidget.setGeometry(QtCore.QRect(550, 270, 261, 121))
self.gridLayoutWidget.setObjectName("gridLayoutWidget")
self.gridLayout = QtWidgets.QGridLayout(self.gridLayoutWidget)
self.gridLayout.setContentsMargins(0, 0, 0, 0)
self.gridLayout.setObjectName("gridLayout")
self.pushButton = QtWidgets.QPushButton(form)
self.pushButton.setGeometry(QtCore.QRect(370, 410, 75, 23))
self.pushButton.setObjectName("pushButton")
self.retranslateUi(form)
QtCore.QMetaObject.connectSlotsByName(form)
def retranslateUi(self, form):
_translate = QtCore.QCoreApplication.translate
self.pushButton.setText(_translate("Form", "开始记录"))
form.setWindowTitle(_translate("Form", "Form"))
def main():
app = QtWidgets.QApplication([])
form = QtWidgets.QWidget() # PE8: `lower_case_names` for variables
ui = UIForm()
ui.setupUI(form)
form.show()
app.exec()
if __name__ == "__main__":
# better start before GUI to create all needed variables and values
thread_ble = threading.Thread(target=ble.ble)
thread_ble.start()
#thread_main = threading.Thread(target=main)
#thread_main.start()
#input() # keep running program when GUI runs in thread
# GUI rather shouldn't run in separated thread
main()
Frankly, if all plots get data from the same source and in the same time then you could use one QTimer to run all update1
in all plots - but this timer should be in UIForm
instead of PlotShow
EDIT:
Version which use only one QTimer
in UIForm
to execute update_plot()
for all plots on list self.plots
.
#import ble
from PyQt5 import QtCore, QtWidgets
import pyqtgraph as pg # pg.PlotWidget
import numpy as np
import threading
class ble:
''' Simulate module `ble.py` '''
EMG = [0,0,0,0,0,0,0,0,0,0,0,0,0]
def ble():
import math
import random
import time
counter = 0
while True:
for i in range(10):
if i in (0, 4, 8):
ble.EMG[i] = random.randint(0, 550)
elif i in (1, 5, 6):
ble.EMG[i] = random.randint(150, 250)
else:
ble.EMG[i] = 200 + math.sin(math.radians(counter//i)) * 200
counter += 1
time.sleep(0.1)
class PlotShow(): # PE8: `CamelNames` for classes
'''
form, y, x, data, length=1800, width=250, high=120, text="sEMG Voltage"
'''
def __init__(self, form, y, x, data_number, text=""): # PEP8: spaces after commans `,`
self.form = form # PE8: `lower_case_names` for variables
self.y = y
self.x = x
self.data_number = data_number
self.length = 1800
self.data = np.zeros(self.length)
self.ptr = 0
if not text:
self.test="sEMG Voltage"
else:
self.text = text
self.initUI()
#print('start:', self.text)
#self.timer = pg.QtCore.QTimer()
#self.timer.timeout.connect(self.update_plot)
#self.timer.start(10)
def initUI(self):
self.graphicsView = pg.PlotWidget(self.form)
self.graphicsView.setGeometry(QtCore.QRect(self.y, self.x, 250, 120))
self.graphicsView.hideButtons()
self.graphicsView.setObjectName(self.text)
self.graphicsView.setLabel(axis="left",text=self.text)
self.graphicsView.setLabel(axis='bottom',text='Time')
self.graphicsView.setMouseEnabled(x=False,y=False)
self.graphicsView.setAntialiasing(True)
self.graphicsView.setMenuEnabled(False)
self.graphicsView.hideButtons()
self.curve = self.graphicsView.plot(self.data)
def update_plot(self):
#print('update:', self.text)
self.data[:-1] = self.data[1:] # shift data in the array one sample left
self.data[-1] = ble.EMG[self.data_number]
self.ptr += 1
self.curve.setData(self.data)
self.curve.setPos(self.ptr, 0)
self.graphicsView.setRange(xRange=[self.ptr, self.ptr+self.length], yRange=[5, 550], padding=0) # PEP8: spaces after commans `,`
class UIForm(): # PE8: `CamelNames` for classes
def setupUI(self, form, **kwargs):
form.resize(820, 454)
form.setObjectName("Form")
self.plots = [
PlotShow(form=form, y=10, x=10, data_number=0, text="sEMG1 Voltage"),
PlotShow(form=form, y=10, x=140, data_number=1, text="sEMG2 Voltage"),
PlotShow(form=form, y=10, x=270, data_number=2, text="sEMG3 Voltage"),
PlotShow(form=form, y=280, x=10, data_number=3, text="sEMG4 Voltage"),
PlotShow(form=form, y=280, x=140, data_number=4, text="sEMG5 Voltage"),
PlotShow(form=form, y=280, x=270, data_number=5, text="sEMG6 Voltage"),
PlotShow(form=form, y=550, x=10, data_number=6, text="sEMG7 Voltage"),
PlotShow(form=form, y=550, x=140, data_number=7, text="sEMG8 Voltage"),
PlotShow(form=form, y=550, x=270, data_number=8, text="sEMG9 Voltage"),
]
self.gridLayoutWidget = QtWidgets.QWidget(form)
self.gridLayoutWidget.setGeometry(QtCore.QRect(550, 270, 261, 121))
self.gridLayoutWidget.setObjectName("gridLayoutWidget")
self.gridLayout = QtWidgets.QGridLayout(self.gridLayoutWidget)
self.gridLayout.setContentsMargins(0, 0, 0, 0)
self.gridLayout.setObjectName("gridLayout")
self.pushButton = QtWidgets.QPushButton(form)
self.pushButton.setGeometry(QtCore.QRect(370, 410, 75, 23))
self.pushButton.setObjectName("pushButton")
self.retranslateUi(form)
QtCore.QMetaObject.connectSlotsByName(form)
self.timer = pg.QtCore.QTimer()
self.timer.timeout.connect(self.update_all_plots)
self.timer.start(10)
def update_all_plots(self):
for plot in self.plots:
plot.update_plot()
def retranslateUi(self, form):
_translate = QtCore.QCoreApplication.translate
self.pushButton.setText(_translate("Form", "开始记录"))
form.setWindowTitle(_translate("Form", "Form"))
def main():
app = QtWidgets.QApplication([])
form = QtWidgets.QWidget() # PE8: `lower_case_names` for variables
ui = UIForm()
ui.setupUI(form)
form.show()
app.exec()
if __name__ == "__main__":
# better start before GUI to create all needed variables and values
thread_ble = threading.Thread(target=ble.ble)
thread_ble.start()
#thread_main = threading.Thread(target=main)
#thread_main.start()
#input() # keep running program when GUI runs in thread
# GUI rather shouldn't run in separated thread
main()
Answered By - furas
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.