Disabling a radio button does nothing - python

I create my radio buttons and store them in a list as a class property:
for possible_answer in self.possible_answers:
possible_answer =
R = ttk.Radiobutton(self,
text=possible_answer,
variable=var,
value=possible_answer,
command=lambda: self.set_chosen_answer(var.get()))
self.radio_buttons.append(R)
and when the users selects any of the options, I want the radio buttons to become inactive or disabled. I tried doing this in the following manner:
for radio_button in self.radio_buttons:
radio_button.state = DISABLED
The code runs without problems, the only problem I have is that I am still able to click on other buttons after this - which I do not want.
Those two lines do get called as I have verified with printing each of radio button's state right after setting it and it prints "disabled" for each of them.
What am I doing wrong? I have read through documentation and similar posts but did not find anything useful. Did I misunderstand what the disabled state means? Should I do this differently?

state is not an attribute of the widget object. It is a configuration option, which must be set by the config or configure method:
for radio_button in self.radio_buttons:
radio_button.configure(state = DISABLED)

Related

Python tkinter: Delete Menu checkbutton

I want to delete a menu checkbutton when i right click on it.
Its usually done with bind("Mouse3", deletefunction) , BUT i need an actual instance of a checkbutton to bind it with, and the only way to add a checkbutton to a menu i know is a add_checkbutton() method (and i have no access to instance this way). Is there any way i could do this?
import tkinter as tk
root = tk.Tk()
menubar = tk.Menu(root)
view_menu = tk.Menu(menubar, tearoff=0)
view_menu.add_checkbutton(label="Right click on me to delete", onvalue=1, offvalue=False)
# I want to do something like this:
# c = Checkbutton(label="Right click on me to delete")
# c.bind("Mouse3", my_delete_function())
# view_menu.add(c)
menubar.add_cascade(label='View', menu=view_menu)
root.config(menu=menubar)
root.mainloop()
To the best of my understanding, there's essentially two parts to your question:
Can the menu bar item be assigned for manipulation after?
Can the referenced item be then bound to an event?
The first answer is, sort of. While you can't exactly assign the object, you can reference it by index like this:
view_menu.delete(0)
Since you added the checkbutton first, it'll have an index of 0. You can either keep track of the indices, or refer to the item by its label. See this related answer from Bryan Oakley. e.g.:
view_menu.delete(view_menu.index("Right click on me to delete"))
The .index() method will locate the index by the menu entry's label, which can be handy unless you have the same label more than once...
The second answer, as far as I'm aware, there doesn't seem to be any effective binding for typical events like mouse clicks. However after some search I did come across a rather hidden <<MenuSelect>> binding that at least triggers an event. That by itself is not useful to your quest, but you can combine the event state with the checkbutton's command argument as well as a boolean flag to trigger an event on click:
# Add a BooleanVar for flagging:
delete_checkbutton = tk.BooleanVar()
# Add a binding to your view_menu
view_menu.bind('<<MenuSelect>>', event_state)
# Define the callback function:
def event_state(e):
if bool(e.state & 0x0400): # watch for the Mouse Right click state
# you might opt to use 0x0004 or 0x0001 instead
# i.e. Ctrl+click or Shift+Click
delete_checkbutton.set(True)
else: # If the state is not right click, reset the flag
delete_checkbutton.set(False)
# Define a self_delete command for the checkbutton
def self_delete():
if delete_checkbutton.get():
view_menu.delete(view_menu.index("Right click on me to delete"))
# Add the command to your checkbutton
view_menu.add_checkbutton(label="Right click on me to delete", onvalue=lambda:print('hey'), offvalue=False, command=self_delete)
Note: You will actually have to hold right click and then left click on the checkbutton to delete it. Obviously the drawback is you have now triggered the on/off value, and you might need to have some additional handling on those.
If right + left click is too awkward, Ctrl/Shift is another mod state you might consider.
Another side note: I'm a proponent of OOP when it comes to tkinter, it makes accessible variables and flags much easier without needing to worry the global and nonlocal namespaces. Here since delete_checkbutton is set in the global namespace I avoied using the global keyword and accessed it via the tk.BooleanVar() object. However if you were to use a python boolean (e.g. flag = True) then it won't be as effective unless you indicate global flag in both functions. If however you took an OOP approach you can reference the flags directly via self.flag without ambiguity.
Finally, here are the comprehensive changes implemented into your code for sampling:
import tkinter as tk
def event_state(e):
if bool(e.state & 0x0400):
# you might opt to use 0x0004 or 0x0001 instead
# i.e. Ctrl+click or Shift+Click
delete_checkbutton.set(True)
else:
delete_checkbutton.set(False)
def self_delete():
if delete_checkbutton.get():
view_menu.delete(view_menu.index("Right click on me to delete"))
root = tk.Tk()
menubar = tk.Menu(root)
delete_checkbutton = tk.BooleanVar()
view_menu = tk.Menu(menubar, tearoff=0)
view_menu.add_command(label='dude', command=lambda: print('dude'))
view_menu.add_checkbutton(label="Right click on me to delete", onvalue=lambda:print('hey'), offvalue=False, command=self_delete)
menubar.add_cascade(label='View', menu=view_menu)
root.config(menu=menubar)
view_menu.bind('<<MenuSelect>>', event_state)
root.mainloop()
All that said, I am of the opinion that this is not a very smooth User Experience and is somewhat confusing. Just the permanent deletion of the menu item alone is questionable at best, combined with the method you are trying to call upon the deletion feels even more contrived. I'd suggest revisiting your UX flow to consider how to streamline this.

