Trying to open a PyQtGraph window in PyQt5 - python

My OptionsViz class is working on its own. However, when I throw in the asyncio stuff it doesn't show anything updating. The loop is needed for code that I have removed for brevity, so please don't throw that away.
import sys
import asyncio
from qasync import QEventLoop
from PyQt5.QtWidgets import QApplication, QMainWindow, QDockWidget
class OptionViz:
def __init__(self, app):
self.app = app
self.p = pg.plot()
self.p.setWindowTitle("pyqtgraph example: PlotSpeedTest")
self.p.setRange(QtCore.QRectF(0, -10, 5000, 20))
self.p.setLabel("bottom", "Index", units="B")
self.curve = self.p.plot()
self.data = np.random.normal(size=(50,5000))
self.ptr = 0
self.lastTime = time()
self.fps = None
timer = QtCore.QTimer()
timer.timeout.connect(self.update)
timer.start(0)
def update(self):
self.curve.setData(self.data[self.ptr%10])
self.ptr += 1
now = time()
dt = now - self.lastTime
self.lastTime = now
if self.fps is None:
fps = 1.0/dt
else:
s = np.clip(dt*3., 0, 1)
self.fps = self.fps * (1-s) + (1.0/dt) * s
self.p.setTitle('%0.2f fps' % fps)
self.app.processEvents() ## force complete redraw for every plot
async def main(app):
# some await task here
viz = OptionViz(app)
# more await code here
if __name__ == '__main__':
app = QApplication(sys.argv)
loop = QEventLoop(app)
asyncio.set_event_loop(loop)
loop.create_task(main(app))
loop.run_forever()

Qt is not compatible with asyncio so several libraries have been implemented such as quamash, asyncqt, qasync, etc. to make it compatible. In the case of quamash and asyncqt they have a bug that does not allow to execute but the qasync library so that it has solved it(execute pip install qasync to install the package).
On the other hand, the main method is not awaitable since it does not execute a time-consuming task but it is executed instantly so I have had to restructure your project:
import sys
import asyncio
from qasync import QEventLoop
from PyQt5 import QtCore, QtWidgets
import pyqtgraph as pg
import numpy as np
class OptionViz:
def __init__(self, app):
self.app = app
p = pg.plot()
p.setWindowTitle("pyqtgraph example: PlotSpeedTest")
p.setRange(QtCore.QRectF(0, -10, 5000, 20))
p.setLabel("bottom", "Index", units="B")
self.curve = p.plot()
async def main(viz):
data = np.random.normal(size=(50, 5000))
ptr = 0
while True:
viz.curve.setData(data[ptr % 10])
await asyncio.sleep(0.1)
ptr += 1
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
loop = QEventLoop(app)
asyncio.set_event_loop(loop)
viz = OptionViz(app)
loop.create_task(main(viz))
loop.run_forever()
Again, the "main" function only creates an "OptionViz" object that does not take a long time is not awaitable so it is absurd to use async in that case. It seems that the OP does not understand the operation of asyncio.
By restructuring your code we can make the function awaitable so OptionViz must be a QObject to use the asyncSlot decorator, in addition the QTimer must be a child of the QObject so that its life cycle increases.
import sys
import asyncio
from qasync import QEventLoop, asyncSlot
from PyQt5 import QtCore, QtWidgets
import pyqtgraph as pg
import numpy as np
from time import time
class OptionViz(QtCore.QObject):
def __init__(self, app):
super().__init__()
self.app = app
self.p = pg.plot()
self.p.setWindowTitle("pyqtgraph example: PlotSpeedTest")
self.p.setRange(QtCore.QRectF(0, -10, 5000, 20))
self.p.setLabel("bottom", "Index", units="B")
self.curve = self.p.plot()
self.data = np.random.normal(size=(50, 5000))
self.ptr = 0
self.lastTime = time()
self.fps = None
timer = QtCore.QTimer(self)
timer.timeout.connect(self.update)
timer.start(0)
#asyncSlot()
async def update(self):
self.curve.setData(self.data[self.ptr % 10])
self.ptr += 1
now = time()
dt = now - self.lastTime
self.lastTime = now
if self.fps is None:
fps = 1.0 / dt
else:
s = np.clip(dt * 3.0, 0, 1)
self.fps = self.fps * (1 - s) + (1.0 / dt) * s
self.p.setTitle("%0.2f fps" % fps)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
loop = QEventLoop(app)
asyncio.set_event_loop(loop)
viz = OptionViz(app)
loop.run_forever()

