Goal: push a button to show and change the background of elements from a list one by one with a delay of n seconds until the entire text is highlighted.
I wrapped:
text2.insert(INSERT,sonsilabo,"silabo")
text2.pack()
time.sleep(2)
in a for-loop to do that for each element.
But the program still wait for the entire cycle to finish and show the final result.
from Tkinter import *
import Tkinter
import time
root = Tk()
text2 = Text(root)
text2.tag_config("silabo",background="green")
teksto = ['Sa','lu','to','n',' ','mi',' ','no','mi','ĝa','s',' ','A','b','de','l']
def kolorigu():
text2.delete('1.0', END)
for sonsilabo in teksto:
text2.insert(INSERT,sonsilabo,"silabo")
text2.pack()
time.sleep(2)
text2.pack()
B = Button(root, text ="Kolorigu", command = kolorigu)
B.pack()
root.mainloop()
Any idea?
After you add some text, you need to update the textbox by calling text2.update_idletasks:
def kolorigu():
text2.delete('1.0', END)
for sonsilabo in teksto:
text2.insert(INSERT,sonsilabo,"silabo")
########################
text2.update_idletasks()
########################
time.sleep(2)
Also, I removed the text2.pack() line inside kolorigu because it is unnecessary.
Related
i was wondering if it would be possible to output text slowly using tkinter and a label
the main problem is I don't know how to get it to flush. I press the button, and then it waits 9 seconds, then it prints the whole thing.
How do i get this to work with a flush
i've tried label1.flush(), but it gives me an error
also sys.stdout.flush() does't work, but it makes sense because this is to the console
def clicked():
txt = "hello world"
a = txt[0]
label1 = tk.Label(window,text=a)
label1.place(x=60,y=30)
time.sleep(9)
b = txt[1]
c = a+b
label1 = tk.Label(window,text=c)
label1.place(x=60,y=30)
Here is a way to do this using .after loops:
from tkinter import Tk, Label
def print_slow(widget: Label, text, delay: 'in miliseconds', index=1, start_index=0):
widget.config(text=text[start_index: index])
index += 1
return root.after(delay, print_slow, widget, text, delay, index) if index <= len(text) else None
root = Tk()
label = Label(root, text='')
label.pack()
print_slow(label, 'hello world', 2000)
root.mainloop()
So pretty simple:
First import only what You need, don't use wildcard (*)
Second define the function, it will take arguments such as the widget to which it is needed to print slowly, then the text that has to be printed, then delay in milliseconds between characters appearing on the screen, then the end index and start index.
Then simple tkinter stuff:
.config() the widget's text attribute to the text from start_index to the end index
Then increment the end index by one
Then use .after() to schedule the same function (loop) after the given delay and pass the incremented index argument
Then the simple tkinter stuff and also remember to initially call the function
EDIT: updated the function to actually stop when finished, used some ternary conditions to return None (basically stop in this case) if the index is bigger than the lenght of the text
You should not use time.sleep with tkinter as it blocks the GUI processing. This causes it to print the whole thing in one go. Flushing is not possible because this is a Tkinter GUI, not a console, so there is no stdout to flush. Instead, you have to do something like this:
import tkinter as tk
def clicked():
n = 0
txt = "hello world"
showChar(n, txt)
def showChar(n, txt):
n += 1
label.config(text = txt[:n])
if n < len(txt):
root.after(9000, lambda: showChar(n, txt))
root = tk.Tk()
label = tk.Label(root, text = "")
label.pack()
button = tk.Button(root, text = "Start", command = clicked)
button.pack()
root.mainloop()
When the button is clicked, the counter is initialised (set to 0) and the text is set. Then showChar is called. This function increments the counter, then updates the label text to show an extra character. If the string hasn't been full displayed yet, it waits 9000ms (or 9 seconds) until it runs itself again, using the tkinter method root.after. This then works as you want it to.
I got a tkinter app and a thread which writes some data to a file. If I let the thread finish it's job, the file is empty. If I terminate the program before the thread is done (clicking the red square in pyCharm) the file is filled with the data till the point of termination. Here is a code to reproduce the problem:
import tkinter as tk
import _thread
import numpy as np
img_list = []
def create_img_list():
for i in range(1000):
img = np.random.rand(385, 480)
img = img * 65535
img = np.uint16(img)
img_list.append(img)
def write_to_file():
f = open("test.Raw", "wb")
for img in img_list:
f.write(img)
f.close()
root = tk.Tk()
button = tk.Button(root, text="Click Me", command=_thread.start_new_thread(write_to_file, ())).pack()
create_img_list()
root.mainloop()
What is going on here and how do I fix it?
When I add print(img_list) to write_to_file() I see that this function is executed at start - without clicking button - even before create_img_list() runs (which creates list) so write_to_file() writes empty list.
You use command= incorrectly. It needs function name without () (so called "callback") but you run function and you assign its result to command=. Your code works like
result = _thread.start_new_thread(write_to_file, ()) # it executes function at start
button = tk.Button(root, text="Click Me", command=result).pack()
but you need
def run_thread_later():
_thread.start_new_thread(write_to_file, ())
button = tk.Button(root, text="Click Me", command=run_thread_later).pack()
Eventually you can uses lambda to create this function directly in command=
button = tk.Button(root, text="Click Me", command=lambda:_thread.start_new_thread(write_to_file, ())).pack()
BTW: you have common mistake
button = Button(...).pack()
which assign None to variable because pack()/grid()/place() return `None.
If you need access button later then you have to do it in two lines
button = Button(...)
button.pack()
If you don't need access button later then you can skip `button()
Button(...).pack()
I´m trying to use tkinter in order to show a popup window in front of all other windows that reminds me of something through a text displayed in the canvas. I want that the window pops up during some time, eg 5 seconds, and then dissapear for other time. I need this to repeat in a loop. When i tried to do it, a window appear but without the text and the specified dimensions. Here is the code:
from tkinter import *
from time import sleep as s
for i in range(5):
root = Tk()
root.lift()
root.attributes('-topmost',True)
canvas = Canvas(root,width=700,height=100)
canvas.pack()
canvas.create_text(350,50,text='Registrar rabia'+str(i),font=
('fixedsys','25'))
print('Hola')
s(1)
root.destroy()
s(1)
Also, is there a more pythonic way to do it?
EDIT:
from tkinter import *
root = Tk()
for _ in range(6):
#root.deiconify()
root.attributes('-topmost',True)
root.title("About this application...")
about_message = 'Este es el mensaje'
msg = Message(root, text=about_message)
msg.pack()
button = Button(root, text="Dismiss", command=root.withdraw)
button.pack()
root.after(1000)
It didn´t work. I need only one message and one button, and in the code above,
python shows 6 messages and 6 buttons... Also i need to put a delay between appearances but i can´t understand how to use the after method in this particular case.
Im trying to make a little program that endlessly prints out numbers inside GUI window, I can not find a way to print the out put of the function in a text box inside the GUI window instead of the python shell, please help, here is my code so far...
import sys
from tkinter import *
root = Tk()
def number(event):
x = 420
while True:
x +=420
print(x^70)
button_1 = Button(root, text="Start...")
button_1.bind("<Button-1>", number)
button_1.pack()
root.mainloop()
Thanks Harvey
You'll find it hard to constantly insert a value into a widget. The widget does not update with each insert. You can think of it has having a temporary variable for it. It can be accessed during the loop (as shown with print). However you'll notice that the widget itself doesn't update until the loop is over. So if you have while True then your widget will never update, and so you won't have the numbers streaming into the widget.
import sys
from tkinter import *
root = Tk()
def number():
x = 420
while x < 8400: # Limit for example
x +=420
textbox.insert(END, str(x^70)+'\n')
print(textbox.get(1.0, END)) # Print current contents
button_1 = Button(root, text="Start...", command=number) #Changed bind to command, bind is not really needed with a button
button_1.pack()
textbox = Text(root)
textbox.pack()
root.mainloop()
I want to introduce some delay in my code. I am using text.after() to get the delay but my tkinter window opens after that specified delay and the ouput is already printed on it.
Basically I am trying to replicate the functionality of sleep(), that is the lines of code should get executed before the timer starts. Then the timer should introduce 4 secs of delay and then the following lines of code should execute. I want to automate some test equipment and I want the next equipment to turn on after 4 secs of delay after the previous equipment was turned on.
To explain this situation, I will use a very simple code. I want the "Start Timer" to appear first. Then I want the numbers 1,2,3,4 appear on the tkinter GUI after 1 sec interval, like a timer going on from 1 to 4. Then I want "Stop Timer to appear". In this way the execution of the code is delayed by 4sec in between.
Is there a way to do this ?
Here is my updated example code:
from Tkinter import *
root = Tk()
text = Text(root)
text.pack()
def append_number(n):
text.insert(END, str(n) + "\n")
text.see(END)
# add one, and run again in one second
if n > 0:
root.after(1000, append_number, n-1)
# start the auto-running function
text.insert(END, "Start Timer")
append_number(5)
text.insert(END, "Stop Timer")
root.mainloop()
You can use after to cause a function to run in the future. A simple solution is to call that immediately in the loop and schedule all of the updates:
from Tkinter import *
root = Tk()
text = Text(root)
text.pack()
def append_number(n):
text.insert(END, str(n) + "\n")
text.see(END)
for i in range(1, 5, 1):
root.after(i*1000, append_number, i)
root.mainloop()
Another common pattern is to have the function automatically reschedule itself, and then quit after a certain condition. For example:
from Tkinter import *
root = Tk()
text = Text(root)
text.pack()
def append_number(n):
text.insert(END, str(n) + "\n")
text.see(END)
# add one, and run again in one second
if n < 4:
root.after(1000, append_number, n+1)
# start the auto-running function
append_number(1)
root.mainloop()
Change the loop into a function that you can call and don't run a loop inside of it, instead keep track of the values as they change:
from Tkinter import *
root = Tk()
text = Text(root)
text.pack()
def updater(i, j):
if i <= j:
text.insert(END, i)
text.insert(END, "\n")
text.yview_pickplace("end")
i += 1
text.after(1000, updater, *[i, j])
root.after(1000, updater, *[0, 10])
root.mainloop()
This of course is a very general example that you will need to form to fit your own application.