Уведомления

Группа в Telegram: @pythonsu

#1 Фев. 22, 2016 21:16:26

Voroshek
От:
Зарегистрирован: 2011-03-11
Сообщения: 54
Репутация: +  0  -
Профиль   Отправить e-mail  

Функция для Frame

Здравствуйте.
При клике по Frame вызывается функция fn(). Можно ли замутить так, чтобы она вызывалась и при клике по объектам, расположенным на этой Frame? Ну кроме того, чтобы вручную прикручивать её к каждому объекту.



python3 openSUSE-43Leap

Офлайн

#2 Март 9, 2016 22:09:34

drevoborod
Зарегистрирован: 2016-03-09
Сообщения: 15
Репутация: +  0  -
Профиль   Отправить e-mail  

Функция для Frame

Если каждый объект реализован в виде класса, унаследованного от класса tkinter, то можно примерно так:

def fn():
    ....
f = Frame()
for object in f.winfo_children():
    object.bind("<Button-1>", lambda event: fn())
....

На всякий случай написал примитивный проверочный код:
from tkinter import *
def fn(event):
    print("clicked", event)
root = Tk()
f1 = Frame(root)
b1 = Button(f1, text='Button 1')
b2 = Button(f1, text='Button 2')
t1 = Text(f1)
t1.pack()
b1.pack()
b2.pack()
for child in f1.winfo_children():
    child.bind("<1>", lambda event: fn(event))
f1.pack()
root.mainloop()

Офлайн

#3 Март 9, 2016 23:39:11

4kpt_IV
Зарегистрирован: 2016-01-08
Сообщения: 999
Репутация: +  49  -
Профиль   Отправить e-mail  

Функция для Frame

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

def fn():
    ....
f = Frame()
for object in f.winfo_children():
    object.bind("<Button-1>", lambda event: fn())
....

Тут аж целый сонм ошибок

Правильный подход. Построить свой класс рамки. Этот класс должен наследоваться от рамки. В нем переопределяешь бинд таким образом, чтобы он автоматически биндил все дочерние объекты.

Отредактировано 4kpt_IV (Март 9, 2016 23:43:46)

Офлайн

#4 Март 10, 2016 07:06:40

drevoborod
Зарегистрирован: 2016-03-09
Сообщения: 15
Репутация: +  0  -
Профиль   Отправить e-mail  

Функция для Frame

4kpt_IV
Вот как-раз так ни в коем случае писать нельзя
Целью моего поста было всего лишь показать работу метода winfo_children(), а не предложение таким образом реализовывать код В первой же строчке моего поста написано про наследование классов вообще-то. Я подразумевал, что и контейнерный класс тоже отнаследован от соответствующего, то есть, как вы и написали, от Frame. Я предположил, что человек просто не знает про существование этого метода, а уж как именно он его задействует, оставил на его усмотрение. Однако, у вас я вижу только общие слова, но нет конкретики - как же именно переопределить метод bind()?

Офлайн

#5 Март 10, 2016 20:34:21

4kpt_IV
Зарегистрирован: 2016-01-08
Сообщения: 999
Репутация: +  49  -
Профиль   Отправить e-mail  

Функция для Frame

drevoborod
Однако, у вас я вижу только общие слова, но нет конкретики - как же именно переопределить метод bind()?

А чем bind отличается от любых других методов экземпляра класса?

Написал, что так делать нельзя по нескольким причинам.

1. from tkinter import* делать никогда нельзя.
2. object - это зарегистрированное слово. Принято использовать или сокращение или object_.
3. Вы душите event. Во-первых это плохо, а во-вторых обработчик события теряет важный компонент. Ну и зачем это делать? Прокси функции используют наоборот, не для подавления параметров, а добавление их. Свяжите функцию с обработчиком явно, без прокси-посредника.
4. В полном примере использование прокси-функции вообще не имеет никакого смысла

child.bind("<1>", lambda event: fn(event))

child.bind("<1>", fn)

5. Сильные сокращения событий не используются. Не принято.

Офлайн

#6 Март 10, 2016 20:45:46

drevoborod
Зарегистрирован: 2016-03-09
Сообщения: 15
Репутация: +  0  -
Профиль   Отправить e-mail  

Функция для Frame

