I'm trying to make a rainbow tkinter button - python

def RainbowButton():
btn.config(bg=red)
btn.config(bg=purple)
btn.config(bg=yellow)
btn.config(bg=orange)
btn.config(bg=blue)
btn.config(bg=lightblue)
btn.config(bg=green)
btn.config(bg=black)
def ButtonUpdate():
RainbowButton()
window.after(10, ButtonUpdate)
ButtonUpdate()
This is the code I came up with, but it's not working. The button is just black when I run the program, no colors are changing.

The trick is to feed the colors to the button one at a time, once for each time the ButtonUpdate() function runs. You can solve this by using an iterator. When the iterator runs out of items it raises an error which I capture with the try clause. I adjusted the time for the after() function so the effect can be seen better.
import tkinter as tk
window = tk.Tk()
btn = tk.Button(window, text='Button')
btn.pack(padx=50, pady=30)
rainbow_colors = ['red','purple','yellow','orange','blue',
'lightblue','green','black']
color_iterator = iter(rainbow_colors)
def ButtonUpdate():
try:
color = next(color_iterator)
btn.config(bg=color)
except StopIteration:
return
window.after(500, ButtonUpdate)
ButtonUpdate()
window.mainloop()

Related

Get dynamically which button uses function

I have a question about buttons and binds, but it's better if I show you.
from tkinter import Tk,Button
root = Tk()
startbutton = Button(root,text="start button")
pingbutton = Button(root,text="ping button")
startbutton.pack()
pingbutton.pack()
def startenterbind(e):
startbutton.config(relief='sunken')
def startleavebind(e):
startbutton.config(relief='raised')
def pingenterbind(e):
pingbutton.config(relief='sunken')
def pingleavebind(e):
pingbutton.config(relief='raised')
startbutton.bind("<Enter>", startenterbind)
startbutton.bind("<Leave>", startleavebind)
pingbutton.bind("<Enter>", pingenterbind)
pingbutton.bind("<Leave>", pingleavebind)
root.mainloop()
This is my code, now I am wondering, is there a better way to do this?
Maybe it's possible to get which button was hovered dynamically, to then change the button that was hovered?
This is so I can use one function for multiple buttons, while only affecting the one being <Enter>'d or <Leave>'d?
You can reuse an event handler function by making use of the event object they are passed which has an attribute telling you the widget that triggered it.
from tkinter import Tk,Button
root = Tk()
startbutton = Button(root,text="start button")
pingbutton = Button(root,text="ping button")
startbutton.pack()
pingbutton.pack()
def startenterbind(event):
event.widget.config(relief='sunken')
def startleavebind(event):
event.widget.config(relief='raised')
startbutton.bind("<Enter>", startenterbind)
startbutton.bind("<Leave>", startleavebind)
pingbutton.bind("<Enter>", startenterbind)
pingbutton.bind("<Leave>", startleavebind)
root.mainloop()
You could go a bit further by writing a single function that simply toggled the state of the button whenever it was called. One way that could be accomplished is by making the new relief type depend on what it currently is which can be determined by calling the universal widget cget() method:
def enterleavebind(event):
new_relief = 'sunken' if event.widget.cget('relief') == 'raised' else 'raised'
event.widget.config(relief=new_relief)
startbutton.bind("<Enter>", enterleavebind)
startbutton.bind("<Leave>", enterleavebind)
pingbutton.bind("<Enter>", enterleavebind)
pingbutton.bind("<Leave>", enterleavebind)

Tkinter crash in python

