OptionMenu won't show the first option when clicked (Tkinter) - python

I added a OptionMenu widget to my code, and assigned a list as it's options. This is how it is:
z = StringVar()
z.set(userList[0])
usersOption = OptionMenu(frame1, z, *userList)#, command=changeUser)
usersOption.pack(side=RIGHT, padx=3)
Now, I reckon it would show all the options in said list. As so:
Option 1 \/ <-- the box with the selected option
Option 1 }\__the options that show on click
Option 2 }/
but it actually only shows the second option, and when I choose it there is, basically, no way back, if I click the box again it keeps only showing option 2 and I can't change it even with the up and down keys. I tried looking for solutions, but I got nowhere, so I'm starting to think it is the default operating way of the widget, but I found nothing to show me how to solve it in the documentation I read.
P.S.: I'm using Python 3.3

I had the same problem and it was driving me mad, so i looked in the source. I think the issue is that the 3rd constructor argument is the default value. If you don't specify it before *userList, it looks like it takes the first item as the default value. A real fix would be something like:
z = StringVar()
z.set(userList[0])
usersOption = OptionMenu(frame1, z, userList[0] ,*userList)#, command=changeUser)
usersOption.pack(side=RIGHT, padx=3)

Late answer..
Just use
self.option = OptionMenu(PARENT, VALUE TO BE CHANGED, "DEFAULT TEXT", *OPTIONS_ARRAY/LIST)
Works perfectly for me.

Never mind, I took the *userList off and used a for loop to insert the items as commands. Now it works just fine.
The code I used:
for user in userList:
usersOption["menu"].insert("end", "command", label=user, command=_setit(z, user, changeUser))

Related

How to know which check box has just been toggled within a mass created checkbox from tkinter or related libraries

let's say I've this piece of code where it give me a hundred check boxes created by a for loop(I'm working with a switch in customtkinter but it has the same logic as checkbox in tkinter), those check box could either be off or on initially however, I just need to know what is the latest check box that has been toggled, its position or at least what it called so that I could work on it. I've done some research but I couldn't figured out how to work on this, here is the part of that code for the check box, I've not assigned the variable to it since I'm trying to figure out the logic first but any help would be much appreciated, thank you!
with open(r"D:\Largecodefile\TkinterApply\List1.txt",'r') as f:
Lines=f.read().splitlines()
for i,val in enumerate(Lines):
switch = ct.CTkSwitch(master=self.scrollable_framel, text=val,command=self.changestate)
switch.grid(row=i, column=0, padx=10, pady=(0, 20))
self.scrollable_frame_switches.append(switch)
The command=self.changestate doesn't seem to return anything as of right now and it's just mainly used to run the function and that's it so I don't think it'd be of great help either :(
Edit: I'm thinking of cross comparing between 2 list of off or on state after a certain event occured for those 100 boxes but that doesn't seem to be a good idea, and perhaps it's the same thing for creating 100 function for this purpose as well since it'd take a lot of time to process as the list get longer
You're on the right track, but you have to set the value of i to a variable the lambda can pass: command=lambda button_index=i: changestate(button_index) - that way the value is updated for each button!
Here's an example of how to pass the index value i to the event handler function changestate (I've removed self here since this isn't in a class that I can see, but if you're using a class, just add self.)
def changestate(button_index):
print(button_index) # do whatever you want here!
for i, val in enumerate(Lines):
switch = ct.CTkSwitch(
master=self.scrollable_framel,
text=val,
# pass the latest index value to the lambda for each button
command=lambda button_index=i: changestate(button_index)
)
switch.grid(row=i, column=0, padx=10, pady=(0, 20))
self.scrollable_frame_switches.append(switch)

Radiobutton IN tkinter

I m creating a GUI for a simple quiz and I want my answer to be checked in radiobutton . But the problem is that I can not mark or unmark it , its always set up marked . my code is
R1=Radiobutton(win,text= 'option1',command =sel1)
My function sel1 is as follow :
def sel1():
global ans
ans = 1
As far as I see from the docs the radiobutton is used to select one out of many, whcih as I understand will always require one button to be marked. It's possible you need CheckButton.
EDIT: As it turned out the problem was to set common variable for all radio buttons as mentioned in the docs linked above.

PyQt trigger a button with ctrl+Enter

I'm trying to make a trigger for the Ok button in my application
The current code I tried was this:
self.okPushButton.setShortcut("ctrl+Enter")
However, it doesn't work, which kind of makes sense. I tried looking up some key sequences here, but, again, a similar issue if I try with the shift or alt keys.
How can I trigger the OkButton with ctrl+Enter
According to the docs:
Qt.Key_Enter 0x01000005 Typically located on the keypad.
That is to say when you set Enter we refer to the key that is on the numeric keypad.
But if you want to use the default enter you must use Return.
self.okPushButton.setShortcut("Ctrl+Return")
# seq = QKeySequence(Qt.CTRL+Qt.Key_Return)
# self.okPushButton.setShortcut(seq)

