tkinter radiobutton

W

William Gill

I am placing radiobuttons in a 4 X 4 matrix (using loops) and keep
references to them in a 2 dimensional list ( rBtns[r][c] ). It works
fine, and I can even make it so only one button per column can be
selected, by assigning each column to an intVar. In many languages a
radiobutton has a property that can be directly read to see if it is
selected on unselected. Tkinter radiobuttons don't seem to have any
such property. Is there any way to look (via the script not the screen)
to determine if it is selected?, or can this only be achieved via
control variables?

Bill
 
E

Eric Brunel

I am placing radiobuttons in a 4 X 4 matrix (using loops) and keep
references to them in a 2 dimensional list ( rBtns[r][c] ). It works
fine, and I can even make it so only one button per column can be
selected, by assigning each column to an intVar. In many languages a
radiobutton has a property that can be directly read to see if it is
selected on unselected. Tkinter radiobuttons don't seem to have any
such property. Is there any way to look (via the script not the screen)
to determine if it is selected?, or can this only be achieved via
control variables?

The value and variable options for a radiobutton seem to be what you're looking for. Here is an example showing how to use them:

----------------------------------------------------------------
from Tkinter import *

root = Tk()
v = StringVar()
Radiobutton(root, text='foo', value='foo', variable=v).pack()
Radiobutton(root, text='bar', value='bar', variable=v).pack()

def p():
print v.get()

Button(root, command=p, text='Print').pack()

root.mainloop()
 
W

William Gill

to determine if it is selected?, or can this only be achieved via
> The value and variable options for a radiobutton seem to be what
> you're looking for.

Thanks, I knew that, but I was looking for a way to read the
selected/unselected status directly. Using control variables gets a
little messy because of the relationship of the rb matrix. They are
arranged in a 4 X 4 matrix where each column is grouped (via intVars) so
that no more than 1 rb per column can be selected, but each row makes up
the 'status' on one 'item' so any combination of buttons in a row is
acceptable.

One solution I have been contemplating requires setting the value of
each rb to the row number ( 0, 1, 2, or 3 or 1,2,3, or 4 in case I need
to use 0 for 'none selected'), and using one intVar for each column.
Then I would have to loop through all four intVars four times to
determine which radiobuttons are selected in each row. That's what I
mean by messy.

Bill

Eric said:
I am placing radiobuttons in a 4 X 4 matrix (using loops) and keep
references to them in a 2 dimensional list ( rBtns[r][c] ). It works
fine, and I can even make it so only one button per column can be
selected, by assigning each column to an intVar. In many languages a
radiobutton has a property that can be directly read to see if it is
selected on unselected. Tkinter radiobuttons don't seem to have any
such property. Is there any way to look (via the script not the screen)
to determine if it is selected?, or can this only be achieved via
control variables?


The value and variable options for a radiobutton seem to be what you're
looking for. Here is an example showing how to use them:

----------------------------------------------------------------
from Tkinter import *

root = Tk()
v = StringVar()
Radiobutton(root, text='foo', value='foo', variable=v).pack()
Radiobutton(root, text='bar', value='bar', variable=v).pack()

def p():
print v.get()

Button(root, command=p, text='Print').pack()

root.mainloop()
 
P

Peter Otten

William said:
I am placing radiobuttons in a 4 X 4 matrix (using loops) and keep
references to them in a 2 dimensional list ( rBtns[r][c] ). It works
fine, and I can even make it so only one button per column can be
selected, by assigning each column to an intVar. In many languages a
radiobutton has a property that can be directly read to see if it is
selected on unselected. Tkinter radiobuttons don't seem to have any
such property. Is there any way to look (via the script not the screen)
to determine if it is selected?, or can this only be achieved via
control variables?

You can either write a little helper function

def selected(rbn):
return rbn.getvar(rbn["variable"]) == rbn["value"]

or use a custom subclass of Tkinter.Radiobutton with a 'selected' attribute:

class Radiobutton(Tkinter.Radiobutton):
def __getattr__(self, name):
if name == "selected":
return self.getvar(self["variable"]) == self["value"]
raise AttributeError


Peter
 
W

William Gill

or use a custom subclass ...

