Reputation: 85
So what I want to do is that there will be a function from script B which will be called inside the script A. So that function will be displaying some output on the console via print, but what I want is that the output should also be displayed on a widget, and the script A is the one where all my GUI operations happen.
So what I did was that I passed a function X as a argument to the function Y in script B, thus, when that output was printed in the function Y, I simply referenced that into a variable and passed that variable into the function X of script A... which I passed as argument. Thus, that function simply inserted the output on the text widget.
But the problem is that the function Y was called after a button click, and due to some reason the button freezes for some time and then displays the final output all at once. But this is not what I want. I want that the output to be displayed at the same time as it is displayed in the console, one after another, that way. But it seems like that the button widget resumes when the entire function passed in the command argument has finished running.
To solve this I tried using sleep and after functions but they don't seem to be helping me that much. Thus, I have tried to recreate my problem in a more simpler way and I have tried it doing it via sleep and after, but none of em seems to work for me.
So the codes are below, though they don't exactly match my problem but I hope they are able to explain my question more clearly.
So say we have two scripts A and B
In the script A -
from time import sleep
# will output a number every 1 second on the console
def Show(number, Function):
while(number < 5):
sleep(1) # Wait specified time
number += 1 # Some random operation, here incrementing the number by 1
print(number) # On console
Function(number) # On widget
In the script B -
import A
import tkinter as tk
number = 0
root = tk.Tk()
# Function which will be passed as an argument
def Print(number):
label = tk.Label(root, text=number)
label.pack()
# Will be used for the after method [ OPTIONAL ]
def Delay(number, Print):
root.after(5000, test.Show(number, Print))
# Below I recommend to comment either one of the button initializations, in order to test each ways
# Using sleep method
button = tk.Button(root, text='Start', command=lambda: A.Show(number, Print))
#OR
# Using after method
button = tk.Button(root, text='Start', command=lambda: Delay(number, Print))
button.pack()
tk.mainloop()
So my point is that, I want to show the numbers on any widget ( in my real problem it is a text widget ) at the same time as it is actually happening, i.e displaying on the console.
UPDATE: This is not the actual problem, it is just a simplified version of my actual problem. So don't assume that I am trying to make this code over complicated. As I am training a classifier with a trained NN so the output is printed every iteration on the console. So what I want to achieve is that the output be printed on the text widget at the same time as well during the ongoing loop.
UPDATE 2: It is finally working as I wanted it to be. The answer is to use threading as described by Mike :D
Upvotes: 0
Views: 1194
Reputation: 15236
sleep()
and tkinter
do not get along. Nor does while
. The problem with sleep and while is that they block the main loop until they are complete and thus nothing updates in your GUI until they have finished. That said I think you are making this code more complex then it needs to be. You have 2 functions for something that can simply be done in one and you are passing functions to functions. Way more complex than need be.
You are also packing a new label every time print is called. Please try to keep to the PEP8 standard for naming your function. all_low_with_underscores for standard functions and variables.
The method after()
is specifically designed to handle timed events within tkinter and is mainly used to replace sleep
within the GUI.
Here is your code simplified and using after()
:
import tkinter as tk
def delay_and_print():
global number
if number < 5:
print(number)
label.config(text=number)
number += 1
root.after(1000, delay_and_print)
root = tk.Tk()
number = 0
tk.Button(root, text='Start', command=delay_and_print).pack()
label = tk.Label(root, text='')
label.pack()
root.mainloop()
Here is an example using threading:
import tkinter as tk
import threading
from time import sleep
def delay_and_print():
global number
if number < 100:
print(number)
label.config(text=number)
number += 1
sleep(2)
delay_and_print()
def start_thread():
thread = threading.Thread(target=delay_and_print)
thread.start()
def do_something():
print('Something')
root = tk.Tk()
number = 0
tk.Button(root, text='Start', command=start_thread).pack()
tk.Button(root, text='some other button to test if app is responsive while loop is going!', command=do_something).pack()
label = tk.Label(root, text='')
label.pack()
root.mainloop()
Result:
Upvotes: 2