Как динамически обновлять слои виджетов tkinter? - PullRequest
0 голосов
/ 06 ноября 2018

Tkinter Требования

diagram showing tkinter layout

Так что я относительно новичок в использовании tkinter, и я борюсь с очень специфическим сомнением здесь. Я пытался найти решения для этого, но насколько я нахожу это очевидным, решение этого, кажется, не легко понять. Поэтому, если вы видите изображение выше, я пытаюсь создать графический интерфейс для конкретного проекта, который требует многоуровневых (я называю это 3D-массив) виджетов. Скажем, переменные, используемые для этой системы указателей: i, j и k.

Я создаю отдельные виджеты слоев, используя цикл for:

for n in range(i):
    frame_x[i] = Frame(root).grid(row = 1, column = i)
    entry_x[i] = Entry(frame_x[i]).grid(row = 2, column = i)
    button_x[i] = Button(frame_x[i]).grid(row=3, column = i)

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

Теперь приступим к проблеме. Я могу сделать основную часть этого. Но проблема в том, что я хочу, чтобы он работал динамически.

Скажем, если пользователь сначала введет j = 4. 4 блока будут созданы. Позже, если он изменит значение на j = 2 и нажмет кнопку, в идеале это должно привести к исчезновению виджетов в блоке j = 3 и 4. Но я думаю, tkinter работает на основе перекрытия и не меняет элемент сетки, пока что-то определенно не перекрывается с ним. Как я это сделал. Я попытался уничтожить весь кадр сразу после входа в цикл for, но это не сработало, так как в первый раз перед уничтожением не создавался виджет, а python выдает NameError, говоря, что я не могу использовать переменную перед присваиванием.

В любом случае, пожалуйста, дайте мне знать, как мне сделать это эффективно. А также в целом, если есть лучший способ сделать все это. Пожалуйста, обратитесь к изображению выше и дайте мне знать, если это не имеет смысла.

Мне не очень комфортно с классами Я предпочитаю неэффективный способ, используя только функции для выполнения всего, что мне нужно. Так что было бы здорово, если бы вы могли поделиться со мной фреймворком без использования классов. Но это нормально, если вы их используете. Я знаю, что должен начать работать с классами в какой-то момент.

1 Ответ

0 голосов
/ 06 ноября 2018

Прежде всего, я хочу обратиться к этой части вопроса:

Я полагаю, что tkinter работает на основе перекрытия и не меняет элемент сетки, пока что-то не перекрывается специально.

Я не совсем уверен, что вы подразумеваете под этим, но если это означает, что я думаю, это означает, что это ложное утверждение. tkinter не "работает на основе перекрытия". Если вы уничтожаете виджет, он уничтожается. Неважно, перекрыт он или нет.


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

Причина, по которой вы не помещаете виджеты в рамку, заключается в следующей строке:

frame_x[i] = Frame(root).grid(row = 1, column = i)

В python, когда вы делаете x=y().z(), x имеет значение z(). Таким образом, когда вы делаете frame_x[i] = Frame(...).grid(...), frame_x[i] имеет значение .grid(...), а .grid(...) всегда возвращает None . Таким образом, frame_x[i] будет None.

Когда вы в следующий раз делаете entry_x[i] = Entry(frame_x[i]).grid(...), это то же самое, что и entry_x[i] = Entry(None).grid(...). Поскольку мастер Entry равен None, он становится дочерним элементом корневого окна.

Итак, первый шаг - отделить создание виджета от макета виджета.

frame_x[i] = Frame(root)
frame_x[i].grid(row = 1, column = i)

Как только вы это сделаете, виджеты Entry и Button станут дочерними для фрейма, и вы можете удалить ненужные виджеты, уничтожив фрейм (например: frame_x[i].destroy()), так как уничтожив виджет также приведет к уничтожению всех дочерних элементов виджета.

Как только вы это сделаете, вы можете уничтожить ненужные виджеты, просто вызвав .destroy() в кадре. Например, если вы ранее создали 10 групп и теперь вам нужно только 5, вы можете уничтожить остальные и затем удалить их из списка следующим образом:

# assume 'num' contains the number of frames that we want,
# and that it is smaller than the number of items in frames_x
for frame in frames_x[num:]:
    frame.destroy()
frames_x = frames_x[:num]

Вот полная рабочая программа для иллюстрации. Введите число и нажмите кнопку. Это создаст столько комбинаций фрейм + запись + кнопка. Введите новый номер, который больше или меньше, и он будет добавлять или удалять виджеты.

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

import tkinter as tk

frames_x = [] def create_widgets():
    global frames_x

    num = int(num_widgets.get())
    # if the number is less than the previous number of
    # widgets, delete the widgets we no longer want
    for frame in frames_x[num:]:
        frame.destroy()
    frames_x = frames_x[:num]

    # if the number is greater than the previous number of
    # widgets, create additional widgets
    for i in range(len(frames_x), num):
        # create new widget
        frame = tk.Frame(root, bd=1, relief="raised")
        entry = tk.Entry(frame)
        button = tk.Button(frame, text="click me")

        # pack entry and button in frame
        button.pack(side="right")
        entry.pack(side="left", fill="x", expand=True)

        # grid the frame in the parent
        frame.grid(row=i+1, column=0, columnspan=2)

        # save the new frame in the array
        frames_x.append(frame)

root = tk.Tk() num_widgets = tk.Entry(root) button = tk.Button(root, text="Create widgets", command=create_widgets)

button.grid(row=0, column=1) num_widgets.grid(row=0, column=0, sticky="ew")

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