I was trying to make a stopwatch in python but every time it stops working beacause of overflow, can someone please fix this??
Code:
import time
from tkinter import *
cur=time.time()
root = Tk()
def functio():
while True:
s = time.time()-cur
l1 = Label(root,text=s)
l1.pack()
l1.destroy()
time.sleep(0.5)
Button(root,text='Start',command=functio).pack()
root.mainloop()
The while loop will block tkinter mainloop from handling pending events, use after() instead.
Also it is better to create the label once outside the function and update it inside the function:
import time
# avoid using wildcard import
import tkinter as tk
cur = time.time()
root = tk.Tk()
def functio():
# update label text
l1['text'] = round(time.time()-cur, 4)
# use after() instead of while loop and time.sleep()
l1.after(500, functio)
tk.Button(root, text='Start', command=functio).pack()
# create the label first
l1 = tk.Label(root)
l1.pack()
root.mainloop()
Note that wildcard import is not recommended.
Flow of execution can never exit your endless while-loop. It will endlessly block the UI, since flow of execution can never return to tkinter's main loop.
You'll want to change your "functio" function to:
def functio():
s = time.time()-cur
l1 = Label(root,text=s)
l1.pack()
l1.destroy()
root.after(500, functio)
That being said, I'm not sure this function makes much sense: You create a widget, and then immediately destroy it?
You'll want to do something like this instead:
import time
from tkinter import *
root = Tk()
def functio():
global timerStarted
global cur
# check if we started the timer already and
# start it if we didn't
if not timerStarted:
cur = time.time()
timerStarted = True
s = round(time.time()-cur, 1)
# config text of the label
l1.config(text=s)
# use after() instead of sleep() like Paul already noted
root.after(500, functio)
timerStarted = False
Button(root, text='Start', command=functio).pack()
l1 = Label(root, text='0')
l1.pack()
root.mainloop()

Showing text in tkinter´s canvas repeatedly

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.

Trying to print the output from my function inside my GUI window

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()

customize tkinter menu while menu is open

I want to have less popular menu items fade in gradually from white to black. Is there any way to have the colors update while the menu is still open? I've experimented with postcommand and threads:
def update():
def color(c):
animal_menu.config(fg=c)
root.update()
print c
def adapt():
color('white')
root.after(100, color('#6E6E6E'))
root.after(100, color('black'))
## adapt() ##attempt no.1
## thread.start_new_thread(adapt, ()) ##attempt no.2
root = Tk()
menubutton = Menubutton(root, text="Animals")
animal_menu = Menu(menubutton, tearoff=0, postcommand=update)
animal_menu.add_command(label="Lion", command=print_time)
animal_menu.add_command(label="Tiger", command=print_time)
animal_menu.add_command(label="Bear", command=print_time)
menubutton.menu = animal_menu
menubutton["menu"] = menubutton.menu
menubutton.pack()
root.config()
root.mainloop()
So far, the first attempt runs completely before the menu appears (which makes sense as postcommand is called before posting the menu), and the second attempt runs only when the menu is not open (which I don't understand) as evidenced by the print statements.
Could anybody give me a pointer on how to make the color properly dynamically change to have the items fade in while the menu is open?
There's a couple of problems with the after method in the callback:
def update():
def color(c):
animal_menu.config(fg=c)
root.update()
print c
def adapt():
color('white')
root.after(100, color('#6E6E6E'))
root.after(100, color('black'))
adapt() ##attempt no.1
First, if you're passing arguments to the function being called in the after, you have to use a lambda expression or just split them by a comma:
root.after(100, color, 'black')
Otherwise, the parenthesis will make that function be evaluated first.
Second, after doesn't work with the typical control flow you're probably used to--it's not evaluated one, then the next--you're setting both after calls to be evaluated after 100ms, so that's what's going to happen.
Here's a working example of a fadein callback:
from Tkinter import *
def fadein(color):
if color < 111111:
animal_menu.config(fg='#000000')
else:
print color
animal_menu.config(fg='#' + str(color))
root.after(100, fadein, color - 111111)
root = Tk()
menubutton = Menubutton(root, text="Animals")
animal_menu = Menu(menubutton, tearoff=0, postcommand=lambda: fadein(999999))
animal_menu.add_command(label="Lion")
animal_menu.add_command(label="Tiger")
animal_menu.add_command(label="Bear")
menubutton.menu = animal_menu
menubutton["menu"] = menubutton.menu
menubutton.pack()
root.config()
root.mainloop()
Note the lambda expression, in postcommand, which is needed to pass the argument to fadein().
More info: http://effbot.org/tkinterbook/widget.htm#Tkinter.Widget.after-method

Categories

Resources