Pass text to menu label in tkinter - python

I want to make the text under the selection, in variable 'a' to appear as a label of the menu.
Here is the code:
def popup(event):
a=t_start.get("sel.first", "sel.last")
menu.post(event.x_root, event.y_root)
return a
def insert_word():
pass
t_start.bind("<Button-3>", popup)
menu = Menu(root, tearoff=0)
menu.add_command(label="Set selection 1", command=set_selection_1)
menu.add_command(label="Set selection 2", command=set_selection_2)
menu.add_command(label="%s" %popup, command=set_selection_2)
Right now, all I get is function popup address.
If I try popup.a, I get an error, function has no attribute 'a'. How do I overcome this and get whatever is in 'a' to be printed as menu label?

Callback methods like popup are not supposed to return anything. Instead, you should manipulate the menu from inside the function.
Also, as suggested by Brian, you probably rather want to modify an existing entry in the menu, instead of adding a new one each time you click the button. In this case, create the entry outside the function (like you do now), but use some placeholder for the label.
def popup(event):
a = t_start.get("sel.first", "sel.last")
menu.post(event.x_root, event.y_root)
menu.add_command(label=a, command=set_selection_2) # add to menu
# menu.entryconfig(2, label=a) # modify existing entry

If you want to change the text on a menu, you must use the entryconfig method. For example, to change the text of the first item you would do:
menu.entryconfig(0, label=a)

Related

How to destroy a specific widget at once ? Tkinter

When i pressed the "Create New" button a submit button will be appear at the bottom. But whenever i choose the drop down value the submit button will be destroyed and a new button called "Update" will be shown. But the problem is that, when I repeatedly choose the drop down value and go back to "Create New" button, the "update" button can only be destroy once. Is it a way to find all the update button and destroy all at once ? Thanks
When i pressed create new button :
When i choose a few times of drop down list value and go back to "create new", the submit and update button is stacked together :
Code:
def submit():
deleteAllEntry()
global CreateJsonBtn, profilenameLbl, ProfileNameEntry
CreateJsonBtn = Button(jsonframe, text="Submit", command=submit, width=11)
CreateJsonBtn.place(x=370, y=540)
CreateNewJsonBtn.configure(state=DISABLED)
UpdateJsonBtn.destroy()
#Get value inside the dropdown value
def dropdown(choice):
if (jsonprofileName == ""):
tkMessageBox.showinfo("ERROR", "Fail to retrieve value. Try again later", icon="error")
else:
cursor = con.cursor()
cursor.execute("select * from json where jsonprofilename='" +options.get() + "'")
rows = cursor.fetchall()
deleteAllEntry()
global row
for row in rows:
insertAllEntry()
cursor.close()
try:
#Delete submit button when select existing value
CreateJsonBtn.destroy()
CreateNewJsonBtn.configure(state=NORMAL)
except:
pass
finally:
global UpdateJsonBtn
UpdateJsonBtn = Button(jsonframe, text="Update", command=submit, width=11)
UpdateJsonBtn.place(x=370, y=550)
Instead of destroying and creating buttons multiple times, you can configure them, check the example below
from tkinter import *
def foo():
print('called foo')
button.configure(text='bar',command=bar)
def bar():
print('called bar')
button.configure(text='foo',command=foo)
root=Tk()
button=Button(root,text='foo',command=foo)
button.pack()
root.mainloop()
config/configure is a universal widget method, you can use this refernece to learn more about them.
The reason you are currently facing the issue is that every time the dropdown is used, a new button "Update" is placed on top of the previous one (if one already exists). The UpdateJsonBtn holds the last assigned instance and hence deletes the topmost one, if there are buttons underlying, they will not be destroyed. I would not suggest the approach currently used by you.
In the question you stated if there is a way to get all the buttons that say "Update", you can do that using the following (again, I am not suggesting to use this in your case, but just to answer your question)
for widget in jsonframe.winfo_children():
if isinstance(widget,Button):
if widget['text']=='Update':
widget.destroy()
So I know that it should work with CreateJsonBtn.place_forget()
You can try that