How to add options in OptionMenu without using the add_command function in Tkinter?

Is there any way to add options in an OptionMenu object without using the command flag?
I have seen many posts, such as this one, showing how the add_command will update/remove/add options to an already existing OptionMenu object. For example, this snippet of code removes all options in serialPortOptionMenu and repopulates the option menu with different options:
serialPortOptionMenu["menu"].delete(0, "end")
for serialPort in serialPortsArray:
serialPortOptionMenu["menu"].add_command(label=serialPort[1], command=lambda v=serialPort: serialPortFunc(v))
However, something like this seems to overwrite the original command flag I wrote when creating the OptionMenu object:
serialPortOptionMenuValue = Tkinter.StringVar(optionMenuFrame)
serialPortOptionMenuValue.set(serialPorts[0])
serialPortOptionMenu = Tkinter.OptionMenu(optionMenuFrame, serialPortOptionMenuValue, *serialPorts, command=lambda *args: callbackFuncWhenOptionMenuSelectsAnotherOption(*args))
serialPortOptionMenu.grid(row=3, column=0, columnspan=2, sticky="we")
As I'm sure many people are wondering why I am setting a command within an OptionMenu (weird I know), it is because I want a callback function to be called when the user picks a new option.
"What about the trace option"-Everyone...Yes, I am aware of this as well, but because I am reading/writing new values to the Tkinter variable in code without having the OptionMenu solely change it, using trace within Tkinter would not be an effective substitute for just tracking when the user selects a new option in the OptionMenu.
Why am I changing the Tkinter value in code and not having the OptionMenu do it? Because I thought it would be nice to modify the Tkinter value string with something like a ~ at the end of the string to signify something is happening behind the scenes that isnt completed yet, and only when it is completed will the ~ go away, hence the reading and writing to the value without having the OptionMenu solely change it.
What I am primarily interested in is if anyone knows of other ways to add options to an OptionMenu object without using
myMenu["menu"].add_command(..., command=...)
As it seems to be removing my original callback function.
Note: Having two OptionMenus and performing grid_remove/grid on them to hide/reveal them also crossed my mind, but just seems to messy.
There is no other way. An option menu is nothing more than a menubutton and a menu with a custom binding. You can create your own OptionMenu and add any methods you want.
I don't know if it will be useful to you anymore, but maybe somebody else will be looking for this as I was. You actually just need to put the command as a callback parameter in the command you use to add the item (I found tk._setit):
def refresh_dropdown():
# Reset var and delete all old options
var.set('')
dropdown['menu'].delete(0, 'end')
# Insert list of new options (tk._setit hooks them up to var)
new_choices = ['a', 'b', 'c', '...'] # you can make a dictionary[key_from_previous_dropdown.get()] as I did in my case
for choice in new_choices:
dropdown['menu'].add_command(label=choice, command=_setit(var, choice, new_command))
dropdown = OptionMenu(app, var, command=new_command)

Tkinter: a simple way to avoid race conditions between an event and value update?

The issue
My issue is pretty simple, but I couldn't figure out how to cope with it easily even after some googling.
So I have a checkbutton:
self.but_val = IntVar()
self.but = Checkbutton(frame, text="text", variable=self.but_val)
This checkbutton triggers updates of some file path on the GUI:
self.but.bind('<ButtonRelease-1>',
lambda e: self.update_files_path(e), add='+')
In update_files_path(event), I need to get the value of the checkbutton to select the file paths to be displayed:
if self.but_val.get() == 0:
[...]
else:
[...]
The issue I have is that I get the value of the button before the clic.
And since the processing of file paths depends on different button values, I can't just use the opposite value.
My current work around
At the moment I have a function that is triggered before the clic and that save the state of the GUI:
self.but.bind('<ButtonPress-1>', lambda e,
self.save_design_opts_state(self.buttons_to_backup,
self.before_clic_vars_state), add='+')
Then in update_files_path(event) I call a function that infers the GUI state after the clic:
gui_state = self.get_gui_state(event)
This function is very annoying to implement, because I need to do a lot of things:
1- Check that the clic is really made on a button (to avoid a clic that starts on a button and end elsewhere!)
2- Get the value of the of all required buttons depending of their type
Is there an easier way to deal with this?
Thank you for your help!
Don't set your own bindings. Use the command option of the checkbutton. This option lets you specify a command to be run after the value has changed. There are other ways, but this is by far the simplest, most common way to solve your problem.

Categories