0

I am trying to get a UI in the below format

enter image description here

import pandas as pd
from Tkinter import *

features =  pd.read_csv("C:\\Users\\ggorantla\\Desktop\\Competition\\otto\\data\\train.csv",dtype=object)
features = features.columns
master = Tk()
row = 0
result = []
mappe = {}
def saver(each):
    print(each, v.get())
    result.append(v.get())
    mappe[each] = v.get()
MODES = [ ("String", "str"), ("Number","num")]
for each in features:
    if row >4 :
        continue
    Label(master, text=each).grid(row=row, column=0)
    v = StringVar()
    v.set("default")
    col = 1
    for text, mode in MODES:
        Radiobutton(master, text=text, variable=v,value = mode, command=saver(each)).grid(row=row,column=col)
        col+=1
    print(v.get())
    row += 1         
mainloop()

My problem is when i run this code, I get the UI as expected, but I am not able to store all the variables in the mappe dictionary or the result list.

All values are defaulted to "default" value. I am stuck with this one.

fhdrsdg
  • 10,297
  • 2
  • 41
  • 62
ggorantl
  • 75
  • 1
  • 11

2 Answers2

2

You make a new StringVar v for every entry in features, but you don't save the previous one to later reference it. You should make v a dictionary and save the StringVars under the key of the feature they belong to.

Also, the command argument expects a reference to a function, but you give it a function call, which means that the return value of that function will be assigned to command. So instead of using command=function(), one should use command=function. Because you want to pass a variable to the function, you need the command to call a new function which calls your actual function. Fortunately, you can do this very easily using a lambda function like

command = lambda: saver(each)

However, because lambda functions are only evaluated when they are called, you need to specify that you want to use the each at that time, not the each from the last iteration. You do that using

command = lambda each=each: saver(each)

Then, for saving the input I'd recommend using the dictionary instead of the list, because you reference the items in features using their name, not their index.

Combining all that, your program becomes:

from Tkinter import *

features = ['a', 'b', 'c', 'd']
master = Tk()
row = 0
mappe = {}
v = {}

def saver(each):
    print(each, v[each].get())
    mappe[each] = v[each].get()
    print mappe
    
MODES = [ ("String", "str"), ("Number","num")]

for each in features:

    if row >4 :
        continue
        
    Label(master, text=each).grid(row=row, column=0)
    v[each] = StringVar()
    v[each].set("default")
    col = 1
    
    for text, mode in MODES:
        Radiobutton(master, text=text, variable=v[each], value=mode, 
                    command=lambda feature=each: saver(feature)).grid(row=row, column=col)
        col+=1
        
    row += 1   
    
master.mainloop()
martineau
  • 119,623
  • 25
  • 170
  • 301
fhdrsdg
  • 10,297
  • 2
  • 41
  • 62
  • This worked perfectly, Thanks a lot. Do you by any chance know how i can use scroll bar with this. When i add scroll bar, it appears only on the first row . I am using scrollbar = Scrollbar(master) scrollbar.grid(column=3). – ggorantl Apr 20 '15 at 18:58
  • 2
    To do that, you have to make a Canvas, bind the Scrollbar to that, put a Frame in the Canvas and put all widgets in the Frame. See an example [here](http://stackoverflow.com/a/3092341/3714930). – fhdrsdg Apr 20 '15 at 19:17
0

This tripped me up as well. The interface feels inconsistent to me since the parent frames persist, but other variables passed into the factories do not. Another workaround is to monkeypatch the parent with it. It feels like a hack, and I can't speak to how Pythonish it is, but in a way it seems very elegant.

label = Label(master, text=each).grid(row=row, column=0)
label.v = StringVar() 
label.v.set("Default")
for text, mode in MODES:
    Radiobutton(master, text=text, variable=label.v, value = mode, 
                command=saver(each)).grid(row=row,column=col)      
    col+=1
    print(label.v.get())
    row += 1 

This will cause the variable to persist when it didn't otherwise. Understandably, you run the risk of overriding existing variables in the Label class. However, it does eliminate the need to track variables outside the scope of where they were assigned to the label.

geekly
  • 588
  • 6
  • 10