Tkinter optionmenu won't allow me to pass the frame of the object I want to update depending on the choice made

I have a list of frames that each have an optionmenu with the same list of choices. When a choice is made in that specific optionmenu, I've only been able to get the last entry widget to change, not the corresponding one. In other widgets I've been able to use something like "lambda F=F:function(args)" but that isn't working here.
I've tried a trace on the variable in the option menu, I've tried wrapper functions, I've tried every combination of a lambda in the command section of the optionmenu widget. Most approaches create errors, some, like the one attached, modify the bottom frame/entry but not the correct corresponding one.
This doesn't seem like it should be too hard. If the option for the top frame selected is "Continuous" or "Discrete", the entry next to it should be 'normal' state with "?..?" in the box, if it is categorical, it should change to be 'disabled' with no contents. I could do this easily if I could somehow pass the Frame dictionary key to the "updateOnChange" function, but I can't, it only allows a single argument to be passed and that is the string value of mType[F].
from tkinter import *
def updateOnChange(type):
print(type)
if type.upper()=='CATEGORICAL':
rangeEntry[F].delete(0,END)
rangeEntry[F].config(state='disabled')
print("runCat")
else:
rangeEntry[F].config(state='normal')
rangeEntry[F].delete(0,END)
rangeEntry[F].insert(0,'?..?')
print("runCont")
mType={}
frame={}
om={}
rangeEntry={}
root=Tk()
Frames=['FrameOne','FrameTwo']
miningTypes=['Continuous','Categorical','Discrete']
for F in Frames:
mType[F]=StringVar(root)
if F=='FrameOne':
mType[F].set("Continuous")
else:
mType[F].set("Categorical")
frame[F]=Frame(root,borderwidth=3,relief=SUNKEN)
frame[F].pack(side=TOP,fill=X)
rangeEntry[F]=Entry(frame[F],width=20,font=("Arial",12))
om[F]=OptionMenu(frame[F],mType[F],*miningTypes,command=updateOnChange)
om[F].pack(side=LEFT)
rangeEntry[F].pack(side=LEFT)
mainloop()
``
Your updateOnChange function hard-coded the entry to be changed as rangeEntry[F], which points to the last Entry widget created in your for loop. To properly associate each entry, you should pass the widget as a parameter:
def updateOnChange(type, entry):
if type.upper()=='CATEGORICAL':
entry.delete(0,END)
entry.config(state='disabled')
print("runCat")
else:
entry.config(state='normal')
entry.delete(0,END)
entry.insert(0,'?..?')
print("runCont")
And then pass the parameter in your command:
om[F]= OptionMenu(frame[F],mType[F],*miningTypes,command=lambda e, i=rangeEntry[F]: updateOnChange(e, i))

highlight clicked items in tkinter canvas?

General idea:
Many items (majority small images) are created on the canvas. The user can click on any item and move it.
I need the user to know which item was last clicked, by showing (drawing) a border/change brightness/any method.. around that item.
Is there any Image/item options to help apply this idea.
You can achieve that by writing a simple modify appearance method for a widget last clicked. Here is the sample code. Below we are performing two actions. First changing the appearance of last widget to normal and then changing the appearance of last clicked widget to highlight it.
def modifyAppearance(self, widget):
global previously_clicked
if 'previously_clicked' in globals():
# rolling back the appearance of previous widget to normal
previously_clicked['bg'] = widget['bg']
previously_clicked['activebackground'] = widget['activebackground']
previously_clicked['relief'] = widget['relief']
# changing the appearance of the last clicked widget
widget['bg'] = 'green'
widget['activebackground'] = '#33B5E5'
widget['relief'] = 'sunken'
previously_clicked = widget
You will need to define global previously_clicked in other methods also, where you will be defining the widgets. You can refer my full code here. It has this functionality
For example this is your button-
B1 = Button(root, text = "Click me", command = clickme)
we can pass more parameters here such as--
highlightcolor=
The color to use for the highlight border when the button has focus. The default is system speciific. (highlightColor/HighlightColor)
and
highlightthickness=
The width of the highlight border. The default is system specific (usually one or two pixels). (highlightThickness/HighlightThickness)
...
OR
...
Whenever the button is clicked you must be specifying some action to do in a function. What you can do is you can tell that function to slight increase the thickness of border by above parameters. :)

