def click():
button1.configure(bg="gray")
time.sleep(1)
button1.configure(bg="green")
button1 = Button(win, text="Button",bg="green",activebackground="red")
button1.pack()
I tryied to change button to gray for only second than change back to green. But it won't change to gray
You need to bind that event after the packing
from tkinter import *
import time
def click(event):
if button1["background"] == "green":
time.sleep(3)
button1["background"] = "yellow"
else:
button1["background"] = "green"
root = Tk()
myContainer1 = Frame(root)
myContainer1.pack()
button1 = Button(myContainer1, text="Button", bg="green")
button1.pack()
button1.bind("<Button-1>", click) # binding event here
root.mainloop()
btw, solid resource on the subject, a bit dated but as an educational material - written perfectly - short :D
http://thinkingtkinter.sourceforge.net/
You have to do it this way.
If you use the Time library, the software won't work
or you can use the Threading module for multithreading in Python but This method is a bit complicated
from tkinter import *
def click(event):
def loop(i):
if i==1:
button1.configure(bg="gray")
elif i==2:
button1.configure(bg="red")
return
i=i+1
button1.after (1000, lambda : loop (i))
loop (1)
root = Tk()
myContainer1 = Frame(root)
myContainer1.pack()
button1 = Button(myContainer1, text="Button", bg="red")
button1.pack()
button1.bind("<Button-1>", click) # binding event here
root.mainloop()
First click() has never been executed. Second all updates are handled by tkinter mainloop(), so those changes will be handled after the function click() exits and the last change will be seen only.
You can use .after() instead of sleep() to change the color of the button back to green after one second:
def click():
button1.configure(bg="gray")
# change the background color back to green after 1 second
button1.after(1000, lambda: button1.configure(bg="green"))
button1 = Button(win, text="Button", bg="green", activebackground="red", command=click)
button1.pack()
Related
Here is an example of what I am trying to convey:
from tkinter import *
def start():
print("Start")
B1.pack_forget()
B2.pack()
def stop():
B2.pack_forget()
B1.pack()
root = Tk()
root.title("TestWin")
B1 = Button(root, text='start', command=start)
B1.pack()
B2 = Button(root, text='stop', command=stop)
root.mainloop()
Now I want to use if logic to get the name of the button currently packed. And it could look something like:
if <button_text_keyword> == 'start' then print('Start') elif <button_text_keyword> == 'stop' then print("Stop").
Can this be done??? Or Do I have to type a long code in order to achieve that???
Please suggest a good method to do what I want or rectify me.
Just to be clear, if the goal is just to print whatever text the button is showing, the simplest is to just have separate command functions, as you have done. It's then explicitly clear which button was there at the time of the click. So by this I mean, just add a print statement in the stop function.
But assuming there is a more nuanced situation where you actually need to determine what is packed in a different part of the program, this is an example based on what #acw1668 suggested in the comments:
from tkinter import *
packed = 'B1'
def start():
global packed
B1.pack_forget()
B2.pack()
packed = 'B2'
def stop():
global packed
B2.pack_forget()
B1.pack()
packed = 'B1'
def check():
if packed == 'B1':
print("Start")
elif packed == 'B2':
print("Stop")
root = Tk()
root.title("TestWin")
B1 = Button(root, text='start', command=start)
B1.pack(side=TOP)
B2 = Button(root, text='stop', command=stop)
B3 = Button(root, text='check', command=check)
B3.pack(side=BOTTOM)
root.mainloop()
The new button ("check") determines what button is packed, based on the flag packed (just to note, if you instead wrap the application in class (see here), you could avoid having to use global).
The above is more than sufficient, but if you actually want to check the button object itself, you can check the packed elements in a Frame with pack_slaves():
from tkinter import *
def start():
B1.pack_forget()
B2.pack()
def stop():
B2.pack_forget()
B1.pack()
def check():
global button_frame
button = button_frame.pack_slaves()[0]
text = (button.cget('text'))
if text == 'start':
print('Start')
elif text == 'stop':
print('Stop')
root = Tk()
root.title("TestWin")
button_frame = Frame(root,)
button_frame.pack(side=TOP)
B1 = Button(button_frame, text='start', command=start)
B1.pack()
B2 = Button(button_frame, text='stop', command=stop)
B3 = Button(root, text='check', command=check)
B3.pack(side=BOTTOM)
root.mainloop()
This second example sounds more similar to the logic you described; i.e. find object corresponding to the pressed button, then get its text using cget, and route that to determine what is printed. Note that this example finds the button widget by using button = button_frame.pack_slaves()[0], so there is an assumption that there will only be clearly one button packed in button_frame.
Another option would be to use bind to program the button's effects, rather than the command parameter. You could then create a function like check in the above example to bind to both buttons, and you could use this technique to get the pressed widget (and then check it's identity/text, determine what to print, etc....).
If you want that on clicking the button your button text will get change then you try #this
from tkinter import *
root = Tk()
root.title("title")
def start():
#for changing text on button
button_name.config(text="new text on button")
B1 = Button(root, text='start', command=start)
B1.pack()
Imagine the following simple example:
def doNothing():
sleep(0.5)
barVar.set(10)
sleep(0.5)
barVar.set(20)
sleep(0.5)
barVar.set(30)
mainWindow = Tk()
barVar = DoubleVar()
barVar.set(0)
bar = Progressbar(mainWindow, length=200, style='black.Horizontal.TProgressbar', variable=barVar, mode='determinate')
bar.grid(row=1, column=0)
button= Button(mainWindow, text='Click', command=doNothing)
button.grid(row=0, column=0)
mainWindow.mainloop()
What I get when I run this, the progressbar is already at 30% when clicking the button, no progress in front of me. Like attached:
What I need: I can see the progress in front of me (not hanging then suddenly 30%)
Update:
I upadted the code according to #Bernhard answer, but still I can not see the progress in front of me. Just a sudden jump of 30% after waiting 1.5 sec
Seocnd Update:
I'm only using sleep here as a simulation for a process that takes time, like connecting over ssh and grabing some info.
Do not use sleep() in tkinter. The entire reason for you problem is sleep() will freeze tkinter until it is done with its count so what you are seeing is a frozen program and when the program is finally released its already set to 30 percent on the next mainloop update.
Instead we need to use Tkinter's built in method called after() as after is specifically for this purpose.
import tkinter as tk
import tkinter.ttk as ttk
mainWindow = tk.Tk()
def update_progress_bar():
x = barVar.get()
if x < 100:
barVar.set(x+10)
mainWindow.after(500, update_progress_bar)
else:
print("Complete")
barVar = tk.DoubleVar()
barVar.set(0)
bar = ttk.Progressbar(mainWindow, length=200, style='black.Horizontal.TProgressbar', variable=barVar, mode='determinate')
bar.grid(row=1, column=0)
button= tk.Button(mainWindow, text='Click', command=update_progress_bar)
button.grid(row=0, column=0)
mainWindow.mainloop()
If you want the bar to appear to move smoothly you will need to speed up the function call and reduce the addition to the DoubbleVar.
import tkinter as tk
import tkinter.ttk as ttk
mainWindow = tk.Tk()
def update_progress_bar():
x = barVar.get()
if x < 100:
barVar.set(x+0.5)
mainWindow.after(50, update_progress_bar)
else:
print("Complete")
barVar = tk.DoubleVar()
barVar.set(0)
bar = ttk.Progressbar(mainWindow, length=200, style='black.Horizontal.TProgressbar', variable=barVar, mode='determinate')
bar.grid(row=1, column=0)
button= tk.Button(mainWindow, text='Click', command=update_progress_bar)
button.grid(row=0, column=0)
mainWindow.mainloop()
Because you are calling the function when the buttion is initialized, you need to loose the '(barVar') in the command=(barVar)). This way you bind the function to the button and don't call it when initializing it.
button= Button(mainWindow, text='Click', command=doNothing)
If you need to pass an argument you need to bypass the calling by using lambda:
button= Button(mainWindow, text='Click', command= lambda: doNothing(barVar))
I think I find the solution.
simply add mainWindow.update() after each progress. So the final code would be:
def doNothing():
sleep(0.5)
barVar.set(10)
mainWindow.update()
sleep(0.5)
barVar.set(20)
mainWindow.update()
sleep(0.5)
barVar.set(30)
mainWindow.update()
I have the following code, which causes a color/text change when a Tkinter button is clicked. I would like to revert to the original color/text when the button is clicked a second time.
from Tkinter import *
window = Tk()
window.title("Start/Stop Button")
window.geometry('200x100')
def clicked_rf1():
btn_rf1.configure(text="Stop")
lbl_rf1.configure(text=" ON ", bg="green")
btn_rf1 = Button(window, text="Start", command=clicked_rf1)
btn_rf1.grid(column=1, row=1)
lbl_rf1 = Label(window, text=" OFF ", bg="red")
lbl_rf1.grid(column=2, row=1)
window.mainloop()
I want something that behaves a little more like a toggle, but I would like the look of a button.
Help gratefully received.
You will need an if block to choose what to do. You can make another flag variable to keep track of the state, or just use the current Label or Button text:
from Tkinter import *
window = Tk()
window.title("Start/Stop Button")
window.geometry('200x100')
def clicked_rf1():
if btn_rf1['text'] == "Start":
btn_rf1.configure(text="Stop")
lbl_rf1.configure(text=" ON ", bg="green")
else:
btn_rf1.configure(text="Start")
lbl_rf1.configure(text=" OFF ", bg="red")
btn_rf1 = Button(window, text="Start", command=clicked_rf1)
btn_rf1.grid(column=1, row=1)
lbl_rf1 = Label(window, text=" OFF ", bg="red")
lbl_rf1.grid(column=2, row=1)
window.mainloop()
This would be an ideal place to make a custom Button subclass, so you could have many of these in your program:
from Tkinter import *
window = Tk()
window.title("Start/Stop Button")
window.geometry('200x100')
class Christina(Frame):
def __init__(self, master=None, **kwargs):
Frame.__init__(self, master, **kwargs)
self.btn = Button(self, text="Start", command=self.clicked)
self.btn.grid(column=0, row=0)
self.lbl = Label(self, text=" OFF ", bg="red")
self.lbl.grid(column=1, row=0)
def clicked(self):
if self.btn['text'] == "Start":
self.btn.configure(text="Stop")
self.lbl.configure(text=" ON ", bg="green")
else:
self.btn.configure(text="Start")
self.lbl.configure(text=" OFF ", bg="red")
btn1 = Christina(window)
btn1.grid()
btn2 = Christina(window)
btn2.grid()
btn3 = Christina(window)
btn3.grid()
window.mainloop()
If you want a toggle, you can use the checkbutton without an indicator. It has options for the color in the selected and deselected state, and you can tie the value and the label together so that the label changes when you toggle the button.
Like any button, you can tie a command to it. The command can check the value of the variable to determine whether it should do the "on" function or the "off" function.
Here's a simple example:
import Tkinter as tk
def toggle():
if var.get() == "ON":
print("turning on...")
else:
print("turning off...")
root = tk.Tk()
var = tk.StringVar()
toggle = tk.Checkbutton(root, onvalue="ON", offvalue="OFF", width=4,
indicatoron=False,
variable=var, textvariable=var,
selectcolor="green", background="red",
command=toggle)
var.set("OFF")
toggle.pack()
root.mainloop()
Another approach might be to put the "pile of code" to run into different function, collect those in an iterator, and then get the next function from that iterator and execute it:
def bunchofcode():
print("foo")
def somethingelse():
print("bar")
whattodo = iter([bunchofcode, somethingelse])
def clicked_rf1():
try:
next(whattodo)()
except StopIteration:
print("nothing to do")
Or for cyclic behaviour:
from itertools import cycle
whattodo = cycle([bunchofcode, somethingelse])
For a two-state toggle button, you could also use a dict to map the current state to the next. You could also use the button's relief to mark the state.
def clicked_rf1():
transition = {"raised": "sunken", "sunken": "raised"}
btn_rf1["relief"] = transition[btn_rf1["relief"]]
I need a Python script that uses the Tkinter module to create a static (not resizable) window.
I have a pretty simple Tkinter script but I don't want it to be resizable. How do I prevent a Tkinter window from being resizable? I honestly don't know what to do.
This is my script:
from tkinter import *
import ctypes, os
def callback():
active.set(False)
quitButton.destroy()
JustGo = Button(root, text=" Keep Going!", command= lambda: KeepGoing())
JustGo.pack()
JustGo.place(x=150, y=110)
#root.destroy() # Uncomment this to close the window
def sleep():
if not active.get(): return
root.after(1000, sleep)
timeLeft.set(timeLeft.get()-1)
timeOutLabel['text'] = "Time Left: " + str(timeLeft.get()) #Update the label
if timeLeft.get() == 0: #sleep if timeLeft = 0
os.system("Powercfg -H OFF")
os.system("rundll32.exe powrprof.dll,SetSuspendState 0,1,0")
def KeepGoing():
active.set(True)
sleep()
quitButton1 = Button(root, text="do not sleep!", command=callback)
quitButton1.pack()
quitButton1.place(x=150, y=110)
root = Tk()
root.geometry("400x268")
root.title("Alert")
root.configure(background='light blue')
timeLeft = IntVar()
timeLeft.set(10) # Time in seconds until shutdown
active = BooleanVar()
active.set(True) # Something to show us that countdown is still going.
label = Label(root, text="ALERT this device will go to sleep soon!", fg="red")
label.config(font=("Courier", 12))
label.configure(background='light blue')
label.pack()
timeOutLabel = Label(root, text = 'Time left: ' + str(timeLeft.get()), background='light blue') # Label to show how much time we have left.
timeOutLabel.pack()
quitButton = Button(root, text="do not sleep!", command=callback)
quitButton.pack()
quitButton.place(x=150, y=110)
root.after(0, sleep)
root.mainloop()
The resizable method on the root window takes two boolean parameters to describe whether the window is resizable in the X and Y direction. To make it completely fixed in size, set both parameters to False:
root.resizable(False, False)
I would like for a cant afford label to appear then after a second disappear when i push a button to buy something. It seems like the time.sleep(1) is making it not work properly. This is done on python tkinter.
def buttonpressed():
Label.place(x = 500, y = 500 #where i want the label to appear
time.sleep(1)
Label.place(x = 10000, y = 10000) #moving it away where i wont be able to see it
You can't use sleep because it stops mainloop
You can use root.after(time_in_milliseconds, function_name) to run function
Example
import tkinter as tk
def button_pressed():
# put text
label['text'] = "Hello World!"
# run clear_label after 2000ms (2s)
root.after(2000, clear_label)
def clear_label():
# remove text
label['text'] = ""
root = tk.Tk()
label = tk.Label(root) # empty label for text
label.pack()
button = tk.Button(root, text="Press Button", command=button_pressed)
button.pack()
root.mainloop()
If you have to create and remove label then use label.destroy()
import tkinter as tk
def button_pressed():
label = tk.Label(root, text="Hello World!")
label.pack()
root.after(2000, destroy_widget, label) # label as argument for destroy_widget
def destroy_widget(widget):
widget.destroy()
root = tk.Tk()
button = tk.Button(root, text="Press Button", command=button_pressed)
button.pack()
root.mainloop()
And shorter version without destroy_widget
import tkinter as tk
def button_pressed():
label = tk.Label(root, text="Hello World!")
label.pack()
root.after(2000, label.destroy)
root = tk.Tk()
button = tk.Button(root, text="Press Button", command=button_pressed)
button.pack()
root.mainloop()
Press button many times to see many labels which disappear after 2s.
You can use after() to set up a callback after a specified interval. In the callback function clear the label with pack_forget() (or grid_forget() if you're using grid). This is better than setting the label's text attribute to an empty string because that causes widgets to be resized, which might not be what you want. Here's an example:
import Tkinter as tk
class App():
def __init__(self):
self.root = tk.Tk()
self.label = tk.Label(text='I am a label')
self.label.place(x=0, y=0)
self.label.after(1000, self.clear_label) # 1000ms
self.root.mainloop()
def clear_label(self):
print "clear_label"
self.label.place_forget()
app=App()
Another option is to use self.label.destroy() to destroy the widget, however, pack_forget() allows you to display the label again by calling pack() on the widget again.
# I made it through calling 2 functions:
from tkinter import *
root = Tk()
root.geometry('400x200') # width by height
def one_text():
label['text'] = "Look around"
root.after(1000, another_text)
def another_text():
label['text'] = "and smile"
root.after(1000, one_text)
label= Label(root ,font=('Helvetica', 14), fg='black', bg='white')
label.pack()
one_text()
root.mainloop()