Related

Emit signals from Worker threads to Main window and back using #pyqtSlot decorators

I'm currently learning to build GUI's using PyQt5 and here is my problem:
I have 2 Threads. The first one is QThread object which generates some data.
The other one is QObject function which runs in QThread using .moveToThread method and processes data received from my so-called data generator. On main window I emit signal to do some stuff with value returned from generator separately from main thread in order to avoid freezing GUI.
The processing looks like:
receive data;
append it to list;
if list is full enough to make some calculations -> emit the result back to GUI;
wait some time (e.g. 3 secs) and start again the reception of data.
The problem is in the last point. I freeze the thread after calculations on list items, but it does not block signal emitted from the GUI, so when time.sleep(3) is up, my list already contains 3 items, but I need to start it from 0.
Commented fragments of code are my thoughts on how to do it using #pyqtSlot decorators and QtCore.pyqtSignal() but it doesn`t work.
Let me know if any clarification is needed, maybe I missed something.
Will be glad for any help.
Here is my code:
import sys
from PyQt5.QtWidgets import QWidget, QPushButton, QApplication, QGridLayout, QLabel
from PyQt5.QtCore import QObject, QThread, pyqtSlot
from PyQt5 import QtCore
import random
import time
class DataThread(QThread):
output = QtCore.pyqtSignal(list)
def __init__(self):
super().__init__()
self._run_flag = True
def run(self):
while self._run_flag:
value = round(random.random(), 4)
value_x2 = value * 2
values = [value, value_x2]
self.output.emit(values)
# print(value)
time.sleep(1)
def stop(self):
self._run_flag = False
self.quit()
class CalculationsThread(QObject):
# accepting signals from main data generator thread
calculations_result = QtCore.pyqtSignal(float)
calc_value = QtCore.pyqtSignal(float)
# emitting signal from this thread
kill = QtCore.pyqtSignal()
deny_access = QtCore.pyqtSignal(bool)
def __init__(self, parent=None):
super(CalculationsThread, self).__init__(parent)
self.list_of_heights = []
#pyqtSlot(float)
def work(self, value):
# self.deny_access.emit(True)
self.list_of_heights.append(value)
self.calculations_result.emit(len(self.list_of_heights))
if len(self.list_of_heights) == 10:
result = sum(self.list_of_heights) / len(self.list_of_heights)
self.calculations_result.emit(result)
self.list_of_heights = []
# self.deny_access.emit(False)
time.sleep(3)
class WinForm(QWidget):
variable_value = QtCore.pyqtSignal(float)
def __init__(self, parent=None):
super(WinForm, self).__init__(parent)
self.setWindowTitle('QThreads example')
self.change_flag = 0
self.calculations_flag = 0
self.list_from_data_generator_thread = []
self.thread = None
self.calculations_worker = None
self.thread1 = None
self.label = QLabel('Label')
self.startBtn = QPushButton('Start')
self.startBtn.clicked.connect(self.start_data_generator)
self.endBtn = QPushButton('Stop')
self.endBtn.clicked.connect(self.stop_data_generator)
self.output1_btn = QPushButton("Value 1")
self.output1_btn.clicked.connect(self.show_value_1)
self.output2_btn = QPushButton("Value 2")
self.output2_btn.clicked.connect(self.show_value_2)
self.calculations_label = QLabel("Calculations result: ")
self.calculations_button = QPushButton("Start calculations")
self.calculations_button.clicked.connect(self.start_calculations)
layout = QGridLayout()
layout.addWidget(self.label, 0, 0)
layout.addWidget(self.startBtn, 1, 0)
layout.addWidget(self.endBtn, 1, 1)
layout.addWidget(self.output1_btn, 2, 0)
layout.addWidget(self.output2_btn, 2, 1)
layout.addWidget(self.calculations_label, 3, 0)
layout.addWidget(self.calculations_button, 3, 1)
self.setLayout(layout)
#pyqtSlot(list)
# #pyqtSLot(bool)
def set_label_text(self, value):
# if access_flag:
self.variable_value.emit(value[1])
# if not access flag:
# pass
if self.change_flag == 0:
self.label.setText(f"1: {value[0]}")
if self.change_flag == 1:
self.label.setText(f"2: {value[1]}")
def start_data_generator(self):
self.startBtn.setEnabled(False)
self.endBtn.setEnabled(True)
self.thread = DataThread()
self.thread.output.connect(self.set_label_text)
self.thread.start()
self.endBtn.clicked.connect(self.thread.stop)
def stop_data_generator(self):
self.thread.output.disconnect(self.set_label_text)
self.startBtn.setEnabled(True)
self.endBtn.setEnabled(False)
#pyqtSlot(float)
def output_calculations(self, value):
self.calculations_label.setText(f"Calculations result: {value}")
def start_calculations(self):
self.calculations_worker = CalculationsThread()
self.thread1 = QThread()
self.calculations_worker.moveToThread(self.thread1)
self.variable_value.connect(self.calculations_worker.work)
self.calculations_worker.calculations_result.connect(self.output_calculations)
self.calculations_worker.kill.connect(self.thread1.quit)
self.thread1.start()
self.calculations_button.clicked.disconnect(self.start_calculations)
self.calculations_button.setText("Stop calculations")
self.calculations_button.clicked.connect(self.thread1.quit)
self.calculations_button.clicked.connect(self.stop_calculations)
def stop_calculations(self):
self.calculations_worker.calculations_result.disconnect(self.output_calculations)
self.calculations_button.clicked.disconnect(self.stop_calculations)
self.calculations_button.clicked.disconnect(self.thread1.quit)
self.calculations_button.setText("Start calculations")
self.calculations_button.clicked.connect(self.start_calculations)
def show_value_1(self):
self.change_flag = 0
def show_value_2(self):
self.change_flag = 1
if __name__ == '__main__':
app = QApplication(sys.argv)
form = WinForm()
form.show()
sys.exit(app.exec_())