How to create a combobox that includes checkbox for each item?

Fairly new to tkinter and python I was wondering how to achieve a button that would act like this :
Click on button drops down a list (so that's a combobox)
Each line of the list has a checkbox.
Finally if a checkbox is clicked run a function, or (even better) once combobox is no more dropped run a function with items checked as args.
UPDATE
The button/menuButton will have to act like a filter. When menu is dropped down user can uncheck multiple options (without the menu to disappear each time an item is clicked) he don't want. Therefore it's really important to be able to see checkboxes so as the user know which options are currently active.
I finally used the idea of Bryan by creating a top level frame. Here is what I have :
There is no widget to do what you want. You'll have to create a toplevel window with a bunch of checkbuttons. You can then trigger the appearance with a normal button.
I don't think the OptionMenu is intended to hold anything but strings. It sounds like you want the functionality of a Listbox, which has options to allow for multiple selections, get all selected items, and so on.
This gives you an OptionMenu with checkboxes in the contained Menu. Check whichever items you like, then right-click in the tkinter window to print the values of the checkboxes to the console.
from tkinter import *
master = Tk()
var = StringVar(master)
var.set("Check")
w = OptionMenu(master, variable = var, value="options:")
w.pack()
first = BooleanVar()
second = BooleanVar()
third = BooleanVar()
w['menu'].add_checkbutton(label="First", onvalue=True,
offvalue=False, variable=first)
w['menu'].add_checkbutton(label="Second", onvalue=True,
offvalue=False, variable=second)
w['menu'].add_checkbutton(label="Third", onvalue=1,
offvalue=False, variable=third)
master.bind('<Button-3>', lambda x: print("First:", first.get(), " Second:",
second.get(), " - Third:", third.get()))
mainloop()
See also this.

Python - TKinter - Editing Widgets

I need a widget in TKinter to be a global widget, however, I need the text displayed in it to be different every time. I'm quite new with TKinter and haven't yet successfully managed to edit an option in a widget.
I assume it's something to do with widget.add_option() but the documentation is quite confusing to me and I can't figure out the command.
I specifically just need to edit the text = "" section.
Thanks
EDIT:
gm1_b_current_choice_label = Label(frame_gm1_b, text = "Current input is:\t %s"% str(save_game[6]))
I specifically need to update the save_game[6] (which is a list) in the widget creation, but I assume once the widget is created that's it. I could create the widget every time before I place it but this causes issues with destroying it later.
You can use the .config method to change options on a Tkinter widget.
To demonstrate, consider this simple script:
from Tkinter import Tk, Button, Label
root = Tk()
label = Label(text="This is some text")
label.grid()
def click():
label.config(text="This is different text")
Button(text="Change text", command=click).grid()
root.mainloop()
When the button is clicked, the label's text is changed.
Note that you could also do this:
label["text"] = "This is different text"
or this:
label.configure(text="This is different text")
All three solutions ultimately do the same thing, so you can pick whichever you like.
You can always use the .configure(text = "new text") method, as iCodez suggested.
Alternatively, try using a StringVar as the text_variable parameter:
my_text_var = StringVar(frame_gm1_b)
my_text_var.set("Current input is:\t %s"% str(save_game[6]))
gm1_b_current_choice_label = Label(frame_gm1_b, textvariable = my_text_var)
Then, you can change the text by directly altering my_text_var:
my_text_var.set("Some new text")
This can be linked to a button or another event-based widget, or however else you want to change the text.

Categories

Resources