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)
Related
import sys
from PyQt5.QtWidgets import *
import pyqtgraph as pg
import logging
import threading
from tqdm import tqdm
import time
class MyWindow(QMainWindow):
def __init__(self):
super().__init__()
self.plot_widget = pg.PlotWidget()
self.setCentralWidget(self.plot_widget)
self.plot_data = None
self.setupPlotLogger(self.plot_widget)
threading.Thread(target=self.foo).start()
def foo(self):
for i in tqdm(range(100)):
time.sleep(0.1)
logging.getLogger('test').debug(i)
def setupPlotLogger(self, widget):
pl = PlotLogger()
pl.comp = widget
pl.data = self.plot_data
logging.getLogger('test').setLevel(level=logging.DEBUG)
logging.getLogger('test').addHandler(pl)
def do_task(self, value):
logging.getLogger('test').info(value)
class PlotLogger(logging.Handler):
def emit(self, record):
record = float(self.format(record))
if self.data is not None:
self.data.append(record)
else:
self.data = [record]
self.comp.plot(self.data)
if __name__ == "__main__":
app = QApplication(sys.argv)
window = MyWindow()
window.show()
app.exec_()
In above code, I have got warning message which is "QObject::startTimer: Timers cannot be started from another thread".
In this case, I wanna logging another gui component(PlotWidget) while thread something.
what i searching is the problem is call another UI component(might be PlotWidget).
Is there solution for what i want to do?
Although it might be look funny, i was findout solution myself.
so, i am shared how can i solve this problem.
the key thing is using 'invokeMethod',
# make some slot function for invokeMethod
#QtCore.Slot(str)
def test(self, value):
logging.getLogger('test').info(float(value))
def foo(self):
for i in tqdm(range(100)):
time.sleep(0.1)
# call invokeMethod
QtCore.QMetaObject.invokeMethod(self,
"test",
QtCore.Qt.QueuedConnection,
QtCore.Q_ARG(str, str(i)))
When User clicks on "Update" Push Button, box_updatetool_fcn will be executed. MainThread will update the Progressbar in the QMainWindow while the other QThread will download the file. But it executes perfectly, but the problem is signal emited by the WorkerThread is not updating immediately.
Even I have gone through many questions, none solved my problem. I don't know why my fcn_qthread_output is not executing immediately after Qthread finished.
Even the QThread finished() function also executing after the Main Function finished, which I am not using in my current program. I don't know what's wrong in the program, is something missing?
Here is the following console output -
Run Function Closed
Out1 : False, FileName
Main Function Ended
Out2 : True, FileName
What I am expecting is -
Run Function Closed
Out2 : True, FileName
Out1 : True, FileName
Main Function Ended
Below is the program execution flow -
class WorkerThread(QtCore.QThread):
outResult = QtCore.pyqtSignal(tuple)
def __init__(self,target,args,parent=None):
super(WorkerThread, self).__init__(parent)
self.fcn = target
self.args = args
def run(self):
outResult = self.fcn(*self.args)
self.outResult.emit(outResult)
print('Run Function Closed')
class ApplicationWindow(QtWidgets.QMainWindow):
def box_updatetool_fcn(self):
t1 = WorkerThread(target=self.boxapi.fcn_downloadfile, args=(fileID,)) #ThreadWithResult
t1.outResult.connect(self.fcn_qthread_output)
t1.start()
self.box_pbar_fcn(tempfilename,fsize)
print(f'Out1 : {self.var_box_output}')
self.boxapi.fcn_updatetool(file_fullpath,self.progpath)
print('Main Function Ended')
def fcn_qthread_output(self,result):
self.var_box_output = result
print(f'Out2 : {self.var_box_output}')
def box_pbar_fcn(self,filename,fullsize):
pobj = self.ui.progressbar
while(barprogress < 100):
if os.path.exists(filename):
currfilesize = os.path.getsize(filename)
barprogress = int(currfilesize/fullsize*100)
pobj.setValue(barprogress)
Reproducible Code -
I have used box_pbar_fcn (progress bar which shows the downloading progress) and t1 Thread (downloading the file) will Run in simultaneously.
import os, sys
from PyQt5 import QtWidgets, QtGui, QtCore
class WorkerThread(QtCore.QThread):
outResult = QtCore.pyqtSignal(tuple)
def __init__(self,parent=None):
super(WorkerThread, self).__init__(parent)
def run(self):
outResult = (True,'Out1')
self.outResult.emit(outResult)
print('Run Function Closed')
class ApplicationWindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self.var_box_output = (False,'Inp1')
self.box_updatetool_fcn()
def box_updatetool_fcn(self):
tempfilename,fsize = 'Test',300
t1 = WorkerThread() #ThreadWithResult
t1.outResult.connect(self.fcn_qthread_output)
t1.start()
self.box_pbar_fcn(tempfilename,fsize)
print(f'Out1 : {self.var_box_output}')
print('Main Function Ended')
def fcn_qthread_output(self,result):
self.var_box_output = result
print(f'Out2 : {self.var_box_output}')
def box_pbar_fcn(self,filename,fullsize):
pass
#pobj = self.ui.progressbar
#while(barprogress < 100):
#if os.path.exists(filename):
# currfilesize = os.path.getsize(filename)
# barprogress = int(currfilesize/fullsize*100)
# pobj.setValue(barprogress)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
mw = ApplicationWindow()
mw.show()
sys.exit(app.exec_())
The problem is that you are blocking the main thread with your while True (There should never be an instruction that blocks the eventloop for long). In Qt the logic is to do the job asynchronously. In this case I suspect that in a thread you are downloading a file and you want to show the download progress in the QProgressBar and for this you are continuously measuring the size of the file. If so, a possible solution is to use a QTimer to run the periodic task of measuring the file size.
Disclaimer: The following code that has not been tested should work. As I do not know how is the way you save the file, it could be that it does not work since many functions only write the file at the end and not in parts for efficiency reasons.
import os
import sys
from functools import cached_property
from PyQt5 import QtCore, QtGui, QtWidgets
class Downloader(QtCore.QThread):
outResult = QtCore.pyqtSignal(tuple)
def run(self):
import time
time.sleep(10)
outResult = (True, "Out1")
self.outResult.emit(outResult)
print("Run Function Closed")
class FileObserver(QtCore.QObject):
def __init__(self, parent=None):
super().__init__(parent)
self._filename = ""
self._current_size = 0
#property
def filename(self):
return self._filename
def start(self, filename, interval=100):
self._current_size = 0
self._filename = filename
self.timer.setInterval(interval)
self.timer.start()
def stop(self):
self.timer.stop()
#cached_property
def timer(self):
timer = QtCore.QTimer()
timer.timeout.connect(self._measure)
return timer
def _measure(self):
if os.path.exists(self.filename):
file_size = os.path.getsize(filename)
if self._current_size != file_size:
self._current_size = file_size
self.file_size_changed.emit(file_size)
class ApplicationWindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self.var_box_output = (False, "Inp1")
self.box_updatetool_fcn()
#cached_property
def file_observer(self):
return FileObserver()
def box_updatetool_fcn(self):
tempfilename, fullsize = "Test", 300
self.ui.progressbar.setMaximum(fullsize)
t1 = WorkerThread(self)
t1.outResult.connect(self.fcn_qthread_output)
t1.finished.connect(self.file_observer.stop)
t1.start()
self.file_observer.start(tempfilename, interval=10)
def fcn_qthread_output(self, result):
self.var_box_output = result
print(f"Out2 : {self.var_box_output}")
def box_pbar_fcn(self, file_size):
self.ui.progressbar.setValue(file_size)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
mw = ApplicationWindow()
mw.show()
sys.exit(app.exec_())
I use Websockets to retrieve data for further processing.
I can't figure out how to retrieve it outside of my class.
I use the thread module to separate the websocket from the rest of the program so I can run a pyqt5 application where I display the processed data but I can't retrieve it.
Maybe I should use something other than threads but I don't have an idea.
Since I can receive a lot of data and have a lot of work to do on it, calculations, display etc. I try to make it a minimum optimized otherwise it will never be able to handle all my requests per second.
import websockets
import asyncio
import json
import threading
import time
class WS(object):
def __init__(self, serveur):
self.serveur = serveur
async def connect(self):
async with websockets.connect(self.serveur) as websocket:
while True:
message = await websocket.recv()
self.data = json.loads(message)
print(self.data)
uri = "wss://www.bitmex.com/realtime?subscribe=instrument:XBTUSD"
ws = WS(uri)
loop = asyncio.get_event_loop()
th1 = threading.Thread(target=lambda: loop.run_until_complete(ws.connect()))
th1.start()
while True: # My application that will display and process the data retrieved by the websocket.
print('blabla')
time.sleep(3)
By default Qt does not support eventloops because in those cases using threading is usually a valid workaround, but in these cases it is better to use libraries such as qasync(python -m pip install qasync) and asyncqt(python -m pip install asyncqt). Considering that case, a possible solution is to use the Qt signals to send the information.
import asyncio
import json
import websockets
from PyQt5 import QtCore, QtWidgets
from asyncqt import QEventLoop
class WS(QtCore.QObject):
dataChanged = QtCore.pyqtSignal(dict)
def __init__(self, server, parent=None):
super().__init__(parent)
self._server = server
#property
def server(self):
return self._server
async def connect(self):
async with websockets.connect(self.server) as websocket:
while True:
message = await websocket.recv()
data = json.loads(message)
self.dataChanged.emit(data)
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.text_edit = QtWidgets.QTextEdit()
self.setCentralWidget(self.text_edit)
#QtCore.pyqtSlot(dict)
def update_data(self, data):
# only for test
text = json.dumps(data)
self.text_edit.setPlainText(text)
def main():
import sys
app = QtWidgets.QApplication(sys.argv)
loop = QEventLoop(app)
asyncio.set_event_loop(loop)
uri = "wss://www.bitmex.com/realtime?subscribe=instrument:XBTUSD"
ws = WS(uri)
w = MainWindow()
ws.dataChanged.connect(w.update_data)
w.show()
with loop:
loop.create_task(ws.connect())
loop.run_forever()
if __name__ == "__main__":
main()
Update:
import asyncio
import json
import websockets
from PyQt5 import QtCore, QtWidgets
from asyncqt import QEventLoop, asyncSlot
class WS(QtCore.QObject):
dataChanged = QtCore.pyqtSignal(dict)
def __init__(self, server, parent=None):
super().__init__(parent)
self._server = server
self._websocket = None
#property
def websocket(self):
return self._websocket
async def connect(self):
self._websocket = await websockets.connect(self._server)
await self.ready_read()
async def ready_read(self):
while True:
message = await self.websocket.recv()
data = json.loads(message)
self.dataChanged.emit(data)
#asyncSlot(dict)
async def send_message(self, message):
data = json.dumps(message)
await self.websocket.send(data)
class MainWindow(QtWidgets.QMainWindow):
sendMessageSignal = QtCore.pyqtSignal(dict)
def __init__(self, parent=None):
super().__init__(parent)
self.button = QtWidgets.QPushButton("Press me")
self.text_edit = QtWidgets.QTextEdit()
central_widget = QtWidgets.QWidget()
lay = QtWidgets.QVBoxLayout(central_widget)
lay.addWidget(self.button)
lay.addWidget(self.text_edit)
self.setCentralWidget(central_widget)
self.button.clicked.connect(self.on_clicked)
#QtCore.pyqtSlot()
def on_clicked(self):
auth_data = {"op": "subscribe", "args": ["instrument:XBTUSD"]}
self.sendMessageSignal.emit(auth_data)
#QtCore.pyqtSlot(dict)
def update_data(self, data):
# only for test
text = json.dumps(data)
self.text_edit.setPlainText(text)
def main():
import sys
app = QtWidgets.QApplication(sys.argv)
loop = QEventLoop(app)
asyncio.set_event_loop(loop)
uri = "wss://www.bitmex.com/realtime?subscribe=instrument:XBTUSD"
ws = WS(uri)
w = MainWindow()
ws.dataChanged.connect(w.update_data)
w.sendMessageSignal.connect(ws.send_message)
w.show()
with loop:
loop.create_task(ws.connect())
loop.run_forever()
if __name__ == "__main__":
main()
this code opens web site with Qt browser. If internet connection is lost I want to give Information message with QMessageBox:
import sys
from PyQt5.QtCore import *
from PyQt5.QtCore import QUrl
from PyQt5.QtWidgets import QApplication, QWidget, QMainWindow, QLabel
from PyQt5.QtWebEngineWidgets import *
from PyQt5.QtGui import QGuiApplication as App
from PyQt5.QtGui import QPixmap, QWindow
from PyQt5 import QtNetwork, QtCore, QtWidgets
import urllib
from urllib.request import urlopen
import threading
import time
class WebApp(QMainWindow):
def __init__(self):
self.is_connected = None
self.is_msgshow = True
self.msg = QtWidgets.QMessageBox()
super().__init__()
self.title = "OZI"
self.t_internet = threading.Thread(target=self.is_internet)
self.t_internet.start()
self.t_refreshpage = threading.Thread(target=self.refresh_page)
self.t_refreshpage.start()
self.web = QWebEngineView()
self.web.window().setWindowTitle(self.title)
self.web.load(QUrl("http://www.google.com"))
self.web.showFullScreen()
def is_internet(self):
"""
Query internet using python
:return:
"""
while True:
time.sleep(5)
try:
urlopen("http://www.google.com", timeout=1)
self.is_connected = True
except urllib.error.URLError as Error:
print(Error)
self.is_connected = False
print(self.is_connected)
def refresh_page(self):
while True:
time.sleep(.1)
if self.is_connected == False:
time.sleep(5)
if self.is_connected == True:
self.web.page().action(QWebEnginePage.Reload).trigger()
else:
if self.is_msgshow == True:
print('testtt')
self.msg.information(None, 'INFO', 'PLEASE CHECK YOUR INTERNET CONNECTION!!!')
self.is_msgshow = False
self.msg.close()
else:
pass
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = WebApp()
sys.exit(app.exec_())
However, I couldn't succeed at
if self.is_msgshow == True:
print('testtt')
this part while internet connection is lost my code opens many MessageBox. I guess my fault is controlling self.is_msgshow flag.
Far beyond the problem you are pointing out, you have other errors and it is more dangerous than the one indicated: You should not access the GUI from another thread, and you should also not access the same variable from 2 threads without protecting it through a mutex, semaphore or Similary. For example, you are calling self.web.page().action(QWebEnginePage.Reload).trigger() and self.msg.information(None, 'INFO', 'PLEASE CHECK YOUR INTERNET CONNECTION!!!') from the secondary thread, and the variable "is_connected" is accessed by several threads.
Another error is that method QMessage::information() is static and generates a new object that is not directly accessible, instead you must use the object "self.msg" through its own methods to display the necessary information.
Considering the above I have created a class that is only responsible for analyzing the status of the connection and if it changes to emit a signal. To send information between threads in Qt you can use signals or QMetaObject::invokedMethod() in addition to the classic mutex and semaphores.
Then it is only necessary to analyze the value of the status sent to the GUI to implement the logic:
import sys
import threading
import time
import urllib
from urllib.request import urlopen
from PyQt5 import QtCore, QtWidgets, QtNetwork, QtWebEngineWidgets
class ConnectivityManager(QtCore.QObject):
statusChanged = QtCore.pyqtSignal(bool)
def __init__(self, *, timeout=4000, parent=None):
super().__init__(parent)
self._status = False
self._timeout = timeout
def start(self):
threading.Thread(target=self._check, daemon=True).start()
#QtCore.pyqtProperty(bool, notify=statusChanged)
def status(self):
return self._status
#QtCore.pyqtSlot(bool)
def _update_status(self, status):
if self._status != status:
self._status = status
self.statusChanged.emit(self.status)
def _check(self):
while True:
try:
urlopen("http://www.google.com", timeout=1)
status = True
except urllib.error.URLError as Error:
status = False
QtCore.QMetaObject.invokeMethod(
self,
"_update_status",
QtCore.Qt.QueuedConnection,
QtCore.Q_ARG(bool, status),
)
time.sleep(5)
class WebApp(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.setWindowTitle("OZI")
self.web = QtWebEngineWidgets.QWebEngineView()
self.setCentralWidget(self.web)
self.web.load(QtCore.QUrl("http://www.google.com"))
self.connectivity_manager = ConnectivityManager()
self.connectivity_manager.statusChanged.connect(self.on_status_changed)
self.connectivity_manager.start()
self.msg = QtWidgets.QMessageBox()
self.msg.setWindowTitle("INFO")
self.msg.setText("PLEASE CHECK YOUR INTERNET CONNECTION!!!")
#QtCore.pyqtSlot(bool)
def on_status_changed(self, status):
if status:
self.msg.hide()
self.statusBar().showMessage("Connected")
self.web.page().action(QtWebEngineWidgets.QWebEnginePage.Reload).trigger()
else:
self.statusBar().showMessage("Disconnected")
self.msg.show()
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
ex = WebApp()
ex.showFullScreen()
sys.exit(app.exec_())
On the other hand, the same logic can be implemented using QtNetwork without the need for threads:
import sys
from PyQt5 import QtCore, QtWidgets, QtNetwork, QtWebEngineWidgets
class ConnectivityManager(QtCore.QObject):
statusChanged = QtCore.pyqtSignal(bool)
def __init__(self, *, timeout=4000, parent=None):
super().__init__(parent)
self._status = False
self._timeout = timeout
self.manager = QtNetwork.QNetworkAccessManager()
self._timer = QtCore.QTimer(
singleShot=True, interval=self._timeout, timeout=self.verify_status
)
def start(self):
QtCore.QTimer.singleShot(0, self._check)
#QtCore.pyqtProperty(bool, notify=statusChanged)
def status(self):
return self._status
#QtCore.pyqtSlot(bool)
def _update_status(self, status):
if self._status != status:
self._status = status
self.statusChanged.emit(self.status)
def _check(self):
url = QtCore.QUrl("https://www.google.com/")
req = QtNetwork.QNetworkRequest(url)
self._reply = self.manager.get(req)
self._reply.finished.connect(self.verify_status)
self._timer.start()
#QtCore.pyqtSlot()
def verify_status(self):
if self._timer.isActive():
self._timer.stop()
if self._reply.error() == QtNetwork.QNetworkReply.NoError:
v = self._reply.attribute(
QtNetwork.QNetworkRequest.HttpStatusCodeAttribute
)
if 200 <= v < 300:
self._update_status(True)
else:
print("error", "code error: {}".format(v))
self._update_status(False)
else:
print("error", self._reply.errorString())
self._update_status(False)
else:
self._reply.finished.disconnect(self.verify_status)
self._reply.abort()
print("Timeout")
self._update_status(False)
QtCore.QTimer.singleShot(5000, self._check)
self._reply.deleteLater()
class WebApp(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.setWindowTitle("OZI")
self.web = QtWebEngineWidgets.QWebEngineView()
self.setCentralWidget(self.web)
self.web.load(QtCore.QUrl("http://www.google.com"))
self.connectivity_manager = ConnectivityManager()
self.connectivity_manager.statusChanged.connect(self.on_status_changed)
self.connectivity_manager.start()
self.msg = QtWidgets.QMessageBox()
self.msg.setWindowTitle("INFO")
self.msg.setText("PLEASE CHECK YOUR INTERNET CONNECTION!!!")
#QtCore.pyqtSlot(bool)
def on_status_changed(self, status):
if status:
self.msg.hide()
self.statusBar().showMessage("Connected")
self.web.page().action(QtWebEngineWidgets.QWebEnginePage.Reload).trigger()
else:
self.statusBar().showMessage("Disconnected")
self.msg.show()
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
ex = WebApp()
ex.showFullScreen()
sys.exit(app.exec_())
I do not know the pyqt5 at all but guessing by flow of your code, here:
if self.is_msgshow == True:
print('testtt')
self.msg.information(None, 'INFO', 'PLEASE CHECK YOUR INTERNET CONNECTION!!!')
self.is_msgshow = False
self.msg.close()
It seems the self.msg.information() call is expected to be synchronous. If that is so, then as long as it remains open, the is_msgshow is still True because you changing it once you got dialog closed. And this is your bug as if new even occurs then nothing blocks new dialog from being shown. The fix is pretty simple - just move self.is_msgshow = False to be very first thing done in that code block, you should be good:
if self.is_msgshow:
self.is_msgshow = False
print('testtt')
self.msg.information(None, 'INFO', 'PLEASE CHECK YOUR INTERNET CONNECTION!!!')
self.msg.close()
Additional note, you may want to reset it back to True once connectivity is restored otherwise you won't show a thing next time network is down.
The QMessageBox class provides a modal dialog for informing the user or for asking the user a question and receiving an answer.
https://doc.qt.io/qtforpython/PySide2/QtWidgets/QMessageBox.html
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()