Tkinter Grid: как размещать виджетов, чтобы они не склеивались - программирование
Подтвердить что ты не робот

Tkinter Grid: как размещать виджетов, чтобы они не склеивались

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

В моих исследованиях я нашел предложения использовать липкие, padx и pady варианты. Но независимо от того, какие аргументы я передаю .grid(), я не могу создать пространство между моими виджетами. Я понимаю, что независимо от количества столбцов и строк между двумя виджетами, если указанные строки/столбцы пустые, то, как если бы они не существовали, а виджеты отображались вместе.

Используя метод .grid(), как я могу позиционировать виджетов так, чтобы они не склеивались?

Вот мой код:

#!/usr/bin/python
from Tkinter import *

class MyApp:
    def __init__(self, parent):
        self.myParent = parent
        self.main_container = Frame(parent)
        self.main_container.grid(row=0, rowspan=2, column=0, columnspan=4)

        self.top_frame = Frame(self.main_container)
        self.top_frame.grid(row=0, column=0, columnspan=4)

        self.top_left = Frame(self.top_frame)
        self.top_left.grid(row=0, column=0, columnspan=2)

        self.top_right = Frame(self.top_frame)
        self.top_right.grid(row=0, column=2, columnspan=2)

        self.bottom_frame = Frame(self.main_container)
        self.bottom_frame.grid(row=2, column=0, columnspan=4)

        self.top_left_label = Label(self.top_left, text="Top Left")
        self.top_left_label.grid(row=0, column=0, sticky='W', padx=2, pady=2)

        self.top_right_label = Label(self.top_right, text="Top Right")
        self.top_right_label.grid(row=0, column=4, sticky='E', padx=2, pady=2)

        self.text_box = Text(self.bottom_frame, height=5, width=40)
        self.text_box.grid(row=0, column=0)

root = Tk()
root.title("Test UI")
myapp = MyApp(root)
root.mainloop()

~~ Update ~~

Я пробовал следующее, но это не сработало:

    self.top_left = Frame(self.top_frame)
    self.top_left.grid(row=0, column=0, columnspan=2)
    for c in range(2):
        self.top_left.columnconfigure(c, weight=2)

    self.top_right = Frame(self.top_frame)
    self.top_right.grid(row=0, column=2, columnspan=2)
    for c in range(2):
        self.top_right.columnconfigure(c, weight=2)
4b9b3361

Ответ 1

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

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

Разделить и победить

Лучший способ сделать макет в Tkinter - "разделить и победить". Начните с самых внешних виджетов и получите их точно так, как вы хотите. Затем решайте каждый из них за раз.

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

self.main_container = Frame(parent. background="bisque")
self.main_container.pack(side="top", fill="both", expand=True)

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

Затем у вас есть два кадра - top_frame и bottom_frame. По их именам и судя по тому, как вы пытались использовать сетку, я предполагаю, что они должны заполнить GUI в направлении x. Кроме того, я предполагаю, что верхний кадр - это своего рода панель инструментов, а нижний фрейм - настоящая "главная" часть вашего графического интерфейса. Таким образом, пусть нижний виджет занимает все лишнее пространство.

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

    self.top_frame = Frame(self.main_container, background="green")
    self.bottom_frame = Frame(self.main_container, background="yellow")
    self.top_frame.pack(side="top", fill="x", expand=False)
    self.bottom_frame.pack(side="bottom", fill="both", expand=True)

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

    self.top_left = Frame(self.top_frame, background="pink")
    self.top_right = Frame(self.top_frame, background="blue")
    self.top_left.pack(side="left", fill="x", expand=True)
    self.top_right.pack(side="right", fill="x", expand=True)

Затем у вас есть ваши "угловые" метки. Опять же, поскольку контейнер представляет собой только один ряд виджетов, pack упрощает работу. Поскольку вам нужны метки в углах, мы будем устанавливать атрибут sticky немного по-другому для каждого:

    self.top_left_label = Label(self.top_left, text="Top Left")
    self.top_right_label = Label(self.top_right, text="Top Right")
    self.top_left_label.pack(side="left")
    self.top_right_label.pack(side="right")

Наконец, у вас есть текстовый виджет. Он заполняет всю нижнюю рамку, поэтому еще раз pack наш друг:

    self.text_box = Text(self.bottom_frame, height=5, width=40, background="gray")
    self.text_box.pack(side="top", fill="both", expand=True)

пакет или сетка?

Вы использовали сетку для своего исходного кода и спросили, как ее исправить. Почему я использовал пакет для своих примеров?

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

В моих графических интерфейсах я почти всегда использую комбинацию grid и pack. Они оба сильны и преуспевают в разных вещах. Единственное, что нужно запомнить - , и это важно - это то, что вы не можете использовать их в одном родителе. Используя ваш код в качестве примера, вы не можете использовать pack для кадра top_left и grid для кадра top_right, так как оба они имеют один и тот же родительский элемент. Однако вы можете смешивать их в одном приложении.

Еще раз, используя сетку

