How to change Tkinter label text on button press - python

I have this code, and its meant to change the text of the Instruction label when the item button is pressed. It doesn't for some reason, and I'm not entirely sure why. I've tried creating another button in the press() function with the same names and parameters except a different text.
import tkinter
import Theme
import Info
Tk = tkinter.Tk()
message = 'Not pressed.'
#Sets window Options
Tk.wm_title(Info.Title)
Tk.resizable(width='FALSE', height='FALSE')
Tk.wm_geometry("%dx%d%+d%+d" % (720, 480, 0, 0))
#Method run by item button
def press():
message = 'Button Pressed'
Tk.update()
#item button
item = tkinter.Button(Tk, command=press).pack()
#label
Instruction = tkinter.Label(Tk, text=message, bg=Theme.GUI_hl2, font='size, 20').pack()
#Background
Tk.configure(background=Theme.GUI_bg)
Tk.mainloop()

Doing:
message = 'Button Pressed'
will not affect the label widget. All it will do is reassign the global variable message to a new value.
To change the label text, you can use its .config() method (also named .configure()):
def press():
Instruction.config(text='Button Pressed')
In addition, you will need to call the pack method on a separate line when creating the label:
Instruction = tkinter.Label(Tk, text=message, font='size, 20')
Instruction.pack()
Otherwise, Instruction will be assigned to None because that is the method's return value.

You can make message a StringVar to make callback.
message = tkinter.StringVar()
message.set('Not pressed.')
You need to set message to be a textvariable for Instruction:
Instruction = tkinter.Label(Tk, textvariable=message, font='size, 20').pack()
and then
def press():
message.set('Button Pressed')

Related

How do i differentiate between two widgets on the same event

I want to have some input boxes which contain an text for the user to know what is required to enter. This text should disappear when the user clicks on it. How do i know which box the user clicked?
class window():
def handleEvent(self,event):
self.text.set("")
def handleEvent2(self,event):
a = self.efeld.get()
print(a)
def page0(self):
self.text = tk.StringVar(None)
self.text.set("Enter text here")
self.efeld = ttk.Entry(fenster, textvariable=self.text)
self.efeld.place(x=5, y=20)
self.efeld.bind("<Button-1>",self.handleEvent)
self.efeld.bind("<Return>",self.handleEvent2)
self.text2 = tk.StringVar(None)
self.text2.set("Enter text 2 here")
self.efeld2 = ttk.Entry(fenster, textvariable=self.text2)
self.efeld2.place(x=5, y=50)
self.efeld2.bind("<Button-1>",self.handleEvent)
self.efeld2.bind("<Return>",self.handleEvent2)
fenster = tk.Tk()
fenster.title("Test")
fenster.geometry("500x350")
fenster.resizable(False,False)
window().page0()
fenster.mainloop()
You can use the widget attribute of the event object. It is a reference to the widget that got the event.
def handleEvent2(self,event):
a = event.widget.get()
print(a)
You can use the event.widget attribute to get a reference to the widget that triggered the event.
Since you are using tkinker,
event.widget will contain what you want.
Sorry for short reply, navigating from mobile.

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)

Make Tkinter run a command without clicking or pressing a button

