Python Forum
A dynamically updating GUI screen from URL
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
A dynamically updating GUI screen from URL
#1
I work on about developing a Qt GUI using Qt5 and PyQt.

I am trying to implement a display screen to Raspberry Pi that will dynamically "live update" the result to represent the rate at which the data is being transmitted in a (data/sec format). Data will be taken from URL WebPage (TCP/IP). This screen should cleanly live update. Any suggestions would be greatly appreciated. I want this to be built in as a widget within the GUI.

Detail:Data is distance measurement from ultrasonic sensor.

from PyQt5 import QtCore, QtGui, QtWidgets
import socket
HOST = '192.168.1.33'  # The server's hostname or IP address
PORT = 80              # The port used by the server

with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
    s.connect((HOST, PORT))
    s.sendall(b'Hello, world')
    data = s.recv(1024)

class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(800, 600)  #pencere boyutu
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.label = QtWidgets.QLabel(self.centralwidget)
        self.label.setGeometry(QtCore.QRect(30, 30, 600, 300))
        font = QtGui.QFont()
        font.setFamily("Calibri")
        font.setPointSize(20)
        self.label.setFont(font)
        self.label.setAlignment(QtCore.Qt.AlignCenter)
        self.label.setObjectName("label")
        self.labelDistance = QtWidgets.QLabel(self.centralwidget)
        self.labelDistance.setGeometry(QtCore.QRect(150, 150, 400, 300))
        font = QtGui.QFont()
        font.setFamily("Calibri")
        font.setPointSize(20)
        self.labelDistance.setFont(font)
        self.labelDistance.setAlignment(QtCore.Qt.AlignCenter)
        self.labelDistance.setObjectName("labelDistance")
        self.labelcm = QtWidgets.QLabel(self.centralwidget)
        self.labelcm.setGeometry(QtCore.QRect(150, 150, 700, 300))
        font = QtGui.QFont()
        font.setFamily("Calibri")
        font.setPointSize(20)
        self.labelcm.setFont(font)
        self.labelcm.setAlignment(QtCore.Qt.AlignCenter)
        self.labelcm.setObjectName("labelcm")
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 259, 21))
        self.menubar.setObjectName("menubar")
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "Measurement"))
        self.label.setText(_translate("MainWindow", "Distance"))
        self.labelDistance.setText(_translate("MainWindow", data))
        self.labelcm.setText(_translate("MainWindow", "cm"))
        
if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    MainWindow = QtWidgets.QMainWindow()
    ui = Ui_MainWindow()
    ui.setupUi(MainWindow)
    MainWindow.show()
    sys.exit(app.exec_())
Data information is showing in row 57. But, this is not dynamic. In this code, I'm seeing constant value. When distance measurement on IP change, display widget should show new data result. On my program, you can next distance result, only when run python code again. My aim is that GUI should have refresh screen. How it is possible ? (I mean that it should be updating like a clock.)
If distance change, GUI should show the twinkling of an eye.

Code output now:
İmage
Reply
#2
You should use another thread that make a request every second or so, and emit a signal to update the value of your GUI.

#!/usr/bin/python3
# Threading example with QThread and moveToThread (PyQt5)
import sys
import time
from PyQt5 import QtWidgets, QtCore

class WorkerThread(QtCore.QObject):
    signalExample = QtCore.pyqtSignal(str, int)

    def __init__(self):
        super().__init__()

    @QtCore.pyqtSlot()
    def run(self):
        while True:
            # Long running task ...
            self.signalExample.emit("leet", 1337)
            time.sleep(5)

class Main(QtWidgets.QMainWindow):
    def __init__(self):
        super().__init__()
        self.worker = WorkerThread()
        self.workerThread = QtCore.QThread()
        self.workerThread.started.connect(self.worker.run)  # Init worker run() at startup (optional)
        self.worker.signalExample.connect(self.signalExample)  # Connect your signals/slots
        self.worker.moveToThread(self.workerThread)  # Move the Worker object to the Thread object
        self.workerThread.start()

    def signalExample(self, text, number):
        print(text)
        print(number)

if __name__== '__main__':
    app = QtWidgets.QApplication([])
    gui = Main()
    sys.exit(app.exec_())
Reply
#3
(Apr-04-2019, 07:20 PM)Alfalfa Wrote: You should use another thread that make a request every second or so, and emit a signal to update the value of your GUI.

