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.
Related
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)
I am new in Python and in tkinter so the question may seems naive: is it ok to create and place widgets at the same time if I don't need to change them?
It works but is it a good practice? And if not why?
An example of what I mean:
import tkinter as tk
window=tk.Tk()
tk.Label(window,text='Lost Label').pack()
window.mainloop()
To expand upon #Skynet's answer....
Whenever you do Widget(*args, **kwargs).pack() the pack() method returns None as would other geometry managers, so if you tried to assign this to a variable the variable would be None.
In this case then probably not, since you probably actually want to be storing the reference to the widget.
If you don't need a reference then there's not really a problem with it. As the other answer notes you don't need a definitve reference to every single widget in your GUI unless you plan to use this reference in some way. Unless I plan on changing the label text / modifying it in someway then I typically use your method to save some space. No need to write more code than you have to!
For example you're creating a Button widget.
btn = Button(blabla, text="Button1")
This returns a button object and if you need later to configure it or get information about it you can do it by through the btn variable.
But if you use something like btn = Button(blabla, text="Button1").pack() it returns None and not a button object so you won't be able to change anything about the button or get information about it later.
Another example is with the Entry widget
entry = Entry(blabla)
Using that later you can do entry.get() to get the text inside the entry
but you won't be able to do it if you use entry = Entry(blabla).pack() since it doesn't return an entry object, it just packs the widget and you won't be able to access it for later use.
There is nothing wrong with that approach and I have already seen it quite a few times. You don't have to keep a reference to every widget in your GUI.
I want to dynamically create radiobuttons for every year from 2013. I've already created these buttons but I am not able to check the first radiobutton(2013). To do that I am using QtGui.QRadioButton('2013').setChecked(True)
I have no idea what I am missing...
def dynamicaly_cratede_radiobtns(self):
current_year = datetime.today().year
self.button_group = QtGui.QButtonGroup()
for i in range(2013, current_year+1):
self.button_name = QtGui.QRadioButton("{}".format(i))
self.button_name.setObjectName("radiobtn_{}".format(i))
self.verticalLayout.addWidget(self.button_name)
self.button_group.addButton(self.button_name,i)
self.button_name.toggled.connect(self.subplot_year)
QtGui.QRadioButton('2013').setChecked(True)
def subplot_year(self):
print self.button_group.checkedId()
EDIT
I just solved it by using if
for i in range(2013, current_year+1):
self.button_name = QtGui.QRadioButton("{}".format(i))
self.button_name.setObjectName("radiobtn_{}".format(i))
self.verticalLayout.addWidget(self.button_name)
self.button_group.addButton(self.button_name,i)
self.button_name.toggled.connect(self.subplot_year)
if i == 2013:
self.button_name.setChecked(True)
But anyway, why does the original approach doesn't work? I think it is more elegant solution.
QtGui.QRadioButton('2013').setChecked(True)
creates a new radio button with text value of '2013'. This one gets checked.
This radio button is not part of any layout or radio group and thus non of the buttons is checked.
It seems like you think the radio buttons get cached somewhere and can be accessed with the text value. But this is not the case.
To solve this issue you need to check one of the buttons you added to the layout like you did in your edit.
I'm reading Programming Python by Mark Lutz, and I came across this bit of code that I don't get.
buttons = []
def onpress(i):
global state
state = i
for btn in buttons:
btn.deselect()
buttons[i].select()
I get what it's doing, but I don't get where these select and deselect methods are coming from. I've never seen these list methods before (I and the book are using Python 3). Are these builtin methods? And what do they do? I tried using my Google-fu to figure it out, but to no avail.
http://www.java2s.com/Tutorial/Python/0360__Tkinker/Deselectforradiobuttonssimplysetsthebuttonsassociatedvaluetoanullstring.htm
So a quick google, if you are doing this it means you are most likely using tkinter in which case you have missing relevant code somewhere.
Those methods .deselect() and .select() are actually tkinter Radiobuttons() methods. If you're tutorial reverences tkinter. "from tkinter import *" will import those class's. If not i have no idea why its being taken out of context.
But what this code does is, it takes 'i', which is a button you click, then it sets 'state' to 'i', making 'state' 'global' over writing the last 'state', then it deselects all buttons, and selects 'i' which is your new choice. In effect this means you'll only ever select 1 button
If you wonder why state is an empty string, its because an empty string is the first button in tkinter. When you pass a new string in, it gives the new item being something like "I001" or something.
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))