I am making a simple tkinter popup where you can type a message.
In the textbox itself, I inserted the text "Type your message here" with a grey colour and when clicked, the inserted text is deleted so the user can type in their own message. In addition, the colour of the text typed by the user is set to black.
However, when I was testing I realised that this will only happen if they click the textbox with a mouse button. My question is, is there a way for tkinter to automatically run a command when a condition is changed? For example, if the textbox is empty, the font colour should be set to black.
I tried putting if-statements in the tk.mainloop, but sadly that didn't work.
Any ideas?
this is my (hopefully) simplified version of the code:
from tkinter import *
def changecolor(event):
if textbox.get("1.0", "end-1c") == "Type your message here":
textbox.delete("1.0", "end")
textbox.config(fg='black')
root = Tk()
canvas = Canvas(root, height=400, width=600)
canvas.pack()
textbox = Text(canvas, font=40, fg="grey")
textbox.insert(1.0, "Type your message here")
textbox.bind("<Button-1>", changecolor)
textbox.pack()
root.mainloop()
~finally found out how to format code here.
Take a look at this class I created that does similar to what your code does, do try it on.
from tkinter import *
class PlaceholderText(Text):
def __init__(self,master,placeholder,placeholdercolor='black',fg='grey',**kwargs):
Text.__init__(self,master,**kwargs) #init the text widget
self.placeholder = placeholder
self.fgcolor = fg
self.placeholdercolor = placeholdercolor
self.has_placeholder = False #make flag
self.add() #run the function to add placeholder
self.bind('<FocusIn>',self.clear) #binding to focusin and not button-1
self.bind('<FocusOut>',self.add) #function wil get triggered when widget loses focus
def clear(self,event=None):
if self.get('1.0','end-1c') == self.placeholder and self.has_placeholder: #condition to clear a placeholder
self.delete('1.0','end-1c') #delete the placeholder
self.config(fg=self.fgcolor) #change the color
self.has_placeholder = False #set flag to flase
def add(self,event=None):
if self.get('1.0','end-1c') == '' and not self.has_placeholder: #condition to add placeholder
self.insert('1.0',self.placeholder) #add placeholder
self.has_placeholder = True #set flag to true
self.config(fg=self.placeholdercolor) #change text color to what you specify?
def ret(self,index1,index2):
if self.get('1.0','end-1c') == self.placeholder and self.has_placeholder: #gives none if there is nothing in the widget
return 'None'
else:
return self.get(index1,index2) #else gives the text
root = Tk()
pl = PlaceholderText(root,placeholder='Type something here...')
pl.pack()
e = Entry(root) #dummy widget to switch focus and check
e.pack(padx=10,pady=10)
root.mainloop()
I've explained to through the comments. But keep in mind its not the best of classes yet, you have to do add a lot more methods in to make it more efficient.
Just if your wondering on how to do this without classes, then:
from tkinter import *
has_placeholder = False #make flag
placeholder = 'Type Something Here...' #the text to be inserted
def clear(event=None):
global has_placeholder
if a.get('1.0','end-1c') == placeholder and has_placeholder: #condition to clear a placeholder
a.delete('1.0','end-1c') #delete the placeholder
a.config(fg='grey') #change the color
has_placeholder = False #set flag to flase
def add(event=None):
global has_placeholder
if a.get('1.0','end-1c') == '' and not has_placeholder: #condition to add placeholder
a.insert('1.0',placeholder) #add placeholder
has_placeholder = True #set flag to true
a.config(fg='black') #change text color to normal
root = Tk()
a = Text(root)
a.pack()
add() #add the placeholder initially
a.bind('<FocusIn>',clear) #binding to focus and not button-1
a.bind('<FocusOut>',add)
e = Entry(root) #dummy widget to show focus loss
e.pack()
root.mainloop()
Why not to use classes if the latter method is more easier? This is not reusable, say you want to add one more Text widget, that cannot have such property, while using a custom class with custom class you can have as many as text widgets with same properties you like.
Do let me know if any doubts.
You can simply add a <Key> binding to your text widget and use your changecolor function to determine what state your textbox is in.
#Give a hoot. Don't pollute. :D
import tkinter as tk
txtmsg = "Type your message here"
def changecolor(event):
text = textbox.get("1.0", "end-1c")
#customize accordingly
if text:
if text == txtmsg:
print("text is txtmsg")
else:
print("text is user text")
else:
print("text is empty")
#FYI:
#whether this was a button press or key press DOES NOT have string equality
#if you need to create button vs key conditions
#use tk.EventType.ButtonPress and tk.EventType.KeyPress
#or learn the .value and compare that
print(event.type, type(event.type), event.type.value)
root = tk.Tk()
textbox = tk.Text(root, font=40, fg="grey")
textbox.insert(1.0, txtmsg)
textbox.pack()
#add events
for ev in ['<Key>', '<1>']:
textbox.bind(ev, changecolor)
root.mainloop()

Tkinter: How to update a label text after a certain amount of time has passed?

