Recover data sent via websocket - python

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()

Related

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 : Destroyed while thread is still running

Tried to add exceptions, what should the program (client) do when the server is disconnected.
The idea is simple. If the server is turned off - the client should try to connect to the server until the server is turned on.
When server is turn off - program try to connect. But when I turned server on, client is connected, and after that connection drops, and in client app I saw that - QThread: Destroyed while thread is still running
This is simple example of what I want (minimal reproducible example for client):
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
import socket
class ListeningThread(QtCore.QThread):
mysignal = QtCore.pyqtSignal(str)
def __init__(self, server_socket, parent=None):
QtCore.QThread.__init__(self, parent)
self.server_socket = server_socket
self.message = None
def run(self):
try:
while True:
self.message = self.server_socket.recv(4096)
self.mysignal.emit(self.message.decode('utf-8'))
except:
Push()
class Push(QtWidgets.QMainWindow):
def __init__(self):
super(Push, self).__init__()
print('now connecting...')
self.connect_server()
def connect_server(self):
self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
self.server_socket.connect(('127.0.0.1', 5555))
self.message_monitor = ListeningThread(self.server_socket)
self.message_monitor.mysignal.connect(self.init_UI)
self.message_monitor.start()
except:
Push()
def init_UI(self, message):
print(message)
app =QtWidgets.QApplication([])
application = Push()
sys.exit(app.exec_())
I tried that in ListeningThread:
class ListeningThread(QtCore.QThread):
mysignal = QtCore.pyqtSignal(str)
def __init__(self, server_socket, parent=None):
QtCore.QThread.__init__(self, parent)
self.server_socket = server_socket
self.message = None
def run(self):
try:
while not self.isInterruptionRequested():
self.message = self.server_socket.recv(4096)
self.mysignal.emit(self.message.decode('utf-8'))
except:
print('error in thread')
self.requestInterruption()
self.wait()
Push()
But the problem is still the same.
I think I should close the thread before it starts in Push.connect_server but idk how.
For minimal reproducible example u can use that server:
import socket
import threading
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.bind(('127.0.0.1', 5555))
server_socket.listen()
clients = []
def accept_socket():
while True:
client_socket, addr = server_socket.accept()
print(f'connection from {addr}')
print(client_socket)
if client_socket not in clients:
clients.append(client_socket)
def sending_message(clients, data):
for client_socket in clients:
try:
client_socket.send(data.encode("utf-8"))
except:
pass
accept_thread = threading.Thread(target= accept_socket)
accept_thread.start()
while True:
data = input('Enter_message:\n')
sending_message(clients, data)
The problem is that when an exception occurs you are creating a new "Push" object that has a limited scope since it is a local variable so it will be destroyed, and objects such as the thread will also be destroyed, and that is what it indicates. the error message.
Instead of complicating your life with the handling of threads (IMO they are the last option) you can use a QTcpSocket that allows to handle the sockets in a simple way through signals and that uses the Qt eventloop.
from functools import cached_property
import sys
from PyQt5 import QtCore, QtGui, QtWidgets, QtNetwork
class Client(QtCore.QObject):
messageChanged = QtCore.pyqtSignal(str)
def __init__(self, parent=None):
super().__init__(parent)
self.socket.stateChanged.connect(self.handle_state_changed)
self.socket.errorOccurred.connect(self.handle_error_occurred)
self.socket.readyRead.connect(self.handle_ready_read)
#cached_property
def socket(self):
return QtNetwork.QTcpSocket()
def try_connect(self):
self.socket.connectToHost("127.0.0.1", 5555)
def handle_state_changed(self, state):
print(f"state: {state}")
if state == QtNetwork.QAbstractSocket.UnconnectedState:
print("disconnected")
QtCore.QTimer.singleShot(1000, self.try_connect)
elif state == QtNetwork.QAbstractSocket.ConnectedState:
print("connected")
def handle_error_occurred(self, error):
print(f"error code {error}, message: {self.socket.errorString()}")
def handle_ready_read(self):
codec = QtCore.QTextCodec.codecForName("UTF-8")
message = codec.toUnicode(self.socket.readAll())
self.messageChanged.emit(message)
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.client.messageChanged.connect(self.handle_message_changed)
self.client.try_connect()
#cached_property
def client(self):
return Client()
def handle_message_changed(self, message):
print(f"client message: {message}")
def main():
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()

How to integrate youtube-dl in PyQt5

