Найти - Пользователи
Полная версия: Всем кнопкам назначается одна и таже функция.
Начало » Python для новичков » Всем кнопкам назначается одна и таже функция.
1
masha
Прошу помочь, сама уже не могу даже предположить, что не так. Проблема в том, что всем кнопкам почему-то назначается одна и также функция. В словаре skillet перечислены координаты для каждой кнопки и функция, по идее все должно быть шикарно, но на практике, почему-то у всем кнопкам присваивается функция для кнопки с номером 8.
P.S кстати ещё один интересный момент, если менять названия кнопок, то функция будет браться уже из другой кнопки.
#!/usr/bin/python2.7
#coding:utf8
import Tkinter
class MainWindow(Tkinter.Frame):
    def __init__(self, root):
        Tkinter.Frame.__init__(self, root, height=475, width=250)
        self.pack(anchor='nw')
        self.pack_propagate(0)
        self.build_widget()
    def build_widget(self):
        skillet = {u'1': [0, 0, lambda: self.test()], u'2': [1, 0, lambda: exit(0)],
                    u'3': [2, 0, lambda: exit(0)], u'4': [3, 0, lambda: exit(0)],
                    u'5': [4, 0, lambda: exit(0)], u'6': [0, 1, lambda: exit(0)],
                    u'7': [1, 1, lambda: exit(0)], u'8': [2, 1, lambda: exit(50)],
                    u'9': [3, 1, lambda: exit(0)]}  # Ключ служит названием кнопки, а в значении хранятся
                                                              # координаты кнопки и вызываемая при нажатии функция
        button = []
        for i in skillet.keys():
            but = Tkinter.Button(self, text=i, width=20)
            but.grid(row=skillet[i][0], column=skillet[i][1], padx=25, pady=5, sticky='nw')
            but.bind("<ButtonRelease-1>", lambda event: skillet[i][2]())
            button.append(but)
    def test(self):
        print('test')
if __name__ == '__main__':
    root = Tkinter.Tk()
    root.title('Test')
    root.geometry('475x250+0+0')
    main = MainWindow(root)
    root.mainloop()
FishHook
Во-первых, нахрена ключи словаря - строки?
Во-вторых, зачем такой цикл? Обычно делают так
for key, value in skillet.items():
В-третьих, использовать безымянные функции в цикле нужно осторожно, потому что они ленивые. Вот смотри
args = [1, 2 ,3]
for i in args:
    func = lambda x: x + i  # Определили функцию, но пока мы ее не вызовем, ленивая лямбда тело функции не построит
    print(func(0)) # Тут все ожидаемо, тело функции построилось с текущим значение i
funcs = []
for j in args:
    funcs.append(lambda x: x + j)
# А вот так все функции будут одинаковые lambda x: x + 3, потому что по завершению цикла j==3, и вызывая функцию, ты получаешь в ее теле j == 3 везде после этого
for f in funcs:
    print(f(0))
masha
FishHook
Во-первых, нахрена ключи словаря - строки?Во-вторых, зачем такой цикл? Обычно делают так
Я сюда пример тестовую версию залила, вообще там будут строки с названиями кнопок, на пример Выход, и т д По этому ключи строки.
masha
FishHook
Подскажите пожалуйста, где можно почитать про labmda функции, точнее про эту их особенность, я раньше не встречалась с этим. Чтобы Лутц это упомянал не помню.

masha
Подскажите пожалуйста, как мне быть если хочу вот так в цикле обработку делать и в функцию нужно передать какие-то данные, я кроме того, что функцию нужно обернуть в labmda не знаю.
FishHook
masha
Подскажите пожалуйста, как мне быть если хочу вот так в цикле обработку делать и в функцию нужно передать какие-то данные, я кроме того, что функцию нужно обернуть в labmda не знаю.
Я б как-то так сделал наверное
import tkinter
root = tkinter.Tk()
def callback(num):
    def func(event):
        print("clicked button {}".format(num))
    return func
frame = tkinter.Frame(root, width=100, height=100)
for num in range(5):
    btn = tkinter.Button(frame, text=num)
    btn.bind("<Button-1>", callback(num))
    btn.pack()