So, I'm trying to create a basic Tkinter program which, when I press a button, updates the text on a label field, waits X amount of seconds and then update the label again.
For example:
I click the button, the label clears immediately after pressing it, then the program waits 3 seconds and shows "Hello" on screen.
The code shown below does not do what I want it to do because when I press the button, it remains pressed for X amount of time and then the text is updated inmediately. I want to press the button, clear the label, wait for 3 seconds and then show "Hello" on screen.
from tkinter import *
class Origin:
def __init__(self):
self.root = Tk()
self.root.geometry('800x600')
self.root.config(bg="black")
self.v = StringVar()
self.v.set('O R I G I N')
self.main_label = Label(self.root, textvariable=self.v, font="Arial 40", fg="white", bg="black")
self.main_label.place(x=240, y=150)
self.clear = Button(self.root, text='Clear', command=self.clear)
self.clear.place(x=400, y=400)
self.root.mainloop()
def clear(self):
#just to clear the string
self.v.set('')
self.root.after(3000, self.v.set('Hello'))
def main():
App = Origin()
if __name__ == '__main__':
main()
after needs callback - it means function's name without () and arguments. If you have to use function with argument then use `lambda
after(3000, lambda:self.v.set('Hello'))
or create function which doesn't need arguments
def callback():
self.v.set('Hello')
self.root.after(3000, callback)
Your current code works like
result = self.v.set('Hello')
self.root.after(3000, result)
It executes function self.v.set('Hello') at once and uses its result as callback in after().
EDIT: as #acw1668 said in comment you can also run function with arguments this way
self.root.after(3000, self.v.set, 'Hello')

Simple gui that display a repeated set of images when a button is pushed

I made a very simple gui that has a button and shows an image(.gif). My goal is to output another .gif whenever you press the button. There are 2 .gif files in my file directory and the point is to keep switching between these two whenever you press the button.
#Using python2.7.2
import Tkinter
root = Tkinter.Tk()
try:
n
except:
n = 0
def showphoto(par):
if par%2 == 0:
try:
label2.destroy()
except:
pass
photo = Tkinter.PhotoImage(file="masc.gif")
label2 = Tkinter.Label(image=photo)
label2.image = photo
label2.pack()
else:
try:
label2.destroy()
except:
pass
photo = Tkinter.PhotoImage(file="123.gif")
label2 = Tkinter.Label(image=photo)
label2.image = photo
label2.pack()
myContainer1 = Tkinter.Frame(root, width = 100, height = 100)
myContainer1.pack()
def callback(event):
global n
showphoto(n)
n = n + 1
button1 = Tkinter.Button(myContainer1)
button1["text"]= "Next pic"
button1["background"] = "green"
button1.bind("<Button-1>", callback(n))
button1.pack()
root.mainloop()
The current code just outputs the first image (masc.gif) but when I press the button it doesn't switch to the other image(123.gif). What am I doing wrong?
This can achieved much easier with classes as the class holds all the data necessary without the use of global variables.
import Tkinter as tk
from collections import OrderedDict
class app(tk.Frame):
def __init__(self,master=None, **kwargs):
self.gifdict=OrderedDict()
for gif in ('masc.gif','123.gif'):
self.gifdict[gif]=tk.PhotoImage(file=gif)
tk.Frame.__init__(self,master,**kwargs)
self.label=tk.Label(self)
self.label.pack()
self.button=tk.Button(self,text="switch",command=self.switch)
self.button.pack()
self.switch()
def switch(self):
#Get first image in dict and add it to the end
img,photo=self.gifdict.popitem(last=False)
self.gifdict[img]=photo
#display the image we popped off the start of the dict.
self.label.config(image=photo)
if __name__ == "__main__":
A=tk.Tk()
B=app(master=A,width=100,height=100)
B.pack()
A.mainloop()
Of course, this could be done more generally ... (the list of images to cycle through could be passed in for example), and this will switch through all the images in self.gifs ...
This approach also removes the necessity to destroy and recreate a label each time, instead we just reuse the label we already have.
EDIT
Now I use an OrderedDict to store the files. (keys=filename,values=PhotoImages). Then we pop the first element out of the dictionary to plot. Of course, if you're using python2.6 or earlier, you can just keep a list in addition to the dictionary and use the list to get the keys.
button1 = Tkinter.Button(myContainer1)
button1["text"]= "Next pic"
button1["background"] = "green"
button1.bind("<Button-1>", callback(n))
First, you bind the <Button-1> event to None (that's what callback(n) evaluates to). You should bind it to callback (no parentheses a.k.a the call operator).
Second, I suggest you change callback to not accept any arguments, remove the bind call and create your button as:
button1 = Tkinter.Button(myContainer1, command=callback)

Categories

Resources