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.
Related
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)
I have created a custom widget for tkinter that lays out 5 buttons. The widget works beautifully for the most part. The problem is that I cannot figure out how to pass the button that the user presses in the widget to the main application. The custom widget stores the last button pressed in a variable, but I cannot figure out how to make the main application see that it has been changed without resorting to binding a button release event to root. I would like to try to build out this custom widget further, and I want it to work without having to do some messy hacks. Ideally, in the example below, when a button is pressed, the label should change to reflect the button pressed. For example, if the user clicks the "2" button, the label should change to "2 X 2 = 4". How can I pass the text on the button directly to the main application for use? Hopefully, I am making it clear enough. I want to be able to get the value from the widget just like any other tkinter widget using a .get() method. Here is the code that I am using:
import tkinter as tk
from tkinter import ttk
class ButtonBar(tk.Frame):
def __init__(self, parent, width=5, btnLabels=''):
tk.Frame.__init__(self, parent)
self.btnLabels = []
self.btnNames = []
self.setLabels(btnLabels)
self.selButton = None
self.display()
def getPressedBtn(self,t):
"""
This method will return the text on the button.
"""
self.selButton = t
print(t)
def createBtnNames(self):
"""
This method will create the button names for each button. The button
name will be returned when getPressedBtn() is called.
"""
for i in range(0,5):
self.btnNames.append(self.btnLabels[i])
def display(self):
"""
This method is called after all options have been set. It will display
the ButtonBar instance.
"""
self.clear()
for i in range(len(self.btnLabels)):
self.btn = ttk.Button(self, text=self.btnLabels[i], command=lambda t=self.btnNames[i]: self.getPressedBtn(t))
self.btn.grid(row=0, column=i)
def setLabels(self, labelList):
if labelList == '':
self.btnLabels = ['1', '2', '3', '4', '5']
self.createBtnNames()
else:
btnLabelStr = list(map(str, labelList))
labelsLen = len(btnLabelStr)
def clear(self):
"""
This method clears the ButtonBar of its data.
"""
for item in self.winfo_children():
item.destroy()
root = tk.Tk()
def getButtonClicked(event):
global selBtn
print(event)
if example.winfo_exists():
selBtn = example.selButton
answer = int(selBtn) * 2
myLabel.config(text='2 X ' + selBtn + ' = ' + str(answer))
tabLayout = ttk.Notebook(root)
tabLayout.pack(fill='both')
vmTab = tk.Frame(tabLayout)
myLabel = tk.Label(vmTab, text='2 X 0 = 0', width=50, height=10)
myLabel.pack()
vmTab.pack(fill='both')
tabLayout.add(vmTab, text='Volume Movers')
# Create the ButtonBar.
example = ButtonBar(vmTab)
selBtn = None
example.pack()
lbl = tk.Label(root, text='')
root.mainloop()
I have looked at some other posts on stackoverflow. This one creating a custom widget in tkinter was very helpful, but it didn't address the button issue. I though this Subclassing with Tkinter might help. I didn't understand the If I bind the event using root.bind("<ButtonRelease-1>", getButtonClicked), then the widget works fine. Is there any other way to do it though?
I'd say that you have made the code more complex than it should be, you really just need to create the buttons and give them some callback that is passed as an argument. And that callback should take at least one argument which would be the text that would be on the button which will be also passed to that callback.
import tkinter as tk
from tkinter import ttk
class ButtonBar(tk.Frame):
def __init__(self, master, values: list, command=None):
tk.Frame.__init__(self, master)
for col, text in enumerate(values):
btn = ttk.Button(self, text=text)
if command is not None:
btn.config(command=lambda t=text: command(t))
btn.grid(row=0, column=col, sticky='news')
def change_label(val):
res = 2 * int(val)
new_text = f'2 X {val} = {res}'
my_label.config(text=new_text)
root = tk.Tk()
my_label = tk.Label(root, text='2 X 0 = 0')
my_label.pack(pady=100)
texts = ['1', '2', '3', '4', '5']
example = ButtonBar(root, values=texts, command=change_label)
example.pack()
root.mainloop()
You can also base the buttons on a list of values so that you can specify any values and it will create buttons that have that text on them and pressing them will call the given function with an argument of their text. That way you can use it as really any other widget, it would require the master, some values (text) and a command. Then you would just create that callback, which will take that one argument and then change the label accordingly. (I also removed all the notebook stuff, but I am just showing how you can achieve what you asked for)
Also:
I strongly suggest following PEP 8 - Style Guide for Python Code. Function and variable names should be in snake_case, class names in CapitalCase. Have two blank lines around function and class declarations. Object method definitions have one blank line around them.
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()
I am trying to keep the label text value: "This is the subtotal" next to subtotal value. Meaning:
If I were to click on the "Calulate Subtotal" Button the text "This is the subtotal" should be to the right and the actual subtotal should be to the left. Currently, If I were to click on the "Calulate Subtotal" Button the text "this is the subtotal" disappears. Can someone steer me in the right direction?
try:
import Tkinter as tk
except:
import tkinter as tk
class GetInterfaceValues():
def __init__(self):
self.root = tk.Tk()
self.root.geometry('500x200')
self.button = tk.Button(self.root,
text='Calculate Subtotal',
command=self.getSubtotals)
self.button.pack()
self.firstLabel = tk.Label(self.root, text="This is the subtotal:")
self.firstLabel.pack()
self.root.mainloop()
def getSubtotals(self):
self.firstLabel["text"] = 55*10
app = GetInterfaceValues()
You can simply change your getSubtotals method to retain the current text of firstLabel as the following:
def getSubtotals(self):
self.firstLabel["text"] = self.firstLabel["text"] + str(55 * 10)
Couple of suggestions:
You might want to create another widget to show subtotal value other than firstLabel.
You might want to restructure your class so that you only initialize the attributes in the init method
Please check the indentations and code formatting when asking questions to make it easier for others to inspect your code
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')