Implementing checkable buttons in Pyqt5 file menu to act similarly to radio buttons

I have created a file menu in PyQt5 with a "File" option with two checkable buttons (button1, button2). Unfortunately, I have not found a way to implement radio buttons into the file menu so I assume at the moment it is not possible. Instead, I would like to make these two checkable buttons act like radio buttons - this means that if one is checked the other becomes unchecked. Only one can be checked at a given time.
I have attempted to do it this way (which I find the most logical and straightforward), but it does not work:
def fileMenu(self):
if self.button1.isChecked() == True:
self.button2.setChecked(False)
If I check button2 and then check button1, button2 does not uncheck. Is there any other way to do this or any error in my code preventing it from working? Or... ideally is there any way to implement radio buttons into the file menu?
You can accomplish this using a QActionGroup. You would have to group all those buttons together under an action group. It will not work otherwise.
The documentation can be found here.
Here is an example of implementing a QActionGroup with two radio buttons:
w is your QtMainWindow, ag is defining the QActionGroup, and menu is the name of your menu.
ag = QtGui.QActionGroup(w, exclusive=True)
a = ag.addAction(action_a)
menu.addAction(a)
b = ag.addAction(action_b)
menu.addAction(b)

Enabling a checkbox Tkinter (Python 3.4)

I have a CheckBox in Tkinter. I want it to always stay checked but disabling the checkbox destroys the looks of the GUI application. I want to keep its state as Normal and if a user tries to uncheck it the box remains checked, or rechecks itself immediately after.
global ghistory
ghistory = IntVar()
cc = Checkbutton(frame3, text="History", variable=ghistory)
cc.select()
cc.pack()
How do I do it?
Add a function that sets the variable to True. A quick lambda function would do the trick:
cc=tk.Checkbutton(frame3,text="History",variable=ghistory, command=lambda:ghistory.set(1))
Or you could use the select command:
cc=tk.Checkbutton(frame3,text="History",variable=ghistory)
cc['command'] = cc.select
use .cget('state') to see if the button is disabled...
self.widget_checkbutton = tk.Checkbutton(self, variable=self.some_variable, command=lambda:self.stay_checked())
def stay_checked(self):
if self.widget_checkbutton.cget('state') == 'disabled':
self.widget_checkbutton.select()
else:
self.widget_checkbutton.deselect()
#this will only let the button be checked if widget is active.
(so basically, if some widget is disabled... you tell the checkbox to stay the same. else if some widget is normal, you allow the checkbox to work normally... select... deselect...)

How do I make a Text widget readonly

I am trying to make the TEXT widget read-only, so the user can view it yet cannot edit it. I saw the state of "readonly" being used in another SO question, however it throws this error at me
_tkinter.TclError: bad state "readonly": must be disabled or normal
My code is below
e = Text(root ,height=10, width=50).config(state="readonly")
e.place(x=1,y=1)
There's no such a possible value "readonly" for the state of a Text widget. You can disable it, setting the state to "disabled" (and you can do it directly in the constructor):
e = Text(root, height=10, width=50, state='disabled') # no need to call config
From the Tk documentation:
If the text is disabled then characters may not be inserted or deleted
and no insertion cursor will be displayed, even if the input focus is
in the widget.
I think you should use a Label, if you want just to show some text, that's why labels exist.

PyQt - Radiobuttons not checkable

I want to display the result of an SQL query with radio buttons. For this purpose, I use an array of radiobuttons. This works fine, it displays what I want. The problem I have is that the buttons are not "checkable". If I click on them, nothing happens. I'm sure it is a stupid mistake, but I can't see what's wrong.
Here is my code :
groups = select.getGroups(self.parent.db) #This returns a list of strings
self.groupList = {}
self.groupBtn = []
i = 0
radioLayout = QtGui.QVBoxLayout()
radioGroup = QtGui.QGroupBox()
for g in groups:
self.groupList[g[0]] = i
name = g[0]
self.groupBtn.append(QtGui.QRadioButton(name))
radioLayout.addWidget(self.groupBtn[i])
i = i+1
radioGroup.setLayout(radioLayout)
self.groupBtn[0].setChecked(True)
self.groupLayout.addWidget(radioGroup)
Is there something obvious I'm missing ?
"Radio buttons typically present the user with a "one of many" choice. In a group of radio buttons only one radio button at a time can be checked; if the user selects another button, the previously selected button is switched off."
If that is not your desired behaviour, you may consider using check buttons (QCheckButton). If it is:
You need to be sure that you have not incidentally set setAutoExclusive(false) somewhere in code, for any of those radio buttons (by default it is set on true).
Last thing I can suggest (based on the code that you have shown): Try to put radio buttons in QButtonGroup() instead of QGroupBox().

Categories