I'm trying to show the live progress of download in the gui. I'm getting the live progress of download in cmd but gui only updates when the download is complete.
def myhook(self,d):
self.percentage=QLabel(self)
self.d=d
if self.d['status']=='finished':
print("finished")
if self.d['status']=='downloading':
print(self.d['filename'],self.d['total_bytes'],self.d['downloaded_bytes'],self.d['elapsed'],self.d['eta']
,str(str(self.d['speed']/1000)[0:5]+'KiB/s')
,str((self.d['downloaded_bytes']*100)/self.d['total_bytes'])[0:5]+'%')
if self.d['total_bytes']>1048576:
total_size=self.d['total_bytes']/1048576
total_size=str(total_size)[0:5]+' MB'
if self.d['total_bytes']>1073741824:
total_size=self.d['total_bytes']/1073741824
total_size=str(total_size)[0:5]+' GB'
self.percentage.setText('{} {}'.format(total_size
,str((self.d['downloaded_bytes']*100)/self.d['total_bytes'])[0:5]+' %'))
self.percentage.setStyleSheet('color:white;border-bottom:2px solid orange;')
self.percentage.setFont(QFont('Arial',15))
self.percentage.resize(500,30)
self.percentage.move(320,650)
self.percentage.show()
self.url=self.urlfield.text()
print("144p")
options={'format':'bestvideo[height=144]+bestaudio/best','noplaylist':True,'postprocessors':[{'key':'FFmpegMetadata'}]
,'noprogress':True,'progress_hooks':[self.myhook]}
with youtube_dl.YoutubeDL(options) as ydll:
ydll.download(['{}'.format(self.url)])
Since many times you have wondered about how is the correct way to obtain information from youtube-dl and show it in a GUI written with pyqt5, I will take the license to go beyond the current question and show a more generic example.
The youtube_dl download method is very time consuming so it should not be executed in the main thread as it generates unwanted effects such as window freezing. On the other hand, there are 2 means of information provided by youtube_dl: the hooks and the logger. And these media are executed in the same thread as the download method, so they should not be accessed directly but through signals.
qyoutubedl.py
import threading
from PyQt5 import QtCore
import youtube_dl
class QLogger(QtCore.QObject):
messageChanged = QtCore.pyqtSignal(str)
def debug(self, msg):
self.messageChanged.emit(msg)
def warning(self, msg):
self.messageChanged.emit(msg)
def error(self, msg):
self.messageChanged.emit(msg)
class QHook(QtCore.QObject):
infoChanged = QtCore.pyqtSignal(dict)
def __call__(self, d):
self.infoChanged.emit(d.copy())
class QYoutubeDL(QtCore.QObject):
def download(self, urls, options):
threading.Thread(
target=self._execute, args=(urls, options), daemon=True
).start()
def _execute(self, urls, options):
with youtube_dl.YoutubeDL(options) as ydl:
ydl.download(urls)
for hook in options.get("progress_hooks", []):
if isinstance(hook, QHook):
hook.deleteLater()
logger = options.get("logger")
if isinstance(logger, QLogger):
logger.deleteLater()
main.py
from PyQt5 import QtWidgets
from qyoutubedl import QLogger, QHook, QYoutubeDL
from hurry.filesize import size
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.url_le = QtWidgets.QLineEdit()
self.download_btn = QtWidgets.QPushButton(self.tr("Download"))
self.progress_lbl = QtWidgets.QLabel()
self.download_pgb = QtWidgets.QProgressBar()
self.log_edit = QtWidgets.QPlainTextEdit(readOnly=True)
central_widget = QtWidgets.QWidget()
self.setCentralWidget(central_widget)
lay = QtWidgets.QGridLayout(central_widget)
lay.addWidget(QtWidgets.QLabel(self.tr("url:")))
lay.addWidget(self.url_le, 0, 1)
lay.addWidget(self.download_btn, 0, 2)
lay.addWidget(self.progress_lbl, 1, 1, 1, 2)
lay.addWidget(self.download_pgb, 2, 1, 1, 2)
lay.addWidget(self.log_edit, 3, 1, 1, 2)
self.progress_lbl.hide()
self.downloader = QYoutubeDL()
self.download_btn.clicked.connect(self.download)
self.url_le.setText("https://www.youtube.com/watch?v=BaW_jenozKc")
self.resize(640, 480)
def download(self):
qhook = QHook()
qlogger = QLogger()
url = self.url_le.text()
options = {
"format": "bestvideo[height=144]+bestaudio/best",
"noplaylist": True,
"postprocessors": [{"key": "FFmpegMetadata"}],
"noprogress": True,
"logger": qlogger,
"progress_hooks": [qhook],
}
self.downloader.download([url], options)
qhook.infoChanged.connect(self.handle_info_changed)
qlogger.messageChanged.connect(self.log_edit.appendPlainText)
self.download_pgb.setRange(0, 1)
def handle_info_changed(self, d):
if d["status"] == "downloading":
self.progress_lbl.show()
total = d["total_bytes"]
downloaded = d["downloaded_bytes"]
self.progress_lbl.setText("{} of {}".format(size(downloaded), size(total)))
self.download_pgb.setMaximum(total)
self.download_pgb.setValue(downloaded)
def main():
import sys
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()

How can give info only once with QMessageBox while internet connection is OFF?

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

Publisher() doesn't work in Thread

I am rewriting my program in MVC pattern, everything was okay, but now it's not. Publisher doesn't send message to the subscriber.
from wx.lib.pubsub import pub
from threading import Thread
Publisher = pub.Publisher()
class GameDataLoading(object):
def __init__(self):
self.view = DataLoadingView()
self.view.Center()
self.view.Show()
Publisher().subscribe(self.test, 'test')
LoadGameData().start()
def test(self, data):
print 'working'
class LoadGameData(Thread):
def __init__(self):
super(LoadGameData, self).__init__()
def run(self):
wx.CallAfter(Publisher().sendMessage, 'test')
What can be wrong in this code?
Does the code keep a reference for GameDataLoading object?
For example, the following code does not work. But if you replace GameDataLoading() with loading = GameDataLoading(), it works.
from threading import Thread
from wx.lib.pubsub import pub
import wx
Publisher = pub.Publisher()
class GameDataLoading(object):
def __init__(self):
self.view = wx.Frame(None)
self.view.Center()
self.view.Show()
Publisher().subscribe(self.test, 'test')
LoadGameData().start()
def test(self, data):
print 'working'
class LoadGameData(Thread):
def __init__(self):
super(LoadGameData, self).__init__()
def run(self):
wx.CallAfter(Publisher().sendMessage, 'test')
app = wx.PySimpleApp()
GameDataLoading()
app.MainLoop()

Categories