I'm very new to tkinter. How do I get a value to display every time it changes? The code below only shows the last entry in the Entry widget, but print shows all the values (python 3.6.0)
from tkinter import *
def start():
for n in range(1000000):
if n%100000 == 0:
v.set(str(n))
def makeWindow () :
global v
win = Tk()
frame1 = Frame(win)
frame1.pack()
Label(frame1, text="Number").grid(row=0, column=0, sticky=W)
v = StringVar()
m = Label(frame1, textvariable=v)
m.grid(row=0, column=1, sticky=W)
frame2 = Frame(win)
frame2.pack()
b1 = Button(frame2,text=" Start ",command=start)
b1.pack(side=LEFT)
return win
win = makeWindow()
win.mainloop()
it doesnt look like the problem is with GUI, it looks like your problem is with your programming.
Quote:def start():
for n in range(1000000):
if n%100000 == 0:
v.set(str(n))
Every time you hit the button you execute start(). This function sets the string var (v) to every 100K (0-900000) in a split second. The last value 900000 gets seen as the label because its the last iteration and the completion of the function. Thus you will not see any other number in the label like you would with the print function...assuming this is what you wanted to do?
EDIT:
Do you mean something like this? I would suggest to use classes instead of global keywords.
from tkinter import *
class Values:
def __init__(self):
self.vals = []
self.index = 0
self.assign()
def assign(self):
'''get our values'''
for n in range(1000000):
if n%100000 == 0:
self.vals.append(n)
def reset(self):
'''what do we do when we iterate all values? Start at 0'''
if self.index > len(self.vals)-1:
self.index = 0
def callback(self):
'''execute when button gets pressed'''
self.reset()
v.set(self.vals[self.index])
self.index += 1
win = Tk()
v = StringVar()
obj = Values()
frame1 = Frame(win)
frame1.pack()
Label(frame1, text="Number").grid(row=0, column=0, sticky=W)
m = Label(frame1, textvariable=v)
m.grid(row=0, column=1, sticky=W)
frame2 = Frame(win)
frame2.pack()
b1 = Button(frame2,text=" Start ",command=obj.callback)
b1.pack(side=LEFT)
#win = makeWindow()
win.mainloop()
Hi metulburr.
Thanks for the quick reply. Your solution is not quite what I want. I have been "programming" in python for quite a few years as a means of solving some cryptographic puzzles, but without using classes. I just use python as a type of programmable calculator to manipulate text to find solutions to problems. Now I have retired I thought I would try to make my programs look a bit smarter. Some of the programs I write take days to execute, and I have been using print to show me that the program is still running and to give me an idea of how far it has progressed. I was trying to use tkinter to display the results on screen every time that a particular variable changed without me having to press the "start" button each time. The example I posted was just a simple program that I hoped would show me the count updating in the Entry box each time it changed, as it does on the console if I put a "print(n)" statement in the loop.
A metulburr said you code is updating so fast the eye cannot register the changes so you only see the last one.
If you import time and changed your example code to this
def start():
for n in range(1000000):
if n%100000 == 0:
v.set(str(n))
time.sleep(0.5)
you would see the values changing.
(Mar-31-2017, 04:38 PM)Barrowman Wrote: [ -> ]A metulburr said you code is updating so fast the eye cannot register the changes so you only see the last one.
If you import time and changed your example code to this def start():
for n in range(1000000):
if n%100000 == 0:
v.set(str(n))
time.sleep(0.5)
you would see the values changing.
But because of it being tkinter you cant use time sleep on any GUI as it blocks the execution, you have to use
after
Hi Barrowman.
Thanks for the reply. I'll have a look at the after method and see if I can get it to work.
Hi dannyH
I assume you want to update the value of a Label not Entry? If you use a Label to display a changing value the following script may help you:
(Use of
threading and
after())
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import threading
try:
# Tkinter for Python 2.xx
import Tkinter as tk
import Queue as qu
except ImportError:
# Tkinter for Python 3.xx
import tkinter as tk
import queue as qu
APP_TITLE = "Update Label Display"
APP_XPOS = 100
APP_YPOS = 100
APP_WIDTH = 300
APP_HEIGHT = 200
UPDATE_TIME = 500 # Milliseconds
QUEUE_SIZE = 10
POLLING_TIME = 500 # Milliseconds
class AppThread(threading.Thread):
def __init__(self, queue=None, max_number=0, modulo=0):
self.queue = queue
self.max_number = max_number
self.modulo = modulo
threading.Thread.__init__(self)
self.start()
def run(self):
num_count = 0
while num_count < self.max_number:
if num_count%self.modulo == 0:
self.update_queue(num_count)
num_count += 1
def update_queue(self, num_count):
self.queue.put(num_count)
self.queue.join()
class Application(tk.Frame):
def __init__(self, master):
self.master = master
self.master.protocol("WM_DELETE_WINDOW", self.close)
tk.Frame.__init__(self, master)
self.max_number = 1000000
self.modulo = 100000
self.app_thread = None
self.control_frame = tk.Frame(self)
self.control_frame.pack(expand=True)
tk.Label(self.control_frame, text="Number:").pack(fill='x', pady=(0,1))
self.var = tk.StringVar()
tk.Label(self.control_frame, relief='sunken', bg='white', width=10,
textvariable=self.var).pack(fill='x', pady=(0,10))
tk.Button(self.control_frame, text=" Start ",
command=self.start_thread).pack(fill='x')
self.queue = qu.Queue(QUEUE_SIZE)
self.queue_polling()
def start_thread(self):
if self.app_thread == None:
self.app_thread = AppThread(
self.queue, self.max_number, self.modulo)
else:
if not self.app_thread.isAlive():
self.app_thread = AppThread(
self.queue, self.max_number, self.modulo)
def queue_polling(self):
if self.queue.qsize():
try:
data = self.queue.get()
self.var.set(str(data))
self.queue.task_done()
except qu.Empty:
pass
self.after(POLLING_TIME, self.queue_polling)
def close(self):
print("Application-Shutdown")
self.app_thread.join()
self.master.destroy()
def main():
app_win = tk.Tk()
app_win.title(APP_TITLE)
app_win.geometry("+{}+{}".format(APP_XPOS, APP_YPOS))
app_win.geometry("{}x{}".format(APP_WIDTH, APP_HEIGHT))
app = Application(app_win).pack(fill='both', expand=True)
app_win.mainloop()
if __name__ == '__main__':
main()
wuf

Many thanks. That is the functionality I was looking for, although the code is a bit more complicated than I am used to writing. Now that I have more free time I will investigate tkinter, classes and threading and hopefully write better python programs in the future.
The problem with the original code was not speed - I slowed it down by a factor of 100 so that it took 32 seconds to run, but still the only value that appeared in the window was the last one to be calculated.