0

I've made a script that uses a while True loop to constantly update a series of variables based on UDP packets that I am constantly recieving. I want to ultimately create a GUI that displays that data and updates the screen constantly, which I plan to do with tkinter (using my_label.after in a function which then calls itself, not sure if this is a good plan).

Here is some testing scripts that I can't get to work properly:

GUI2.py (my test looping script)

import time
var = 0
while True:
   var += 1
   time.sleep(0.1)

GUI Testing.py (the script that would be accessing those variables)

from GUI2 import *
import time
print('never')
print(var)
time.sleep(1)

The second script never reaches the print('never') line, I think because it gets stuck in the other script's while True loop and never returns.

How should I go about this? I have one script that I want in a constant loop to update my variables to the correct values based on incoming packets, and then another script updating a tkinter window. I went this way as most examples I could find using Tkinter didn't use any sort of while True loops. Could I just put my packet recieving code inside the Tkinter mainloop, and would that effectively act as a while True?

EDIT (added Tkinter loop that I can't get working):

This opens a Tkinter window, but the label stays at 99, then reopens a window when I close it with the new x value (ie. 98, 97, etc). I want the label to update every second.

import tkinter as tk
import time
x = 99
while True:
    root = tk.Tk()
    label = tk.Label(root, text=x)
    label.pack()
    x -= 1
    time.sleep(1)
    root.mainloop()
  • Being caught in an endless loop has IMO score 10 on the scale 0-10 of undesirable import side-effects. You should use the `if __name__ == "__main__":` idiom. (this is just a comment, it does not solve the problem in your question) – VPfB Jul 19 '20 at 17:45

2 Answers2

1

Below is a sample script to show you how you can update the value in the label widget at a certain time interval. I have provided you the hyperlinks to help you understand tkinter's methods. Best regards.

Key points:

  • use the textvariable option of the tk.Label widget.
  • use tkinter's control variable. I have shown you how to set and get it's value.
  • you can use tkinter's widget method called .after() without having to explicitly use a while-statement and time.sleep() method. Tkinter has it's own event loop that you can use.
  • writing your tkinter GUI as a class makes it easier to implement what you need.

Example Script:

import tkinter as tk

class App(tk.Frame):
    def __init__( self, master, *args, **kw ):
        super().__init__( master )
        self.master = master
        self.create_label()
        self.update_label()

    def create_label( self ):
        self.var = tk.IntVar() # Holds an int; default value 0
        self.label = tk.Label(self, textvariable=self.var ) # Use textvariable not text 
        self.label.pack()

    def update_label( self ):
        value = self.get_value()
        self.var.set( value ) # Set the label widget textvariable value. 
        self.after(1000, self.update_label) # Call this method after 1000 ms.
    
    def get_value( self ):
        '''To simulate calling a function to return a value'''
        value = self.var.get() + 1
        return value
    
if __name__ == "__main__":
    root = tk.Tk()
    root.geometry('100x100+0+24')

    app = App( root )
    app.pack()

    root.mainloop() #This command activates tkinter's event loop

Edit:

  • As a clarification, this answer shows how to utilize the .after() and .mainloop() methods in GUI Testing.py, i.e. using tkinter event loop and not use two while-loops, to achieve what you wanted to do. This is a way to simplify your GUI script.
  • For more sophisticated algorithms, e.g. more than one while-loop is involved, you have to look into using threads(note it has its issues) or more recently I found a way of using python's Asyncio approach to do it. The learning curve for these two approaches is a lot steeper. To use the asyncio approach, you can explore modifying my answer to do what you want.
Sun Bear
  • 7,594
  • 11
  • 56
  • 102
0

Best solution is to use threads however If you plan to do in simplest possible manner then implement the main loop inside your Tkinter GUI and once you read the packet simply update it on your GUI in same loop. Here is the Updated and working Code.

import tkinter as tk
import time


def setvalue(self, x):
    self.label.config(text=x, )
    root.update()
    time.sleep(1)

def changevalues(self):
    x = 99
    self.label = tk.Label(root, text=x)
    self.label.pack()
    while x >0:
        x -= 1
        setvalue(root,x)

root = tk.Tk()
changevalues(root)
root.mainloop()
Xythprynx
  • 39
  • 6
  • From what I can see, threading looks complicated and easy to do incorrectly, so I might steer clear of that option. What you've explained is exactly what I want to do, but I can't work out how to do it. If I have a while True loop for the reading packets, where do I put the Tkinter .mainloop? I'll update my original question with an example of a Tkinter window I can't get right. – ninjaisfast Jul 19 '20 at 18:01
  • Sure, please post your code here for better understanding – Xythprynx Jul 19 '20 at 18:04
  • I have made amendments to my answer as you have posted your origional code. – Xythprynx Jul 19 '20 at 19:35