2016-02-24 2 views
0

Я запускаю простой диалог из консольного скрипта, чтобы показать список элементов, который должен быть закрыт, когда элемент списка дважды щелкнут. Я скрываю корень и жду, когда действие будет продолжено с помощью скрипта. Тем не менее, ничего не происходит, когда я дважды нажимаю элементы списка, несмотря на то, что некоторые сообщения должны печататься на консоли.Tkinker listbox bind не работает

import Tkinter as tk 

class SelectionList: 
    def __init__(self, list_of_options): 
     root = tk.Tk() 
     self.top = tk.Toplevel(root) 
     root.withdraw() 
     self.selection = None 
     self.initialize(list_of_options) 
     root.wait_window(self.top) 

    def initialize(self, list_of_options): 
     frame = tk.LabelFrame(self.top, text='Select an option') 
     frame.grid(
      row=0, columnspan=1, sticky='W', padx=5, pady=5, ipadx=5, ipady=5) 
     self.listbox = tk.Listbox(self.top, width=25, height=10) 
     self.listbox.grid(row=0, column=0, sticky='E', padx=5, pady=2) 
     for opt in list_of_options: 
      self.listbox.insert(tk.END, opt) 
     self.listbox.bind('<Double-1>', self.selection) 

    def selection(self): 
     print 'Hello world' 
     self.selection = self.listbox.get(self.listbox.curselection()[0]) 
     print self.selection 
     self.quit() 

Есть идеи?

EDIT: На основании ответа Гилла, я отредактировал мой код:

  • Теперь метод называется _get_selection, чтобы избежать конфликтов с переменной self.selection. Метод также получает событие, даже если я его не использую
  • Я вызываю mainloop и удаляю Toplevel. Класс теперь наследуется от Tkinker.Tk, поэтому он имеет метод quit.

Тем не менее, он все еще не работает. Появится диалоговое окно, но при двойном щелчке (даже при ошибке) ничего не печатается на консоли. Что мне не хватает?

Код:

import Tkinter as tk 


class SelectionList(tk.Tk): 
    def __init__(self, list_of_options): 
     tk.Tk.__init__(self, None) 
     self.title('Select an option') 
     self.selection = None 
     self.initialize(list_of_options) 

    def initialize(self, list_of_options): 
     frame = tk.LabelFrame(self, text='Select an option') 
     frame.grid(
      row=0, columnspan=1, sticky='W', padx=5, pady=5, ipadx=5, ipady=5) 
     self.listbox = tk.Listbox(self, width=25, height=10) 
     self.listbox.grid(row=0, column=0, sticky='E', padx=5, pady=2) 
     for opt in list_of_options: 
      self.listbox.insert(tk.END, opt) 
     self.listbox.bind('<Double-1>', lambda ev: self._get_selection) 

    def _get_selection(self, event): 
     print 'Hello world' 
     self.selection = self.listbox.get(self.listbox.curselection()) 
     print self.selection 
     self.quit() 


if __name__ == '__main__': 
    list_of_options = ['a', 'b', 'c', 'd'] 
    dialog = SelectionList(list_of_options) 
    dialog.mainloop() 
    print 'Selected: ' + str(dialog.selection) 
+0

Существует что-то не так с отступом вашего кода. –

+0

Где код 'self.quit()'? – mshildt

+0

Вы не вызываете 'mainloop()'. –

ответ

2

Есть целый ряд проблем.

1) Вы определили self.selectionдважды (или, может быть, три раза, в зависимости от того, как вы рассчитываете). Существует метод, называемый selection. Затем в __init__ есть атрибут с таким же именем, что и None (который затем сбрасывается по методу selection ...). На самом деле метод затенен атрибутом, поэтому, когда вы выберете bind в списке self.selection, вы привязываете его к None, поэтому при двойном щелчке вы получите TypeError, если вы обратите внимание на консоль.

Насколько я могу судить, атрибут selection не делает для вас никакой работы. Просто удалите его. И self.selection в теле метода selection следует переименовать в нечто другое. Если вам не нужно запоминать выбранные элементы, используйте локальную переменную:

selection = self.listbox.get(self.listbox.curselection()[0]) 
print(selection) 

После этого делается:

2) Функция, связанный с виджетом должен принять аргумент события. Ваш метод selection не принимает этого (он принимает self в качестве первого аргумента и не имеет места для других аргументов). Поэтому вам нужно либо изменить подпись selection, либо изменить функцию, которую вы связываете. Для последнего:

self.listbox.bind('<Double-1>', lambda e: self.selection()) 

3) self.quit не определен. Либо определите его, либо удалите.

PS.

@BryanOakley имеет точку, когда он говорит, что вы должны позвонить root.mainloop(). Это правда, что Toplevel будет автоматически называть свой собственный эквивалент mainloop как бы то ни было, но организация вашего кода, подобного этому, удивит людей, which is not good.Более традиционная организация будет выглядеть так:

class SelectionList: 
    def __init__(self, master, list_of_options): 
     self.top = tk.Frame(master) 
     self.top.pack() 
     self.initialize(list_of_options) 
     # ... 

if __name__ == '__main__': 
    root = tk.Tk() 
    SelectionList(root, ['foo', 'bar']) 
    root.mainloop() 
    # You do not need wait_window for what you do 

EDIT:

Был фатальная опечатка в моем оригинальный ответ:

self.listbox.bind('<Double-1>', lambda e: self.selection) 

не будет вызывать self.selection() по двойному щелчку. Это должно было

self.listbox.bind('<Double-1>', lambda e: self.selection()) 

Без () это как вызов bar() в следующем коде. foo() не будет вызван, и ничего не будет напечатано.

def foo(): 
    print('foo') 

def bar(): 
    foo # should be foo() 

И конкретизации другой вариант: если вы хотите, чтобы избежать lambda вы можете просто добавить аргумент события в self.selection (теперь self._get_selection в отредактированном вопрос, но я буду продолжать с старой версии здесь). В вашем случае аргумент события не будет использоваться, но это нормально.

def selection(self, event): 
    # no change to the rest 

И связывание может быть просто (на этот раз без ()):

self.listbox.bind('<Double-1>', self.selection) 
+0

Существует также проблема, что 'mainloop()' никогда не вызывается, поэтому код не может обрабатывать события. –

+0

@BryanOakley По причинам, которые я сам не понимаю, код OP будет работать без вызова 'mainloop()'. Я не могу проверить его с помощью python 2 (что и использует OP), но на 3.4 и 3.5 появится список, если вы просто создаете экземпляр 'SelectionList'. – gil

+0

@BryanOakley Ну, думаю, теперь я знаю. Это потому, что OP использует 'Toplevel', для которого не требуется' mainloop() 'для запуска цикла. – gil

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