#!/usr/bin/python3
# Threading example with QThread and moveToThread (PyQt5)
import sys
import time
from PyQt5 import QtWidgets, QtCore

class WorkerThread(QtCore.QObject):
    signalExample = QtCore.pyqtSignal(str, int)

    def __init__(self):
        super().__init__()

    @QtCore.pyqtSlot()
    def run(self):
        while True:
            # Long running task ...
            self.signalExample.emit("leet", 1337)
            time.sleep(5)

class Main(QtWidgets.QMainWindow):
    def __init__(self):
        super().__init__()
        self.worker = WorkerThread()
        self.workerThread = QtCore.QThread()
        self.workerThread.started.connect(self.worker.run)  # Init worker run() at startup (optional)
        self.worker.signalExample.connect(self.signalExample)  # Connect your signals/slots
        self.worker.moveToThread(self.workerThread)  # Move the Worker object to the Thread object
        self.workerThread.start()

    def signalExample(self, text, number):
        print(text)
        print(number)

if __name__== '__main__':
    app = QtWidgets.QApplication([])
    gui = Main()
    sys.exit(app.exec_())

Thanks for your idea. I will. Can i combine in one main module ? You can see my all code. How to join to it ?
Reply
#4
You can, but it is usually a better practice to separate the logic from the gui, as it make the maintenance much easier as the code grows. If you want an example of how to structure your code, see this post.
Reply
#5
(Apr-04-2019, 07:20 PM)Alfalfa Wrote: You should use another thread that make a request every second or so, and emit a signal to update the value of your GUI.

#!/usr/bin/python3
# Threading example with QThread and moveToThread (PyQt5)
import sys
import time
from PyQt5 import QtWidgets, QtCore

class WorkerThread(QtCore.QObject):
    signalExample = QtCore.pyqtSignal(str, int)

    def __init__(self):
        super().__init__()

    @QtCore.pyqtSlot()
    def run(self):
        while True:
            # Long running task ...
            self.signalExample.emit("leet", 1337)
            time.sleep(5)

class Main(QtWidgets.QMainWindow):
    def __init__(self):
        super().__init__()
        self.worker = WorkerThread()
        self.workerThread = QtCore.QThread()
        self.workerThread.started.connect(self.worker.run)  # Init worker run() at startup (optional)
        self.worker.signalExample.connect(self.signalExample)  # Connect your signals/slots
        self.worker.moveToThread(self.workerThread)  # Move the Worker object to the Thread object
        self.workerThread.start()

    def signalExample(self, text, number):
        print(text)
        print(number)

if __name__== '__main__':
    app = QtWidgets.QApplication([])
    gui = Main()
    sys.exit(app.exec_())

First of al, I tried this. I added socket function, HOST adress, PORT number and some rows for client. My output should be distance "data".

self.signalExample.emit("distance", data)
.
.
.
    def signalExample(self, text, data):
        print(text)
        print(data)
But instead of data, there are many different large numbers in gui widget. I cant reach data on IP.

Maybe I must share all of them for you.
import sys
import time
import socket
from PyQt5 import QtWidgets, QtCore

HOST = '192.168.1.33'  # The server's hostname or IP address
PORT = 80        # The port used by the server

with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
    s.connect((HOST, PORT))
    s.sendall(b'Hello, world')
    data = s.recv(1024)
    print(data)

class WorkerThread(QtCore.QObject):
    signalExample = QtCore.pyqtSignal(str, int)
 
    def __init__(self):
        super().__init__()
 
    @QtCore.pyqtSlot()
    def run(self):
        while True:
            # Long running task ...
            self.signalExample.emit("distance", data)
            time.sleep(5)
 
class Main(QtWidgets.QMainWindow):
    def __init__(self):
        super().__init__()
        self.worker = WorkerThread()
        self.workerThread = QtCore.QThread()
        self.workerThread.started.connect(self.worker.run)  # Init worker run() at startup (optional)
        self.worker.signalExample.connect(self.signalExample)  # Connect your signals/slots
        self.worker.moveToThread(self.workerThread)  # Move the Worker object to the Thread object
        self.workerThread.start()
 
    def signalExample(self, text, data):
        print(text)
        print(data)
 
if __name__== '__main__':
    app = QtWidgets.QApplication([])
    gui = Main()
    sys.exit(app.exec_())
