Python Forum
[Tkinter] Updating variables in an "after" loop
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
[Tkinter] Updating variables in an "after" loop
#1
Hello everyone,

I'm new here, and also quite new with Python. I'have been only for some months learning it and enjoying it :). Right now i'm writing a GUI for my master thesis in physics education and need some help. Basically i have made a small measuring devices with a RasPi and different sensors, and I am making a GUI for showing the kids the data live while we are measuring. That has worked quite well with a TkAgg canvas and an embedded pyplot figure and an animation.

For retrieving the data I've used the after method like this:

def tick():
    single_anzeige=random.random()*100
    values_anzeige.append(single_anzeige)
    anzeige.config(text='%07.3f °C' % (single_anzeige))
    values_y.append(single_anzeige) if len(values_anzeige)%5 == 0 else 0
    anzeige.after(100, tick)
Btw, for testing i'm collecting right now just random numbers generated with the random function. Values are collected in two variables (values anzeige for the raw data and values_y that are shown later in the graph).

And here comes finally my question: I've tried to make a button for reseting the data or for interacting somehow with it but once the after loop is on, it doesn't care anymore about global variables. It simply keeps running with the values it collected at the beginning. So is there any way of forcing the tick function to update the global values at some point?

I'm not sure if my question even makes sense at all. I'm still learning and haven't reached yet a lot of stuff like classes etc, but I'm open to every advice and piece of info you can give me :)

Thanks in advance and have a nice day.

Somehow I have made it work by avoiding reassigning the values to different objects, but making only in-place changes.

Somehow I have made it work by avoiding reassigning the values to different objects, but making only in-place changes.

[quote="omrot" pid="49272" dateline="1528352330"]Hello everyone, I'm new here, and also quite new with Python. I'have been only for some months learning it and enjoying it :). Right now i'm writing a GUI for my master thesis in physics education and need some help. Basically i have made a small measuring devices with a RasPi and different sensors, and I am making a GUI for showing the kids the data live while we are measuring. That has worked quite well with a TkAgg canvas and an embedded pyplot figure and an animation. For retrieving the data I've used the after method like this:
 def tick(): single_anzeige=random.random()*100 values_anzeige.append(single_anzeige) anzeige.config(text='%07.3f °C' % (single_anzeige)) values_y.append(single_anzeige) if len(values_anzeige)%5 == 0 else 0 anzeige.after(100, tick) 
Btw, for testing i'm collecting right now just random numbers generated with the random function. Values are collected in two variables (values anzeige for the raw data and values_y that are shown later in the graph). And here comes finally my question: I've tried to make a button for reseting the data or for interacting somehow with it but once the after loop is on, it doesn't care anymore about global variables. It simply keeps running with the values it collected at the beginning. So is there any way of forcing the tick function to update the global values at some point? I'm not sure if my question even makes sense at all. I'm still learning and haven't reached yet a lot of stuff like classes etc, but I'm open to every advice and piece of info you can give me :) Thanks in advance and have a nice day.
Somehow I have made it work by avoiding reassigning the values to different objects, but making only in-place changes.

OMG Now I've made amess here XD
Reply
#2
Hi omrot

What's about that:
import random
import tkinter as tk

values_anzeige = list()
values_y = list()

def clear_values():
    global values_anzeige, values_y
    print("Before clearing:", values_anzeige, values_y)
    values_anzeige = []
    values_y = []
    print("After clearing:", values_anzeige, values_y)
        
def tick():
    global values_anzeige, values_y
    single_anzeige = random.random()*100
    values_anzeige.append(single_anzeige)
    anzeige.config(text='%07.3f °C' % (single_anzeige))
    values_y.append(single_anzeige) if len(values_anzeige)%5 == 0 else 0
    anzeige.after(100, tick)

app_win = tk.Tk()

main_frame = tk.Frame(app_win)
main_frame.pack(padx=10, pady=10)

anzeige = tk.Label(main_frame, width=20, bg='white')
anzeige.pack(pady=2)

