Найти - Пользователи
Полная версия: Функция для Frame
Начало » GUI » Функция для Frame
1 2
Voroshek
Здравствуйте.
При клике по Frame вызывается функция fn(). Можно ли замутить так, чтобы она вызывалась и при клике по объектам, расположенным на этой Frame? Ну кроме того, чтобы вручную прикручивать её к каждому объекту.
drevoborod
Если каждый объект реализован в виде класса, унаследованного от класса 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()
4kpt_IV
Вот как-раз так ни в коем случае писать нельзя

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

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

Правильный подход. Построить свой класс рамки. Этот класс должен наследоваться от рамки. В нем переопределяешь бинд таким образом, чтобы он автоматически биндил все дочерние объекты.
drevoborod
4kpt_IV
Вот как-раз так ни в коем случае писать нельзя
Целью моего поста было всего лишь показать работу метода winfo_children(), а не предложение таким образом реализовывать код В первой же строчке моего поста написано про наследование классов вообще-то. Я подразумевал, что и контейнерный класс тоже отнаследован от соответствующего, то есть, как вы и написали, от Frame. Я предположил, что человек просто не знает про существование этого метода, а уж как именно он его задействует, оставил на его усмотрение. Однако, у вас я вижу только общие слова, но нет конкретики - как же именно переопределить метод bind()?
4kpt_IV
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. Сильные сокращения событий не используются. Не принято.
drevoborod
Да, вы правы, надо было написать как следует, если уж решил полный пример писать. Разумеется, всё вами перечисленное мне известно, но до автоматизма не доведено пока, так что, когда пишу быстро, получается не очень красиво.
Но всё-таки целью моей было всего лишь продемонстрировать применения метода winfo_children. Когда я у вас спросил, как именно вы предлагаете реализовать переопределение bind, я имел в виду конкретный код. Будет ли в этом новом bind использоваться тот же самый winfo_children для обхода всех потомков или что-то другое?
4kpt_IV
Использовать желательно немного измененную внутреннюю реализацию winfo_children. Сразу нужно при получении дочерних виджетов вешать им bind. Плюс нужно строить это дело рекурсивно.
drevoborod
А вот про рекурсию я не подумал, полезное замечание.
Переписал пример более кошерно В таком виде класс 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()
4kpt_IV
Напрасно использовали bind_all. Он немного не для того Создайте кнопку вне рамки и убедитесь в этом

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

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

Есть же super…

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

Ну и __init__ здесь вообще лишний
drevoborod
В таком случае, мой класс будет выглядеть так:

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, как в моём примере), это не сработает. Впрочем, не уверен, что это требовалось в изначальной задаче
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