I had considered extending radiobutton to add whatever properties
needed, but the net/net is the same, that property must be set using
methods that trigger on the rb command procedure, or an external (to the
rb) control variable value.

The radiobutton widget knows if it is selected or unselected, or it
wouldn't be able to display correctly, but based on what I'm seeing,
that information is inaccessable to the app. Instead the app must
evaluate an associated control variable. That doesn't make sence to me,
but even trying to look at the code for the radiobutton class didn't help.

I guess I need to set up an observer on the control variable, or a
command procedure on the radiobutton (effectively to create my own
control variable).

I know I can 'slice' my original 4 X 4 matrix vertically, by associating
a different intVar to each 'column', but I can't figure out how to
'slice' them horizontally w/o breaking their vertical relationships.


Bill

Peter said:
William Gill wrote:

I am placing radiobuttons in a 4 X 4 matrix (using loops) and keep
references to them in a 2 dimensional list ( rBtns[r][c] ). It works
fine, and I can even make it so only one button per column can be
selected, by assigning each column to an intVar. In many languages a
radiobutton has a property that can be directly read to see if it is
selected on unselected. Tkinter radiobuttons don't seem to have any
such property. Is there any way to look (via the script not the screen)
to determine if it is selected?, or can this only be achieved via
control variables?


You can either write a little helper function

def selected(rbn):
return rbn.getvar(rbn["variable"]) == rbn["value"]

or use a custom subclass of Tkinter.Radiobutton with a 'selected' attribute:

class Radiobutton(Tkinter.Radiobutton):
def __getattr__(self, name):
if name == "selected":
return self.getvar(self["variable"]) == self["value"]
raise AttributeError


Peter
 
P

Peter Otten

William said:
The radiobutton widget knows if it is selected or unselected, or it
wouldn't be able to display correctly, but based on what I'm seeing,
that information is inaccessable to the app.  Instead the app must
evaluate an associated control variable.  That doesn't make sence to me,
but even trying to look at the code for the radiobutton class didn't help.

I guessed you wanted to solve a practical problem, but the thoughts
expressed above suggest, err, philosophical qualms. So, for the sake of the
argument and since we both don't know the implementation details, be it in
C or TCL, let's assume that the individual radiobuttons do *not* /know/
whether they are selected or not but instead compare their associated
'variable' with their 'value' every time they are /asked/ to draw
themselves. That would avoid duplicate state and require only log N instead
of N bits. Wouldn't that be an elegant implementation, at least in theory?

So why bother about the layers below when you have all the information to
write code that works?

Peter
 
W

William Gill

I thought the problem was practical, not philosophical, but what do I
know I'm the one asking for help.

I have radiobuttons arranged in 4 rows of 4 buttons each ( 4 rows and 4
columns )

The user can select no more than 1 choice in any column, but any number
in any row.

Restated:
columns can have 0 or 1 selection
rows can have 0,1,2,3, or 4 selections.


Columns can be restricted to 0 or 1 selection through the use of an
intVar. So we now have 4 intVars (control variables)

The app needs to examine the buttons and aggregate the selections for
each row, efectively converting columnar information to row information.

one solution:

Create 4 intVars
Column0 = intVar()
Column1 = intVar()
Column2 = intVar()
Column3 = intVar()

Assign 0, 1, 2, 3 and 4 to values to correspond to the row number.
Row1rb1 = Radiobutton(self, variable = Column0, value = 1)
Row1rb2 = Radiobutton(self, variable = Column1, value = 1)
Row1rb3 = Radiobutton(self, variable = Column2, value = 1)
Row1rb4 = Radiobutton(self, variable = Column3, value = 1)
Row2rb1 = Radiobutton(self, variable = Column0, value = 2)
Row2rb2 = Radiobutton(self, variable = Column1, value = 2)


Row4rb4 = Radiobutton(self, variable = Column3, value = 4)

to 'read' the user's response:

Loop through the 4 intVars 4 times; compare their value to the value for
the row being processed; if they are the same bitor a value to a
rowVariable i.e. convert the column information (intVar values) to row
information.


Bill
 
P

Peter Otten

William said:
I thought the problem was practical, not philosophical, but what do I
know I'm the one asking for help.