Output:
PS C:\Users\User> & "C:/Program Files/Python37/python.exe" c:/Users/User/Desktop/PYQT/time_socket.py b'52' distance 1895228464 distance 1895228464
For example, 52 cm is right but i cant understand what is other big number.
Reply
#6
(Apr-05-2019, 01:34 PM)Alfalfa Wrote: You can, but it is usually a better practice to separate the logic from the gui, as it make the maintenance much easier as the code grows. If you want an example of how to structure your code, see this post.

Also, I checked your post. And i applied according to my program.

import sys
import time
#import random
import socket
from PyQt5 import QtWidgets, QtCore

class WorkerThread(QtCore.QObject):
    signalExample = QtCore.pyqtSignal(int)
 
    def __init__(self):
        super().__init__()
 
    @QtCore.pyqtSlot()
    def run(self):
        while True:
            HOST = '192.168.1.43'  # The server's hostname or IP address, NodeMCU IP Address
            PORT = 80              # The port used by the server, PORT NUMBER in ARDUINO CODE
            with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
                s.connect((HOST, PORT))
                s.sendall(b'Hello, world')
                data = s.recv(1024)
            print('Received duration value', repr(data)) 
            self.signalExample.emit(data)
            time.sleep(1)
 
class WorkerLabel(QtWidgets.QLabel):
    def __init__(self, parent):
        super().__init__()
 
    @QtCore.pyqtSlot(int)
    def slot(self, i):
        self.setText(str(i))
 
class UserInterface(QtWidgets.QWidget):
    def __init__(self, parent):
        super().__init__()
        self.label = WorkerLabel(self)
        self.layout = QtWidgets.QVBoxLayout()
        self.layout.addWidget(self.label)
        self.setLayout(self.layout)
 
class Main(QtWidgets.QMainWindow):
    def __init__(self):
        super().__init__()
        self.ui = UserInterface(self)
        self.setCentralWidget(self.ui)
 
        self.worker = WorkerThread()
        self.workerThread = QtCore.QThread()  # Move the Worker object to the Thread object
        self.workerThread.started.connect(self.worker.run)  # Init worker run() at startup
        self.worker.moveToThread(self.workerThread)
        self.worker.signalExample.connect(self.signalExample)
        self.workerThread.start()
 
        self.show()
    def signalExample(self, number):
        print(number)
  
 
if __name__== '__main__':
    app = QtWidgets.QApplication([])
    gui = Main()
    sys.exit(app.exec_())
Output:
PS C:\Users\User> & "C:/Program Files/Python37/python.exe" c:/Users/User/Desktop/PYQT/gui_1.py Received duration value b'51' 1265033024 Received duration value b'52' 1265030864
51 & 52 cm is right. But i dont know why it shows big number.
Reply
#7
(Apr-08-2019, 09:14 AM)bescf Wrote: 51 & 52 cm is right. But i dont know why it shows big number.

It is a Qt or a PyQt bug, it looks like it does not know how to handle the byte object properly. To solve this simply call int() on your value before emitting the signal.
Reply
#8
(Apr-08-2019, 10:09 PM)Alfalfa Wrote: It is a Qt or a PyQt bug, it looks like it does not know how to handle the byte object properly. To solve this simply call int() on your value before emitting the signal.

Dear Alfalfa, thanks your attention & helps! It's a solution.
Reply
#9
(Apr-08-2019, 10:09 PM)Alfalfa Wrote: It is a Qt or a PyQt bug, it looks like it does not know how to handle the byte object properly. To solve this simply call int() on your value before emitting the signal.

Also, I want to add a question.

class_1 = to set socket for server and client, this TCP/IP should be done one time. I am telling one time, because connecting to IP takes too long. And consequently, it shows data every 5 sec.
class_2 = to take data from socket. But this process should be on while loop.

My question: How I should occur this ? I have to create two class, first is socket, second is data=s.recv(512).

I tried but when classes are different, output gave an error " "data" is undefined ".
Reply
#10
Hard to tell, but you can easily put them in the same class. Simply make one blocking method for the initial connection and call it before the while loop. If the connection is successful, return True and run the loop until the connection is closed.
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  A dynamically updating screen for PyQt GUI from URL bescf 0 2,631 Mar-25-2019, 06:58 AM
Last Post: bescf

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020