2012-06-11 1 views
1

У меня есть программа, которая динамически генерирует графический интерфейс. Я не знаю, сколько кнопок у меня будет, если вообще есть.Как я могу получить кнопки TK, сгенерированные циклом for, передать вход в свою команду? (Python)

Специфической проблемой является чем-то вроде этого:

for varname in self.filetextboxes: 
    if self.varDict[varname]=='': 
     self.varDict[varname] = (StringVar(),) 
     self.varDict[varname][0].set('') 

    fileButton = Button(self, text=" ", command = lambda:self.varDict[varname][0].set(tkFileDialog.askopenfilename()), image=self.filephoto) 

    ftb = Entry(self, textvariable = self.varDict[varname][0],width=40,background='white') 

У меня есть цикл, который создает текстовые поля и кнопки. StringVar() хранится в словаре с ключом varname.

потому что я не могу передать аргументы в функции обратного вызова кнопки, вместо этого я определяю лямбду в каждой кнопке. Это устанавливает StringVar(), связанный с текстовым полем, созданным в этом цикле, к выходу поля filedialog.

Проблема в том, что имя varname, переданное в лямбда, не передает значение, а только имя переменной. Поэтому, когда текстовые поля связаны с переменной, которую они создали с помощью цикла for, lambdas в кнопках постоянно использует текущее значение varname.

Другими словами, каждое текстовое поле ссылается только на одну переменную, но все кнопки устанавливают только текст окончательного текстового поля, т. Е. Текстовое поле с окончательным значением varname.

Есть ли другой способ приблизиться к этому? Могу ли я заставить лямбду каким-то образом использовать значение varname, как оно определено, и не использовать будущие значения varname?

ответ

1

Должен признаться - у меня есть немного немного в тупике. Поскольку вы соединяете Button с Entry и stringvar, одним из способов является объединение этих элементов в класс. (Я мог бы утверждать, что это немного более элегантно в любом случае ...)

import Tkinter as tk 

class ButtonEntry(tk.Frame): 
    def __init__(self,master,ss): 
     tk.Frame.__init__(self) 
     self.var=tk.StringVar() 
     self.var.set(ss) 
     self.Button=tk.Button(self,text='Button',command=lambda :self.var.set("foo!")) 
     self.Entry=tk.Entry(self,textvariable=self.var) 
     self.Button.grid(row=0,column=0) 
     self.Entry.grid(row=0,column=1) 


class App(tk.Frame): 
    def __init__(self,master=None): 
     tk.Frame.__init__(self,master) 
     self.BEs=[] 
     for i in range(10): 
      b=ButtonEntry(self,'Button %d'%i) 
      b.grid(row=i,column=0) 
      self.BEs.append(b) 


if __name__ == '__main__': 
    root=tk.Tk() 
    f=App(root) 
    f.grid(row=0,column=0) 
    root.mainloop() 

Однако, я бы очень хотелось знать, почему лямбда ведет себя так, как это делает. Я точно подумал, что вы должны работать.

EDIT

Я разыскал поведение лямбда-функции. https://stackoverflow.com/a/10452819/748858 дает отличный пример того, как это сделать должным образом. Проблема в том, что переменные в лямбда-функции все еще связаны с областью, в которой объявлен лямбда. Чтобы отделить их от этой области действия, вам нужно установить их как аргумент ключевого слова функции. Ницца! Сегодня утром я узнал что-то новое.

+0

Спасибо, создание отдельного класса было отличным решением. Хотя теперь, когда я пытаюсь вызвать grid() в моем новом классе, Filetextboxes всегда размещаются под всеми другими виджетами в кадре. – user1442389

+0

@ user1442389 - Сегодня утром я выяснил проблему с лямбдой.Я опубликовал ссылку и объяснение. На самом деле это довольно просто. – mgilson

+0

Мне нравится это решение много. Возможно, я вернусь к использованию лямбда, хотя я уже изменил весь свой код. – user1442389

1

Как правило, вы связываете с кнопкой с именем varname и StringVar() через словарь, причем ключ является идентификатором или номером кнопки, указывая на что угодно. Поскольку вы используете StringVar, я предполагаю, что вы делаете это с Tkinter. Ниже приведен простой пример, который передает номер кнопки функции, которая обращается к словарю.

from Tkinter import * 
from functools import partial 

class ButtonsTest: 
    def __init__(self): 
     self.top = Tk() 
     self.top.title('Buttons Test') 
     self.top_frame = Frame(self.top, width =400, height=400) 
     self.button_dic = {} 
     self.buttons() 
     self.top_frame.grid(row=0, column=1) 

     Button(self.top_frame, text='Exit', 
       command=self.top.quit).grid(row=10,column=1, columnspan=5) 

     self.top.mainloop() 

    ##-------------------------------------------------------------------   
    def buttons(self): 
     b_row=1 
     b_col=0 
     for but_num in range(1, 11): 
     ## note that the correct "but_num" is stored 
     self.button_dic[but_num] = "self.cb_button_%d()" % (but_num) 
     b = Button(self.top_frame, text = str(but_num), 
        command=partial(self.cb_handler, but_num)) 
     b.grid(row=b_row, column=b_col) 

     b_col += 1 
     if b_col > 4: 
      b_col = 0 
      b_row += 1 

    ##---------------------------------------------------------------- 
    def cb_button_1(self): 
     print "push button 1 and this code is executed" 

    ##---------------------------------------------------------------- 
    def cb_button_2(self): 
     print "push button 2 and this code is executed" 

    ##---------------------------------------------------------------- 
    def cb_button_3(self): 
     print "push button 3 and this code is executed" 

    ##---------------------------------------------------------------- 
    def cb_handler(self, cb_number): 
     print "cb_handler", cb_number, self.button_dic[cb_number]     
     if cb_number < 4: 
     exec(self.button_dic[cb_number]) 

##=================================================================== 
BT=ButtonsTest() 
+0

Мне не нравится использование 'exec' здесь - вы могли обойтись без него. Просто передайте функцию, которая будет выполнена cb_handler: 'partial (self.cb_handler, getattr (self, 'cb_button_% d'% i, lambda: None))' похоже, что он должен работать ... но почему не лямбда ? – mgilson

+0

Невозможно для меня функции жесткого кодирования для каждой кнопки, так как я не знаю, сколько кнопок у меня будет или у меня вообще будет. – user1442389

Смежные вопросы