frame.pack()
root.mainloop()
4kpt_II
FishHook
Не-не-не. Так не надо делать. Тут нужна лямбда без вариантов…

import tkinter
root = tkinter.Tk()
def callback(event, num):
    print num
frame = tkinter.Frame(root, width=100, height=100)
for num in range(5):
    btn = tkinter.Button(frame, text=num)
    btn.bind("<Button-1>", lambda event: callback(num))
    btn.pack()
frame.pack()
root.mainloop()

Хотя можно вообще узнать виджет без использования передатчика. Для этого вполне подойдет сам event:

import tkinter
root = tkinter.Tk()
def callback(event):
    print event["text"]
    event.widget["bg"] = "red"
frame = tkinter.Frame(root, width=100, height=100)
for num in range(5):
    btn = tkinter.Button(frame, text=num)
    btn.bind("<Button-1>", callback)
    btn.pack()
frame.pack()
root.mainloop()
4kpt_II
masha
Теперь по Вашему вопросу

#coding:utf8
import Tkinter
#
#
class MainWindow(Tkinter.Frame):
#  
    def __init__(self, root=None):
        Tkinter.Frame.__init__(self, root, height=475, width=250)
        self.pack(anchor='nw')
        self.pack_propagate(0)
        self.build_widget()
#        
    def build_widget(self):
        skillet = {u'1': [0, 0, lambda event: self.test()],
                   u'2': [1, 0, lambda event: exit(20)],
                   u'3': [2, 0, lambda event: exit(30)],
                   u'4': [3, 0, lambda event: exit(40)],
                   u'5': [4, 0, lambda event: exit(50)],
                   u'6': [0, 1, lambda event: exit(60)],
                   u'7': [1, 1, lambda event: exit(70)],
                   u'8': [2, 1, lambda event: exit(80)],
                   u'9': [3, 1, lambda event: exit(90)]}
        for i in skillet.keys():
            but = Tkinter.Button(self, text=i, width=20)
            but.grid(row=skillet[i][0], column=skillet[i][1],
                     padx=25, pady=5, sticky='nw')
            but.bind("<ButtonRelease-1>", skillet[i][2])
#
    @staticmethod
    def test(self):
        print('test')
#
if __name__ == '__main__':
    root = Tkinter.Tk()
    root.title('Test')
    root.geometry('475x250+0+0')
    main = MainWindow(root)
    root.mainloop()

Вот Вам правильное решение учитывая Вашу страсть к словарям

P.S. Плюсанул в карму. Давно уже не видел хорошего кода с Tkinter который практически не надо исправлять: с нормальным наследованием от рамки, с цикличным построением виджетов. Аж слеза навернулась.

P.S.S. Но на будущее. Такие темы желательно все же писать в разделе GUI.
FishHook
FishHook
Не-не-не. Так не надо делать. Тут нужна лямбда без вариантов…
import tkinter
root = tkinter.Tk()
def callback(event, num):
    print num
frame = tkinter.Frame(root, width=100, height=100)
for num in range(5):
    btn = tkinter.Button(frame, text=num)
    btn.bind("<Button-1>", lambda event: callback(num))
    btn.pack()
frame.pack()
root.mainloop()
Этот код не будет работать по причинам обозначенных мною выше.



Не-не-не. Так не надо делать
Обоснуй
4kpt_II
Хм… Забыл. Действительно забыл. Этот фокус с лямбдой в цикле.

import Tkinter
root = Tkinter.Tk()
def callback(event, num):
    print num
frame = Tkinter.Frame(root, width=100, height=100)
for num in range(5):
    btn = Tkinter.Button(frame, text=num)
    btn.bind("<Button-1>", lambda event, num=num: callback(event, num))
    btn.pack()
frame.pack()
root.mainloop()

Так будет работать без проблем.

FishHook
Обоснуй
Так не принято делать и все. Хотя и можно. Другие просто могут код не понять.
Это хорошо, когда у Вас там один обработчик. Иногда просто может их быть несколько (один обработчик с разным поведением на несколько событий) и получается сильное усложнение. Да и читабильнее с лямбдой.

P.S. Хотя как по-мне, то можно и так. Просто непривычно.
This is a "lo-fi" version of our main content. To view the full version with more information, formatting and images, please click here.
Powered by DjangoBB