Как получить правильный виджет для обновления в динамически создаваемой сетке? - PullRequest
0 голосов
/ 23 апреля 2019

Я создаю сетку фреймов из списка списков.У меня есть класс Frame, у которого есть всплывающее меню, связанное с Button-1.Я хотел бы использовать это всплывающее меню для обновления метки внутри фрейма, которая называется всплывающим меню.Сейчас обновляется только текст метки последнего фрейма.

Я пытался поиграться с тегами связывания, так как подумал, что, возможно, Button-1 привязывается только к недавно созданному виджету, но, похоже,как это была красная сельдь.Я на самом деле полностью озадачен тем, куда идти дальше.

#!/bin/env python

import tkinter
import tkinter.messagebox


def create_grid():
    grid = []
    for i in range(0, 3001, 1000):
        row = []
        for j in range(8):
            row.append(i+j)
        grid.append(row)
    grid.reverse()
    return grid


class GridFrame(tkinter.Frame):
    def __init__(self, root, index, *args, **kwargs):
        tkinter.Frame.__init__(self, root, *args, **kwargs)
        self.labeltext = tkinter.StringVar()
        self.labeltext.set("+")

        self.popup_menu = tkinter.Menu(root, tearoff=0)
        self.popup_menu.add_command(label="Set label to 'test'", command=self.update_label)
        self.popup_menu.add_command(label="Do nothing", command=print)

        self.bind_all("<Button-1>", self.popup)
        self.bind_all("<Button-3>", self.reset_label)

        self.index_label = tkinter.Label(self, text="{0:04d}".format(index))
        self.index_label.pack()
        self.framelabel = tkinter.Label(self, textvariable=self.labeltext)
        self.framelabel.pack()

    def popup(self, event):
        try:
            self.popup_menu.tk_popup(event.x_root, event.y_root, 0)
        finally:
            self.popup_menu.grab_release()

    def reset_label(self, event):
        self.labeltext.set("+")

    def update_label(self):
        self.labeltext.set("test")


class GridGUI:
    def __init__(self, root, grid, *args, **kwargs):
        self.root = root
        root.title("Grid")

        for i, row in enumerate(grid):
            for j, index in enumerate(row):
                gridframe = GridFrame(root, index)
                gridframe.config(borderwidth=3, relief="raised")

                gridframe.grid(row=i, column=j, padx=2, pady=2, ipadx=20, ipady=30, sticky="nsew")


def main():
    grid = create_grid()
    root = tkinter.Tk()

    menubar = tkinter.Menu(root)
    filemenu = tkinter.Menu(menubar, tearoff=0)

    filemenu.add_command(label="Exit", command=root.quit)

    menubar.add_cascade(label="File", menu=filemenu)

    root.config(menu=menubar)

    my_gui = GridGUI(root, grid)
    root.mainloop()


main()

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

Ответы [ 2 ]

0 голосов
/ 23 апреля 2019

Ваша проблема в том, что вы используете bind_all, и вы сбрасываете эту привязку каждый раз, когда создаете новый GridFrame. Таким образом, только привязка к последнему GridFrame будет признана tkinter. Когда tkinter обнаруживает привязку, он вызывает popup метод окончательного GridFrame.

Вы можете обойти это, так как объект события сообщает вам, на каком виджете была нажата кнопка. Исходя из этого, вы можете определить, на каком экземпляре GridFrame щелкнули. Однако есть более простое решение.

Решение состоит в том, чтобы не использовать bind_all, а вместо этого связывать каждый виджет отдельно. Когда привязка срабатывает, она вызывает функцию, связанную с соответствующим объектом.

После создания виджетов примените привязку к каждому виджету:

class GridFrame(tkinter.Frame):
    def __init__(self, root, index, *args, **kwargs):
        ...
        for widget in (self, self.index_label, self.framelabel):
            widget.bind("<Button-1>", self.popup)

Это дает дополнительное преимущество, заключающееся в том, что у вас могут быть любые другие виджеты на экране, которые принимают нажатия кнопок (кнопки, полосы прокрутки и т. Д.), Не влияя на них из-за глобальной привязки к щелчку мыши.

0 голосов
/ 23 апреля 2019

Если вы не возражаете против уродливых хаков, вот один из способов сделать это.Вместо создания отдельного всплывающего меню в каждом фрейме, создайте только одно в главном графическом интерфейсе, проверьте местоположение всплывающего окна и измените соответствующий StringVar.

import tkinter
import tkinter.messagebox

y = []

def create_grid():
    grid_list = []
    for i in range(0, 3001, 1000):
        row = []
        for j in range(8):
            row.append(i+j)
        grid_list.append(row)
    grid_list.reverse()
    return grid_list

class GridFrame(tkinter.Frame):
    def __init__(self, root, index, num, *args, **kwargs):
        tkinter.Frame.__init__(self, root, *args, **kwargs,name=f"{num}")
        self.labeltext = tkinter.StringVar()
        y.append(self.labeltext)
        self.labeltext.set("+")
        self.index_label = tkinter.Label(self, text="{0:04d}".format(index))
        self.index_label.pack()
        self.framelabel = tkinter.Label(self, textvariable=self.labeltext)
        self.framelabel.pack()

class GridGUI:
    def __init__(self, root, grid_list, *args, **kwargs):
        root.title("Grid")
        num = 0
        for i, row in enumerate(grid_list):
            for j, index in enumerate(row):
                gridframe = GridFrame(root, index, num)
                gridframe.config(borderwidth=3, relief="raised")
                gridframe.grid(row=i, column=j, padx=2, pady=2, ipadx=20, ipady=30, sticky="nsew")
                num+=1
        self.popup_menu = tkinter.Menu(root, tearoff=0)
        self.popup_menu.add_command(label="Set label to 'test'", command=lambda: self.update_label(self.event))
        self.popup_menu.add_command(label="Do nothing", command=lambda: self.reset_label(self.event))

        root.bind_all("<Button-1>", self.popup)
        root.bind_all("<Button-3>", self.reset_label)

    def popup(self, event):
        self.event = event
        try:
            self.popup_menu.tk_popup(event.x_root, event.y_root, 0)
        finally:
            self.popup_menu.grab_release()

    def reset_label(self, event):
        result = str(event.widget).split(".")[1]
        y[int(result)].set("+")

    def update_label(self,event):
        result = str(event.widget).split(".")[1]
        y[int(result)].set("test")

def main():
    grid = create_grid()
    root = tkinter.Tk()

    menubar = tkinter.Menu(root)
    filemenu = tkinter.Menu(menubar, tearoff=0)

    filemenu.add_command(label="Exit", command=root.quit)

    menubar.add_cascade(label="File", menu=filemenu)

    root.config(menu=menubar)

    my_gui = GridGUI(root, grid)
    root.mainloop()

main()
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...