Хорошо, возможно, вы действительно хотите использовать grid: возможно, это школьное задание, или, может быть, вы просто хотите сосредоточиться на одном менеджере геометрии за раз. Это здорово. Вот как я это сделаю. Опять же, вы должны разделить и победить:

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

    self.main_container.grid(row=0, column=0, sticky="nsew")
    self.myParent.grid_rowconfigure(0, weight=1)
    self.myParent.grid_columnconfigure(0, weight=1)

Хорошо, теперь для верхнего и нижнего фреймов. Удалите pack и добавьте следующие строки. У вас все еще есть только один столбец, но на этот раз есть пара строк. Обратите внимание, какая строка имеет вес 1:

    self.top_frame.grid(row=0, column=0, sticky="ew")
    self.bottom_frame.grid(row=1, column=0,sticky="nsew")
    self.main_container.grid_rowconfigure(1, weight=1)
    self.main_container.grid_columnconfigure(0, weight=1)

Угловые рамы - наконец, контейнер с более чем одним столбцом! Позвольте создать третий столбец посередине, чтобы занять всю слабину. Замените операторы pack следующим образом, обращая пристальное внимание на то, что дано "вес":

    self.top_left.grid(row=0, column=0, sticky="w")
    self.top_right.grid(row=0, column=2, sticky="e")
    self.top_frame.grid_columnconfigure(1, weight=1)

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

    self.top_left_label.grid(row=0, column=0, sticky="w")
    self.top_right_label.grid(row=0, column=0, sticky="e")

Наконец, у нас есть текстовый виджет, который является единственным виджетами в нижнем фрейме. Замените заключительные операторы pack следующим образом:

    self.text_box.grid(row=0, column=0, sticky="nsew")
    self.bottom_frame.grid_rowconfigure(0, weight=1)
    self.bottom_frame.grid_columnconfigure(0, weight=1)

Заключение

Разметка виджета проста, но вы должны быть систематически об этом. Подумайте, что вы делаете, организуйте свои виджеты в группы, а затем занимайтесь группами по одному. Когда вы это сделаете, должно стать очевидным, какой менеджер геометрии вы должны использовать.

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

Ответ 2

Если вы добавите bg = "red" к конструкторам top_left и top_right, вы увидите, как Frames застряли в центре, даже используя параметр sticky. Если они не будут содержать ничего, кроме одного виджета, я рекомендую не использовать их.

#!/usr/bin/python
from Tkinter import *

class MyApp:
    def __init__(self, parent):
        self.top_left_label = Label(parent, text="Top Left")
        self.top_left_label.grid(row=0, column=0, padx=2, pady=2, sticky=N+S+W)

        self.top_right_label = Label(parent, text="Top Right")
        self.top_right_label.grid(row=0, column=1, padx=2, pady=2, sticky=N+S+E)

        self.text_box = Text(parent, height=5, width=40)
        self.text_box.grid(row=1, column=0, columnspan=2)

root = Tk()
root.title("Test UI")
myapp = MyApp(root)
root.mainloop()

Ответ 3

Вот простой способ сделать это:

  • Сначала создайте свой gui на бумаге или используя любой инструмент, который работает для вас.
  • Использовать диспетчер компоновки сетки
  • Везде, где вы хотите создать пустое пространство, используйте columnspan или rowspan свойство макета, в сочетании липкого имущества columnspan или rowspan позволяют вам назначать несколько элементов сетки компоненту gui, а свойство sticky позволяет принудительно привязать этот элемент к одной стороне (сторонам) и оставить пустое пространство на другой стороне (сторонах).

Ответ 4

Лучше поздно, чем никогда;)

Tkinter "grid" хочет собрать все вместе. Вот почему вы не можете пропускать ячейки. Вы должны указать ширину и затем привязать текст. Я добавил цвет, чтобы показать ячейки. Я положил bd в рамку. Затем мне пришлось дать ширину ячейкам слева и справа, чтобы сетка придавала им вес. Затем привяжите текст "Запад" или "Восток". Я не уверен, почему мне пришлось добавить дополнительные 2 пробела для каждой ячейки, но я решил, что это проблема с шрифтом.

Я считаю, что Rodas чище и проще, но я старался оставаться в пределах параметров, о которых вы спрашиваете.

Пакет проще и быстрее для небольших вещей.

from tkinter import *

class MyApp:
    def __init__(self, parent):
        self.myParent = parent

        self.main_container = Frame(parent, bg="green")
        self.main_container.grid()

        self.top_frame = Frame(self.main_container)
        self.top_frame.grid()

        self.top_left = Frame(self.top_frame, bd=2)
        self.top_left.grid(row=0, column=0)

        self.top_right = Frame(self.top_frame, bd=2)
        self.top_right.grid(row=0, column=2)

        self.top_left_label = Label(self.top_left, bd=2, bg="red", text="Top Left", width=22, anchor=W)
        self.top_left_label.grid(row=0, column=0)

        self.top_right_label = Label(self.top_right, bd=2, bg="blue", text="Top Right", width=22, anchor=E)
        self.top_right_label.grid(row=0, column=0)

        self.bottom_frame = Frame(self.main_container, bd=2)
        self.bottom_frame.grid(row=2, column=0)

        self.text_box = Text(self.bottom_frame, width=40, height=5)
        self.text_box.grid(row=0, column=0)

root = Tk()
root.title("Test UI")
myapp = MyApp(root)
root.mainloop()

Ответ 5

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