Store audio from QAudioInput and pass it to SciPy FFT

how can I get audio input in real time from QAudioInput, store it in a NumPy array and pass it to SciPy FFT? What I have tried:
from PyQt5.QtMultimedia import QAudioDeviceInfo, QAudioFormat, QAudioInput
import sys
class Window(QMainWindow):
def __init__(self):
info = QAudioDeviceInfo()
input_device = info.defaultInputDevice()
if input_device.isNull():
# If no avaiable device is found, we display a error
print("There is no audio input device available.")
exit(-1)
audio_format = QAudioFormat()
audio_format.setSampleRate(44100)
audio_format.setSampleSize(8)
audio_format.setChannelCount(1)
audio_format.setCodec("audio/pcm")
audio_format.setSampleType(QAudioFormat.UnSignedInt)
if sys.byteorder == "little":
audio_format.setByteOrder(QAudioFormat.LittleEndian)
else:
audio_format.setByteOrder(QAudioFormat.BigEndian)
self.audioInput = QAudioInput(input_device, audio_format, self)
self.ioDevice = self.audioInput.start()
self.ioDevice.readyRead.connect(self.read_audio)
def read_audio(self):
data: QByteArray = self.ioDevice.readAll()
print(data.toUInt()) # Prints (0, False) which means error converting data
Inspired by the official example Audio Example I have created a QIODevice that allows obtaining the data. The following example takes the last N samples every T seconds by calculating its fft and displaying it using matplotlib.
import sys
import collections
from functools import cached_property
from PyQt5.QtCore import QIODevice, QObject, pyqtSignal, QTimer
from PyQt5.QtMultimedia import QAudioDeviceInfo, QAudioFormat, QAudioInput
from PyQt5.QtWidgets import QApplication, QMainWindow
from matplotlib.backends.backend_qt5agg import FigureCanvas
from matplotlib.figure import Figure
from scipy.fft import fft, fftfreq
import numpy as np
FS = 44100
SAMPLE_COUNT = 2 * 1000
class AudioDevice(QIODevice):
data_changed = pyqtSignal(list, name="dataChanged")
def __init__(self, interval=1000, parent: QObject = None):
super().__init__(parent)
self.m_buffer = collections.deque(
[0 for _ in range(SAMPLE_COUNT)], maxlen=SAMPLE_COUNT
)
self.timer.timeout.connect(self.send_data)
self.timer.setInterval(interval)
self.timer.start()
#cached_property
def timer(self):
return QTimer()
def send_data(self):
self.data_changed.emit(list(self.m_buffer))
def readData(self, data, max_size):
return -1
def writeData(self, data):
max_size = len(data)
resolution = 4
start = 0
available_samples = int(max_size) // resolution
if available_samples < self.m_buffer.maxlen:
start = self.m_buffer.maxlen - available_samples
pos = 0
for _ in range(start, self.m_buffer.maxlen):
y = (1.0 * (data[pos] - 128)) / 128.0
self.m_buffer.append(y)
pos += resolution
return (self.m_buffer.maxlen - start) * resolution
class PlotWidget(QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.canvas = FigureCanvas(Figure(figsize=(5, 3)))
self.setCentralWidget(self.canvas)
self.ax = self.canvas.figure.subplots()
self._line = None
def update_data(self, data):
T = 1 / FS
N = SAMPLE_COUNT
yf = fft(data)
xf = fftfreq(N, T)[: N // 2]
x = xf
y = 2.0 / N * np.abs(yf[0 : N // 2])
if self._line is None:
(self._line,) = self.ax.plot(x, y)
else:
self._line.set_data(x, y)
self.canvas.draw()
def main(args):
app = QApplication(args)
plot_widget = PlotWidget()
plot_widget.resize(640, 480)
plot_widget.show()
info = QAudioDeviceInfo()
input_device = info.defaultInputDevice()
if input_device.isNull():
print("There is no audio input device available.")
exit(-1)
audio_format = QAudioFormat()
audio_format.setSampleRate(FS)
audio_format.setSampleSize(8)
audio_format.setChannelCount(1)
audio_format.setCodec("audio/pcm")
audio_format.setSampleType(QAudioFormat.UnSignedInt)
if sys.byteorder == "little":
audio_format.setByteOrder(QAudioFormat.LittleEndian)
else:
audio_format.setByteOrder(QAudioFormat.BigEndian)
audio_input = QAudioInput(input_device, audio_format, None)
audio_device = AudioDevice(interval=100)
audio_device.data_changed.connect(plot_widget.update_data)
audio_device.open(QIODevice.WriteOnly)
audio_input.start(audio_device)
app.exec_()
if __name__ == "__main__":
main(sys.argv)
data.toUInt() converts whole byte array to one uint value - not what you want. To get sample values you can use either numpy.frombuffer or struct.unpack.
import numpy
def read_audio(self):
data = self.ioDevice.readAll()
values = numpy.frombuffer(data.data(), dtype=numpy.uint8)
or
import struct
def read_audio(self):
data = self.ioDevice.readAll()
fmt = "#{}B".format(data.size())
values = struct.unpack(fmt, data.data())
I added widget that shows waveform to demonstrate that samples actually reflect signal from microphone - not random numbers.
from PyQt5.QtMultimedia import QAudioDeviceInfo, QAudioFormat, QAudioInput
from PyQt5.QtWidgets import QMainWindow, QApplication, QWidget
from PyQt5.QtGui import QPainter, QPolygonF
from PyQt5.QtCore import QPointF
import sys
import numpy
class WaveWidget(QWidget):
def __init__(self, parent = None):
super().__init__(parent)
self._values = None
def setValues(self, values):
self._values = values
self.update()
def paintEvent(self, event):
if self._values is None:
return
painter = QPainter(self)
ys = self._values / 255 * self.height()
xs = numpy.linspace(0, self.width(), num = len(ys))
points = QPolygonF([QPointF(x,y) for x,y in zip(xs,ys)])
painter.drawPolyline(points)
class Window(QMainWindow):
def __init__(self):
super().__init__()
info = QAudioDeviceInfo()
input_device = info.defaultInputDevice()
if input_device.isNull():
# If no avaiable device is found, we display a error
print("There is no audio input device available.")
exit(-1)
audio_format = QAudioFormat()
audio_format.setSampleRate(44100)
audio_format.setSampleSize(8)
audio_format.setChannelCount(1)
audio_format.setCodec("audio/pcm")
audio_format.setSampleType(QAudioFormat.UnSignedInt)
if sys.byteorder == "little":
audio_format.setByteOrder(QAudioFormat.LittleEndian)
else:
audio_format.setByteOrder(QAudioFormat.BigEndian)
self.audioInput = QAudioInput(input_device, audio_format, self)
self.ioDevice = self.audioInput.start()
self.ioDevice.readyRead.connect(self.read_audio)
widget = WaveWidget()
self._widget = widget
self.setCentralWidget(widget)
def read_audio(self):
data = self.ioDevice.readAll()
values = numpy.frombuffer(data.data(), dtype=numpy.uint8)
self._widget.setValues(values)
if __name__ == "__main__":
app = QApplication([])
window = Window()
window.show()
app.exec()

Real time plotting of BLE data using Bleak and PyQtGraph packages

I am trying to plot sensor data in real time using an ESP32-based sensor and BLE. I've attempted to use Bleak and combine the simple scrolling example from the PyQtGraph package. I know that the sensor data is been read correctly from the ESP32, but there is no plot appearing, so clearly integrating the code incorrectly.
My current code:
import asyncio
from bleak import BleakClient
import time
import pyqtgraph as pg
from pyqtgraph.Qt import QtCore, QtGui
import numpy as np
# BLE peripheral ID
address = "6C9F597F-7085-4AAB-806B-D2558588D50D"
UUID = "beb5483e-36e1-4688-b7f5-ea07361b26a8"
# Plot details
win = pg.GraphicsLayoutWidget(show=True)
win.setWindowTitle('pyqtgraph example: Scrolling Plots')
p1 = win.addPlot()
data1 = np.zeros(5)
curve1 = p1.plot(data1)
async def run(address, loop):
global data1
while True:
time.sleep(5)
async with BleakClient(address, loop=loop) as client:
data = await client.read_gatt_char(UUID)
#Parse sensor data from BLE characteristic
sensor_data = data.decode('utf_8')
print(sensor_data)
sensor_data = sensor_data.split(",")
temperature = sensor_data[4]
#Update the array with newest data so plot will appear scroll
def update():
print(temperature)
data1[:-1] = data1[1:]
data1[-1] = temperature
curve1.setData(data1)
timer = pg.QtCore.QTimer()
timer.timeout.connect(update)
timer.start(1000)
loop = asyncio.get_event_loop()
loop.run_until_complete(run(address, loop))
if __name__ == '__main__':
QtGui.QApplication.instance().exec_()
You should never use time.sleep in pyqtgraph.
pyqtgraph does not support asyncio by default but you can use the asyncqt module(python -m pip install asyncqt) or qasync module (python -m pip install qasync) that creates a new eventloop that does support Qt.
import asyncio
import pyqtgraph as pg
from pyqtgraph.Qt import QtCore, QtGui
import numpy as np
from bleak import BleakClient
from asyncqt import QEventLoop, asyncSlot
# BLE peripheral ID
address = "6C9F597F-7085-4AAB-806B-D2558588D50D"
UUID = "beb5483e-36e1-4688-b7f5-ea07361b26a8"
class Window(pg.GraphicsLayoutWidget):
def __init__(self, loop=None, parent=None):
super().__init__(parent)
self._loop = loop
self.setWindowTitle("pyqtgraph example: Scrolling Plots")
plot = self.addPlot()
self._data = np.zeros(5)
self._curve = plot.plot(self.data)
self._client = BleakClient(address, loop=self._loop)
#property
def client(self):
return self._client
async def start(self):
await self.client.connect()
self.start_read()
async def stop(self):
await self.client.disconnect()
#property
def data(self):
return self._data
#property
def curve(self):
return self._curve
async def read(self):
data = await self.client.read_gatt_char(UUID)
sensor_data = data.split(",")
if len(sensor_data) >= 5:
temperature_str = sensor_data[4]
try:
temperature = float(temperature_str)
except ValueError as e:
print(f"{temperature_str} not is float")
else:
self.update_plot(temperature)
QtCore.QTimer.singleShot(5000, self.start_read)
def start_read(self):
asyncio.ensure_future(self.read(), loop=self._loop)
def update_plot(self, temperature):
self.data[:-1] = self.data[1:]
self.data[-1] = temperature
self.curve.setData(self.data)
def closeEvent(self, event):
super().closeEvent(event)
asyncio.ensure_future(self.client.stop(), loop=self._loop)
def main(args):
app = QtGui.QApplication(args)
loop = QEventLoop(app)
asyncio.set_event_loop(loop)
window = Window()
window.show()
with loop:
asyncio.ensure_future(window.start(), loop=loop)
loop.run_forever()
if __name__ == "__main__":
import sys
main(sys.argv)

pyqt5 QTimer.singleShot doesn't run at all

(Python 3.7.3, PyQt5m MacOS Mojave)
In the code that follows I was hoping to see
start_batch
item
item
item
item
returned from start_batch
but what I actually get is
start_batch
returned from start_batch
That is, simulated_item is never running, not even once.
What do I need to do to correct this?
from PyQt5.QtCore import QTimer, QCoreApplication, QThread
import time, sys
class Scanner(object):
def __init__(self):
self.num = -1
def start_batch(self, operator_id, update_callback):
print('start_batch')
self.update_callback = update_callback
QTimer.singleShot(1, self.simulated_item)
def simulated_item(self):
print('item')
self.update_callback()
self.num += 1
if self.num > 4:
self.normal_stop_callback()
return
QTimer.singleShot(100, self.simulated_item)
class dummy(QThread):
def update(self):
print('update')
def run(self):
scnr = Scanner()
scnr.start_batch('opid', self.update)
print('returned from start_batch')
for i in range(10):
time.sleep((0.2))
app = QCoreApplication([])
thread = dummy()
thread.run()
In your code you have the error that you are calling the run method directly but that is not appropriate but you have to use the start() method:
app = QCoreApplication([])
thread = dummy()
thread.start()
app.exec_()
But you still won't get what you want.
Many elements such as the case of QTimer (also the signals) need an event loop to be able to work but when you override the run method that has it by default you have eliminated it so it fails.
So an approximation of what you want may be the following code where the QTimer uses the QCoreApplication event loop:
from PyQt5.QtCore import QTimer, QCoreApplication, QThread
import time, sys
class Scanner(object):
def __init__(self):
self.num = -1
def start_batch(self, operator_id, update_callback):
print("start_batch")
self.update_callback = update_callback
QTimer.singleShot(1, self.simulated_item)
def simulated_item(self):
print("item")
# QTimer.singleShot() is used so that the update_callback function
# is executed in the thread where the QObject to which it belongs lives,
# if it is not a QObject it will be executed in the thread
# where it is invoked
QTimer.singleShot(0, self.update_callback)
# self.update_callback()
self.num += 1
if self.num > 4:
# self.normal_stop_callback()
return
QTimer.singleShot(100, self.simulated_item)
class dummy(QThread):
def update(self):
print("update")
def run(self):
for i in range(10):
time.sleep(0.2)
print("returned from start_batch")
if __name__ == "__main__":
app = QCoreApplication(sys.argv)
thread = dummy()
thread.start()
scnr = Scanner()
thread.started.connect(lambda: scnr.start_batch("opid", thread.update))
sys.exit(app.exec_())
As discussed in the comment to #eyllansec above, I was able to create a working example which is shown below.
from PyQt5.QtCore import QTimer, QCoreApplication
import sys
class Scanner(object):
def __init__(self, app):
self.num = -1
self.app = app
def simulated_item(self):
self.num += 1
print("item", self.num)
if self.num <= 4:
QTimer.singleShot(500, self.simulated_item)
else:
self.app.exit()
if __name__ == "__main__":
app = QCoreApplication(sys.argv)
scnr = Scanner(app)
scnr.simulated_item()
sys.exit(app.exec_())

Python PyQt5 signals & slots

I am having some trouble applying the new pyqt5 signals and slots into a script thats purpose is to test/invoke another problem I've been trying to solve, GUI freezing/crashing ... the aim is so that once these signals and slots are functioning correctly the GUI will not crash after +/- 30 seconds of runtime, and just continue on counting numbers until the end of time. I have provided a pyqt4 example although it would be great to have a pyqt5 solution. Thanks :)
from time import sleep
import os
from PyQt4 import QtCore, QtGui, uic
from PyQt4.QtGui import *
import random
import os
import time
class Cr(QtCore.QThread):
def __init__(self):
QtCore.QThread.__init__(self)
def run(self):
while True:
rndInt = random.randint(1, 100000)
timesleep = random.random()
time.sleep(timesleep)
for i in range(120):
self.emit(QtCore.SIGNAL('host_UP'), 'foo' + str(rndInt), i)
QtGui.QApplication.processEvents()
class Main_Window(QWidget):
def __init__(self, *args):
QWidget.__init__(self, *args)
self.relativePath = os.path.dirname(sys.argv[0])
self.Main_Window = uic.loadUi("Main_Window.ui", self)
self.Main_Window.show()
self.Main_Window.move(790, 300)
self.GU = []
ProgressThreads = self.printThreads
self.details_label = []
for i in range(120):
self.details_label.insert(i, 0)
self.details_label[i] = QLabel(" ")
ProgressThreads.addWidget(self.details_label[i])
ProgressThreads.addSpacing(6)
self.details_label[i].setText(Tools.Trim.Short('Idle', 7))
self.GU.insert(i, Cr())
self.GU[i].start()
self.connect(self.GU, QtCore.SIGNAL("host_UP"), self.UpdateHost)
def UpdateHost(self, str1, pos1):
self.details_label[pos1].setText(str1)
class guiUpdate():
def GUI_main(self):
self.GUI = GUI
if __name__ == "__main__":
app = QApplication(sys.argv)
guiUpdate.GUI_main.GUI = Main_Window()
sys.exit(app.exec_())
Thank you for the help :)
UPDATE
The script below is a hopefully correct PyQt5 version of the script above. However the issue of crashing and 'not responding' message is still unresolved
from time import sleep
import os
from PyQt5 import QtCore, QtGui, uic
from PyQt5.QtWidgets import *
from PyQt5.QtCore import QObject, pyqtSignal
import random
import os
import time
import Tools
import sys
class Cr(QtCore.QThread):
def __init__(self, sam):
QtCore.QThread.__init__(self)
self.sam = sam
def run(self):
while True:
rndInt = random.randint(1, 100000)
timesleep = random.random()
time.sleep(timesleep)
for i in range(5):
#time.sleep(1)
self.sam.connect_and_emit_trigger('foo' + str(rndInt), i)
#self.emit(QtCore.SIGNAL('host_UP'), 'foo' + str(rndInt), i)
#QtGui.QApplication.processEvents()
class Main_Window(QWidget):
def __init__(self, *args):
QWidget.__init__(self, *args)
self.relativePath = os.path.dirname(sys.argv[0])
self.Main_Window = uic.loadUi("Main_Window.ui", self)
self.Main_Window.show()
self.Main_Window.move(790, 300)
sam = Foo()
self.GU = []
ProgressThreads = self.ProgressThreads
self.details_label = []
for i in range(5):
self.details_label.insert(i, 0)
self.details_label[i] = QLabel(" ")
ProgressThreads.addWidget(self.details_label[i])
ProgressThreads.addSpacing(6)
self.details_label[i].setText(Tools.Trim.Short('Idle', 7))
self.GU.insert(i, Cr(sam))
self.GU[i].start()
class Foo(QObject):
# Define a new signal called 'trigger' that has no arguments.
trigger = pyqtSignal()
def connect_and_emit_trigger(self, str, i):
self.str = str
self.i = i
self.trigger.connect(self.handle_trigger)
self.trigger.emit()
def handle_trigger(self):
guiUpdate.GUI_main.GUI.details_label[self.i].setText(self.str)
class guiUpdate():
def GUI_main(self):
self.GUI = GUI
if __name__ == "__main__":
app = QApplication(sys.argv)
guiUpdate.GUI_main.GUI = Main_Window()
sys.exit(app.exec_())
The new recommended way to use threads (and the one I got the best results with) is to use moveToThread() instead of directly subclassing QThread. In short:
write a QObject subclass doing the actual work (let's call it QMyWorker). This will likely look a bit like your existing qthread subclass, with a start() or run() method etc.
create a parent-less instance of QMyWorker
create a parent-less instance of QThread
use QMyWorker.moveToThread(your_thread_instance) (I go by memory, double check the API in doc).
call your QMyWorker.start()
This approach worked for me for very long jobs (4GB files etc).
I used QThreadPool, QRunnable with a worker, can make more workers per thread.
Very good example with explanation here
https://martinfitzpatrick.name/article/multithreading-pyqt-applications-with-qthreadpool/
My PYQT5 was freezing up also, now i fine tune it with printing a TimeStamp

Categories