clear_button = tk.Button(main_frame, text="Clear Values", command=clear_values)
clear_button.pack(pady=2)

tick()

app_win.mainloop()
wuf ;-)
Reply
#3
Global variables do not work well IMHO, and you should be using a class structure for any GUI, IMHO again, as a class easily does things that cause problems when using globals. A simple example to do what I think your lengthy post is asking.
try:
    import Tkinter as tk     ## Python 2.x
except ImportError:
    import tkinter as tk     ## Python 3.x

import random

class ShowSomething():
    def __init__(self, root):
        self.root=root
        self.lab=tk.Label(self.root, text="start", width=20)
        self.lab.grid(row=0, column=0)
        tk.Button(self.root, text="reset", bg="lightblue",
                  command=self.reset).grid(row=10, column=0, sticky="ew")

        self.tick()

    def reset(self):
        ## cancel any waiting after
        self.root.after_cancel(self.id)
        self.lab.config(text="0")

        ## start it again after 1/2 second so you can see it
        self.root.after(500, self.tick)

    def tick(self):
        single_anzeige=random.random()*100
        self.lab.config(text=single_anzeige)

        ## increase time to 1/2 second so you can see
        ## what is going on
        ## also, save the id so reset() can cancel any after 
        self.id=self.root.after(500, self.tick)

root=tk.Tk()
S=ShowSomething(root)
root.mainloop() 
Reply
#4
Hi woooee

Thanks for your improved version.

wuf ;-)
Reply
#5
Wow! That's what i was trying to do. Thank you very much! I need to learn a bit more about classes to understand totally how does it work but i think i see it :)
Reply
#6
Every once in a while I will code a GUI that's supposed to be simple, and so don't use classes. It always backfires, and I have to spend time re-coding. Tkinter tutorial using classes http://python-textbok.readthedocs.io/en/...mming.html
Reply
#7
Hi omrot

Here an other solution without using globals. Insted of creating a class you can use the object reference of the app_win (root) to bind variables. The following script is fetching values of a running counter based on that idea (Including the know how of woooee about the after methode):
import random
import tkinter as tk

LOOP_CYCLE_TIME = 200

def break_loop(event):
    app_win.after_cancel(app_win.loop_id)
    
    #--- Begin: Do something komplex
    stop_value = app_win.var_count.get()
    app_win.var_sample.set(stop_value)
    #--- End: Do something komplex
    
    loop(stop_value)
        
def loop(count=0):
    app_win.var_count.set(count)
    app_win.loop_id = app_win.after(LOOP_CYCLE_TIME, loop, count+1)


app_win = tk.Tk()

main_frame = tk.Frame(app_win)
main_frame.pack(padx=10, pady=10)

app_win.var_count = tk.IntVar(0)
tk.Label(main_frame, textvariable=app_win.var_count, width=8, bg='white'
    ).pack(pady=2)

app_win.var_sample = tk.IntVar(0)
tk.Label(main_frame, textvariable=app_win.var_sample, width=8, bg='yellow'
    ).pack(pady=2)

fetch_button = tk.Button(main_frame, text="Fetch Sample")
fetch_button.pack(pady=2)
fetch_button.bind('<Button-1>', break_loop)

loop()

app_win.mainloop()
wuf ;-)
Reply
#8
ow, also a really nice solution. I like it!! :)
Reply
#9
That only works because app_win is a global variable. Also, it is the same thing, only you are creating an instance object of thd Tk() class instead of your own class. For simple programs this is fine, but for more complex programs, you want to keep track of your own variables with your own class.
Reply
#10
Yes, I've noticed that I lost completely the scope of some of the variables and then i had problems for making everything work. Now I'm working on the code again and trying to build everything up with classes. So far it's going well, but I need to learn more about classes. :)
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  [PyQt] updating references to data variables across widgets Oolongtea 1 1,624 Feb-09-2021, 07:53 PM
Last Post: deanhystad

Forum Jump:

User Panel Messages

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