Я сделал PyQt5 GUI, который отображает последний из нескольких сканов из RGA. Я бы хотел, чтобы пользователь мог масштабировать / панорамировать существующие графики во время получения следующего сканирования, и я пытаюсь использовать многопоточность, чтобы обеспечить реагирование GUI во время выполнения последовательной связи. Это не совсем работает. Я сделал более простую версию кода с некоторой l oop, чтобы имитировать c паузу во время сбора данных. Кроме того, я обнаружил, что мне нужно поместить QApplication.processevents () в мой поток, чтобы график обновлялся в конце каждого l oop в потоке - в противном случае он обновляется только с последними графиками после потока сделано. Который, кажется, победить цель.
У меня также есть кнопка «Стоп». Я хотел бы прервать все, что кажется запаздывающим в ответ, аналогично панорамированию / масштабированию на графике.
Любая помощь будет принята!
Файл пользовательского интерфейса с pyqtgraph, добавленным как PlotWidget, (извинения - сложнее, чем необходимо, поскольку это урезанная версия моего оригинала) ThreadingLayout.ui:
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1126</width>
<height>738</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<layout class="QVBoxLayout" name="verticalLayout_6">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_16">
<property name="sizeConstraint">
<enum>QLayout::SetFixedSize</enum>
</property>
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QPushButton" name="startbutton">
<property name="text">
<string>Start</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="stopbutton">
<property name="text">
<string>Stop</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_5">
<item>
<widget class="PlotWidget" name="plotWidget" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>300</height>
</size>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3"/>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_17">
<property name="spacing">
<number>0</number>
</property>
<item>
<widget class="QPushButton" name="exitbutton">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Exit</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1126</width>
<height>21</height>
</rect>
</property>
</widget>
<widget class="QStatusBar" name="statusbar"/>
</widget>
<customwidgets>
<customwidget>
<class>PlotWidget</class>
<extends>QWidget</extends>
<header location="global">pyqtgraph</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
<buttongroups>
<buttongroup name="buttonGroup_2"/>
<buttongroup name="buttonGroup"/>
</buttongroups>
</ui>
и мой код python3 это:
from PyQt5 import uic
from PyQt5.QtWidgets import QApplication, QMainWindow, QPushButton
from PyQt5.QtCore import *
import pyqtgraph as pg
from random import random
import numpy as np
import traceback, sys
class WorkerSignals(QObject):
finished = pyqtSignal()
error = pyqtSignal(tuple)
result = pyqtSignal(object)
progress = pyqtSignal(int)
class Worker(QRunnable):
def __init__(self, fn, *args, **kwargs):
super(Worker, self).__init__()
# Store constructor arguments (re-used for processing)
self.fn = fn
self.args = args
self.kwargs = kwargs
self.signals = WorkerSignals()
# Add the callback to our kwargs
self.kwargs['progress_callback'] = self.signals.progress
@pyqtSlot()
def run(self):
try:
result = self.fn(*self.args, **self.kwargs)
except:
traceback.print_exc()
exctype, value = sys.exc_info()[:2]
self.signals.error.emit((exctype, value, traceback.format_exc()))
else:
self.signals.result.emit(result) # Return the result of the processing
finally:
self.signals.finished.emit() # Done
class UI(QMainWindow):
def __init__(self):
super(UI, self).__init__()
uic.loadUi("ThreadingLayout.ui", self)
self.startButton = self.findChild(QPushButton, "startbutton")
self.stopButton = self.findChild(QPushButton, "stopbutton")
self.exitButton = self.findChild(QPushButton, "exitbutton")
self.startButton.clicked.connect(self.clickedstartButton)
self.stopButton.clicked.connect(self.clickedstopButton)
self.exitButton.clicked.connect(self.clickedexitButton)
self.numberstartclicks=0
self.numberstopclicks=0
self.show()
self.threadpool = QThreadPool()
def clickedstopButton(self):
self.numberstopclicks = self.numberstopclicks+1
def plotanddelay(self):
self.plotWidget.clear()
try:
self.legend.scene().removeItem(self.legend)
except Exception as e:
pass
self.legend = self.plotWidget.addLegend()
self.singlerunx = list(range(100))
self.singleruny = []
randomscaling = random()
for i in range(100):
self.singleruny.append(randomscaling * self.singlerunx[i] + random())
self.allrunsy = np.append(self.allrunsy, self.singleruny)
# only plot last 4 plots
if self.plotrunindex < self.numberplotlines:
for self.plotlineindex in reversed(range(self.plotrunindex)):
pen = pg.mkPen(color=pg.intColor(self.plotlineindex), width=2)
ploty = self.allrunsy[len(self.allrunsy) - len(self.singlerunx) * (self.plotlineindex + 1):
len(self.allrunsy) - len(self.singlerunx) * self.plotlineindex]
self.plotWidget.plot(self.singlerunx, ploty, pen=pen,
name='Run ' + str(self.plotrunindex - self.plotlineindex))
else:
for plotlineindex in reversed(range(self.numberplotlines)):
pen = pg.mkPen(color=pg.intColor(plotlineindex), width=2)
ploty = self.allrunsy[len(self.allrunsy) - len(self.singlerunx) * (plotlineindex + 1):
len(self.allrunsy) - len(self.singlerunx) * plotlineindex]
self.plotWidget.plot(self.singlerunx, ploty, pen=pen,
name='Run ' + str(self.plotrunindex - plotlineindex))
self.plotWidget.showGrid(True, True)
QApplication.processEvents()
# put in time delay
while i < 2E7:
i = i+1
def clickedstartButton(self):
self.numberstartclicks = self.numberstartclicks+1
if (self.numberstartclicks==1):
self.plotWidget.clear()
try:
self.legend.scene().removeItem(self.legend)
except Exception as e:
pass
self.legend = self.plotWidget.addLegend()
self.allrusnx = []
self.allrunsy = []
self.numberruns = 10
self.numberplotlines = 3
for self.plotrunindex in range(self.numberruns):
if self.numberstopclicks > 0:
print('Run stopped by user')
self.numberstopclicks = 0
break
worker = Worker(self.plotanddelay())
# Execute
self.threadpool.start(worker)
else:
self.numberstartclicks = 0
def clickedexitButton(self):
self.close()
app=QApplication([])
UIWindow=UI()
app.exec()
Просто обновление - основываясь на полезной информации от Люка, я обнаружил, что следующий код добился цели:
from PyQt5 import uic
from PyQt5.QtWidgets import QApplication, QMainWindow, QPushButton
from PyQt5.QtCore import *
import pyqtgraph as pg
from random import random
import numpy as np
class ConfWorker(QThread):
threadSignal = pyqtSignal(list)
finishSignal = pyqtSignal(str)
def __init__(self, startParm):
super().__init__()
self.startParm = startParm # Initialize the parameters passed to the task
def run(self):
for i in range(self.startParm):
if (self.isInterruptionRequested()):
return
self.singlerunx = list(range(100))
returndata=[]
singleruny = []
randomscaling = random()
for j in range(100):
singleruny.append(randomscaling * self.singlerunx[j] + random())
returndata.append(i) #gives the run number
returndata.append(singleruny)
self.threadSignal.emit(returndata)
QThread.msleep(2000)
self.finishSignal.emit('finished')
class UI(QMainWindow):
def __init__(self):
super(UI, self).__init__()
uic.loadUi("ThreadingLayout.ui", self)
self.startButton = self.findChild(QPushButton, "startbutton")
self.stopButton = self.findChild(QPushButton, "stopbutton")
self.exitButton = self.findChild(QPushButton, "exitbutton")
self.startButton.clicked.connect(self.workerStart)
self.stopButton.clicked.connect(self.clickedstopButton)
self.exitButton.clicked.connect(self.clickedexitButton)
self.numberstartclicks=0
self.numberstopclicks=0
self.singlerunx=[]
self.singleruny=[]
self.allrunsx=[]
self.allrunsy=[]
self.threadrunindex=0
self.startParm=0
self.show()
def clickedstopButton(self):
self.worker.requestInterruption()
print('stopped')
def updateplot(self, returndata):
self.plotWidget.clear()
self.plotrunindex=returndata[0]
self.allrunsy = np.append(self.allrunsy, returndata[1:])
self.singlerunx = list(range(100))
try:
self.legend.scene().removeItem(self.legend)
except Exception as e:
pass
self.legend = self.plotWidget.addLegend()
# only plot last 4 plots
if self.plotrunindex < self.numberplotlines:
for self.plotlineindex in reversed(range(self.plotrunindex)):
pen = pg.mkPen(color=pg.intColor(self.plotlineindex), width=2)
ploty = self.allrunsy[len(self.allrunsy) - len(self.singlerunx) * (self.plotlineindex + 1):
len(self.allrunsy) - len(self.singlerunx) * self.plotlineindex]
self.plotWidget.plot(self.singlerunx, ploty, pen=pen,
name='Run ' + str(self.plotrunindex - self.plotlineindex))
else:
for self.plotlineindex in reversed(range(self.numberplotlines)):
pen = pg.mkPen(color=pg.intColor(self.plotlineindex), width=2)
ploty = self.allrunsy[len(self.allrunsy) - len(self.singlerunx) * (self.plotlineindex + 1):
len(self.allrunsy) - len(self.singlerunx) * self.plotlineindex]
self.plotWidget.plot(self.singlerunx, ploty, pen=pen,
name='Run ' + str(self.plotrunindex - self.plotlineindex))
self.plotWidget.showGrid(True, True)
def clickedexitButton(self):
self.close()
def workerStart(self):
self.numberstartclicks = self.numberstartclicks + 1
if (self.numberstartclicks == 1):
self.plotWidget.clear()
try:
self.legend.scene().removeItem(self.legend)
except Exception as e:
pass
self.legend = self.plotWidget.addLegend()
self.allrusnx = []
self.allrunsy = []
self.numberruns = 10
self.numberplotlines = 3
self.plotrunindex = 0
self.startParm = self.numberruns
self.worker = ConfWorker(self.startParm)
self.worker.threadSignal.connect(self.updateplot)
self.worker.finishSignal.connect(self.on_finishSignal)
self.worker.start()
else:
self.numberstartclicks = 0
def on_finishSignal(self, text):
print(text)
app=QApplication([])
UIWindow=UI()
app.exec()