What follows looks more like a spec than a question.
columns can have 0 or 1 selection
rows can have 0,1,2,3, or 4 selections.
Loop through the 4 intVars 4 times; compare their value to the value for
the row being processed; if they are the same bitor a value to a
rowVariable i.e. convert the column information (intVar values) to row
information.

Here's my implementation:

import Tkinter as tk

class Radiogrid(tk.Frame):
def __init__(self, master, columns, trace_write=None):
tk.Frame.__init__(self)
self.variables = []
self.buttons = []
for x, column in enumerate(columns):
var = tk.IntVar()
if trace_write:
var.trace_variable("w", trace_write)
self.variables.append(var)
self.buttons.append([])
for y, text in enumerate(column):
rbn = tk.Radiobutton(self, text=text, variable=var, value=y)
rbn.grid(column=x, row=y)
self.buttons[-1].append(rbn)
def get_row_state(self, row):
return tuple(row == var.get() for var in self.variables)

if __name__ == "__main__":
root = tk.Tk()
def show_state(*args):
for i in range(3):
print "row", i, rg.get_row_state(i)
print
rg = Radiogrid(root,
["alpha beta gamma".split(),
"one two three".split(),
"guido van rossum".split()],
show_state
)
rg.pack()
root.mainloop()

I hope this will move further discussion from the abstract to the
concrete :)

Peter
 
W

William Gill

What follows looks more like a spec than a question.
It is, but I wanted to show why I was getting confused trying to use
control variables to maintain the overall relationship.

Thank you. This is exactly what I'm trying to do, and oddly enough
similar in concept to what I was doing, but far more readable and
maintainable (thus the philosophical component :))
using 'for x, column in enumerate(columns):' for a looping structure
cleared up a lot of the convoluted 'score keeping' I was trying to do in
my nested loops.

Also, does 'row == var.get() for var in self.variables' perform the
comparison row == var.get() for each item in self.variables? I would
have had to write:

for var in self.variables:
return row == var.get()

Again, thanks.

Bill

Peter said:
William Gill wrote:

I thought the problem was practical, not philosophical, but what do I
know I'm the one asking for help.


What follows looks more like a spec than a question.

columns can have 0 or 1 selection
rows can have 0,1,2,3, or 4 selections.

Loop through the 4 intVars 4 times; compare their value to the value for
the row being processed; if they are the same bitor a value to a
rowVariable i.e. convert the column information (intVar values) to row
information.


Here's my implementation:

import Tkinter as tk

class Radiogrid(tk.Frame):
def __init__(self, master, columns, trace_write=None):
tk.Frame.__init__(self)
self.variables = []
self.buttons = []
for x, column in enumerate(columns):
var = tk.IntVar()
if trace_write:
var.trace_variable("w", trace_write)
self.variables.append(var)
self.buttons.append([])
for y, text in enumerate(column):
rbn = tk.Radiobutton(self, text=text, variable=var, value=y)
rbn.grid(column=x, row=y)
self.buttons[-1].append(rbn)
def get_row_state(self, row):
return tuple(row == var.get() for var in self.variables)

if __name__ == "__main__":
root = tk.Tk()
def show_state(*args):
for i in range(3):
print "row", i, rg.get_row_state(i)
print
rg = Radiogrid(root,
["alpha beta gamma".split(),
"one two three".split(),
"guido van rossum".split()],
show_state
)
rg.pack()
root.mainloop()

I hope this will move further discussion from the abstract to the
concrete :)

Peter
 
W

William Gill

p.s. I tweaked

rbn = tk.Radiobutton(self, text=text, variable=var, value=y)
to
rbn = tk.Radiobutton(self, text=text, variable=var, value=y+1)

and

return tuple(row == var.get() for var in self.variables)
to
return tuple(row+1 == var.get() for var in self.variables)

so that the Radiogrid doesn't initialize w/row 1 selected, and
accomodates cases where nothing is selected in any column.

Bill

Peter said:
William Gill wrote:

I thought the problem was practical, not philosophical, but what do I
know I'm the one asking for help.


What follows looks more like a spec than a question.

columns can have 0 or 1 selection
rows can have 0,1,2,3, or 4 selections.

Loop through the 4 intVars 4 times; compare their value to the value for
the row being processed; if they are the same bitor a value to a
rowVariable i.e. convert the column information (intVar values) to row
information.