Да, вы правы, надо было написать как следует, если уж решил полный пример писать. Разумеется, всё вами перечисленное мне известно, но до автоматизма не доведено пока, так что, когда пишу быстро, получается не очень красиво.
Но всё-таки целью моей было всего лишь продемонстрировать применения метода winfo_children. Когда я у вас спросил, как именно вы предлагаете реализовать переопределение bind, я имел в виду конкретный код. Будет ли в этом новом bind использоваться тот же самый winfo_children для обхода всех потомков или что-то другое?

Офлайн

#7 Март 10, 2016 21:00:35

4kpt_IV
Зарегистрирован: 2016-01-08
Сообщения: 999
Репутация: +  49  -
Профиль   Отправить e-mail  

Функция для Frame

Использовать желательно немного измененную внутреннюю реализацию winfo_children. Сразу нужно при получении дочерних виджетов вешать им bind. Плюс нужно строить это дело рекурсивно.

Офлайн

#8 Март 10, 2016 22:04:54

drevoborod
Зарегистрирован: 2016-03-09
Сообщения: 15
Репутация: +  0  -
Профиль   Отправить e-mail  

Функция для Frame

А вот про рекурсию я не подумал, полезное замечание.
Переписал пример более кошерно В таком виде класс MyFrame можно даже импортировать из модуля. В демонстрационном примере используются различные виджеты, встроенные в корневой контейнер на 1-2 уровня. Вместо winfo_children с рекурсией я воспользовался готовым решением - методом bind_all .

import tkinter
class MyFrame(tkinter.Frame):
    def __init__(self, parent=None, **options):
        tkinter.Frame.__init__(self, master=parent, **options)
    def bind(self, sequence=None, func=None, add=None):
        tkinter.Frame.bind_all(self, sequence, func, add)
def myfunc(event):
    print("clicked", event.widget._name)
if __name__ == "__main__":
    root = tkinter.Tk()
    external_frame = MyFrame(root)
    tkinter.Button(external_frame, text='External Button 1').pack()
    tkinter.Button(external_frame, text='External Button 2').pack()
    internal_frame = tkinter.Frame(external_frame)
    internal_frame.pack()
    tkinter.Button(internal_frame, text='Internal button').pack()
    text1 = tkinter.Text(external_frame)
    checkbox1 = tkinter.Checkbutton(text="Check me")
    checkbox2 = tkinter.Checkbutton(text="Check me too")
    text1.window_create("end", window=checkbox1)
    text1.insert('end', '  some text  ')
    text1.window_create('end', window=checkbox2)
    text1.pack()
    external_frame.bind("<Button-1>", myfunc)
    external_frame.pack()
    root.mainloop()

Отредактировано drevoborod (Март 10, 2016 22:08:03)

Офлайн

#9 Март 10, 2016 22:19:43

4kpt_IV
Зарегистрирован: 2016-01-08
Сообщения: 999
Репутация: +  49  -
Профиль   Отправить e-mail  

Функция для Frame

Напрасно использовали bind_all. Он немного не для того Создайте кнопку вне рамки и убедитесь в этом

И так уже не делается:

tkinter.Frame.__init__(self, master=parent, **options)

Есть же super…

super().__init__(self, master=parent, **options)

Ну и __init__ здесь вообще лишний

Офлайн

#10 Март 10, 2016 23:22:42

drevoborod
Зарегистрирован: 2016-03-09
Сообщения: 15
Репутация: +  0  -
Профиль   Отправить e-mail  

Функция для Frame

В таком случае, мой класс будет выглядеть так:

class MyFrame(tkinter.Frame):
    def bind(self, sequence=None, func=None, add=None):
        for child in self.winfo_children():
            child.bind(sequence, func, add)
            MyFrame.bind(child, sequence, func, add)

Однако, в этом случае переопределённый bind сработает только для виджетов, располагаемых с помощью упаковщика. Для виджетов, помещаемых с помощью других способов (например, Text.window_create, как в моём примере), это не сработает. Впрочем, не уверен, что это требовалось в изначальной задаче

Отредактировано drevoborod (Март 10, 2016 23:24:41)

Офлайн

Board footer

Модераторировать

Powered by DjangoBB

Lo-Fi Version