Here's my implementation:

import Tkinter as tk

class Radiogrid(tk.Frame):
def __init__(self, master, columns, trace_write=None):
tk.Frame.__init__(self)
self.variables = []
self.buttons = []
for x, column in enumerate(columns):
var = tk.IntVar()
if trace_write:
var.trace_variable("w", trace_write)
self.variables.append(var)
self.buttons.append([])
for y, text in enumerate(column):
rbn = tk.Radiobutton(self, text=text, variable=var, value=y)
rbn.grid(column=x, row=y)
self.buttons[-1].append(rbn)
def get_row_state(self, row):
return tuple(row == var.get() for var in self.variables)

if __name__ == "__main__":
root = tk.Tk()
def show_state(*args):
for i in range(3):
print "row", i, rg.get_row_state(i)
print
rg = Radiogrid(root,
["alpha beta gamma".split(),
"one two three".split(),
"guido van rossum".split()],
show_state
)
rg.pack()
root.mainloop()

I hope this will move further discussion from the abstract to the
concrete :)

Peter
 
P

Peter Otten

William said:
Also, does 'row == var.get() for var in self.variables' perform the
comparison row == var.get() for each item in self.variables?  I would
have had to write:

for var in self.variables:
return row == var.get()

Or rather

result = []
for var in self.variables:
result.append(row == var.get())
return tuple(result)

This can be rewritten to a 'list comprehension'

return tuple([row == var.get() for var in self.variables])

and, since Python 2.4, to the 'generator expression' that I used and which
avoids building the intermediate list. Both constructs also feature an
if-clause, see

http://docs.python.org/tut/node7.html#SECTION007140000000000000000
http://docs.python.org/tut/node11.html#SECTION00111100000000000000000
p.s.  I tweaked

rbn = tk.Radiobutton(self, text=text, variable=var, value=y)
to
rbn = tk.Radiobutton(self, text=text, variable=var, value=y+1)

and

return tuple(row == var.get() for var in self.variables)
to
return tuple(row+1 == var.get() for var in self.variables)

so that the Radiogrid doesn't initialize w/row 1 selected, and
accomodates cases where nothing is selected in any column.

Another option would have been to initialize the variables

....
var = tk.IntVar()
var.set(-1)
if trace_write:
....

Peter
 
W

William Gill

I did some more digging based on your code, and discovered list
comprehensions. They didn't register the first time I skimmed the
language reference and tutorial. It's obvious the more I learn, the
more I need to relearn what I think I know. I need to study
comprehensions, but they open up lots of opportunities for simpler,
clearer code.
Oops! I saw this as soon as I sent it, but knew you would get my point.
> var.set(-1)
Though both ways may seem roughly equivalent today, I'd bet that in 6
months

....
var.set(-1) # initialize as no choice
....

will be clearer.

Thanks again!

Bill

Peter said:
William Gill wrote:

Also, does 'row == var.get() for var in self.variables' perform the
comparison row == var.get() for each item in self.variables? I would
have had to write:

for var in self.variables:
return row == var.get()


Or rather

result = []
for var in self.variables:
result.append(row == var.get())
return tuple(result)

This can be rewritten to a 'list comprehension'

return tuple([row == var.get() for var in self.variables])

and, since Python 2.4, to the 'generator expression' that I used and which
avoids building the intermediate list. Both constructs also feature an
if-clause, see

http://docs.python.org/tut/node7.html#SECTION007140000000000000000
http://docs.python.org/tut/node11.html#SECTION00111100000000000000000

p.s. I tweaked

rbn = tk.Radiobutton(self, text=text, variable=var, value=y)
to
rbn = tk.Radiobutton(self, text=text, variable=var, value=y+1)

and

return tuple(row == var.get() for var in self.variables)
to
return tuple(row+1 == var.get() for var in self.variables)

so that the Radiogrid doesn't initialize w/row 1 selected, and
accomodates cases where nothing is selected in any column.


Another option would have been to initialize the variables

...
var = tk.IntVar()
var.set(-1)
if trace_write:
...

Peter
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,768
Messages
2,569,574
Members
45,051
Latest member
CarleyMcCr

Latest Threads

Top