Как упаковать виджет tkinter под существующим виджетом, упакованным слева?
Вопрос
Я пытаюсь написать базовый графический интерфейс Tkinter с Text
виджет вверху, затем Button
виджет выровнен по левому краю под ним, затем еще один Text
виджет под кнопкой.Проблема, с которой я столкнулся, заключается в том, что после упаковки Button
виджет слева, когда я потом пойду паковать второй Text
виджет, он помещает его рядом с кнопкой справа, а не под кнопкой.Это происходит независимо от того, что я установил side
аргумент для второго Text
виджет Вот простой фрагмент кода, демонстрирующий такое поведение:
from Tkinter import *
root = Tk()
w = Text(root)
w.pack()
x = Button(root, text="Hi there!")
x.pack(side=LEFT)
y = Text(root)
y.pack(side=BOTTOM)
root.mainloop()
Итак, как мне настроить второй Text
виджет, чтобы он отображался под кнопкой, а не справа от нее?
Решение
Обычно существует два решения проблем с компоновкой:
переключиться на использование сетки.Становится очень легко создавать макеты, подобные тем, которые вы пытаетесь выполнить.Grid может решить, вероятно, 95% всех проблем с макетом (это удивительно, если подумать — Tk делает с одним менеджером то, для чего большинству наборов инструментов требуется полдюжины!)
используйте несколько кадров.Если некоторые виджеты необходимо расположить сверху вниз, а некоторые — слева направо, вы не всегда сможете получить то, что хотите, упаковав все в один кадр.Используйте один кадр для частей макета сверху вниз и дополнительные кадры для содержимого слева направо.
Также помните, что виджеты не обязательно должны быть дочерними элементами виджета, в который они упакованы/построены в сетку.Вы можете использовать параметр «in», чтобы поместить виджеты в другой контейнер, отличный от их родительского.
Например, в вашем конкретном примере вы можете создать три кадра: верхний, средний и нижний.Упакуйте их сверху вниз в окно верхнего уровня.Затем вы можете упаковать первый текстовый виджет вверху, кнопку или кнопки горизонтально посередине, а другой текстовый виджет внизу.
Преимущество такого подхода в том, что с его помощью гораздо проще изменить макет в будущем (что, по моему опыту, всегда произойдет в какой-то момент).Вам не нужно повторно создавать родительские элементы для каких-либо виджетов, просто упакуйте/поместите/поместите их в какой-либо другой контейнер.
В вашем коротком примере это не имеет большого значения, но для сложных приложений эта стратегия может спасти жизнь.
Мой лучший совет таков:макет не является второстепенной мыслью.Подумайте немного, возможно, даже потратьте пять минут на рисование на миллиметровой бумаге.Сначала определитесь с основными областями вашего приложения и используйте для каждой из них фрейм или какой-либо другой контейнер (панельное окно, блокнот и т. д.).Как только они у вас появятся, примените один и тот же подход «разделяй и властвуй» для каждого раздела.Это позволяет вам использовать разные типы макета для разных разделов вашего приложения.Панели инструментов имеют горизонтальное расположение, формы могут иметь вертикальное расположение и т. д.
Другие советы
Сначала я неправильно понимал, как работает упаковка, и не осознавал, что вся левая сторона была «заявлена», когда я это сделал. x.pack(side=LEFT)
.Что я нашел после прочтения этот и ответ Алекса заключается в том, что на самом деле я не был после того, как x
вообще упаковать с левой стороны, а скорее иметь ее поставленный на якорь влево, используя anchor=W
(W для Запада) вместо side=LEFT
.Мой исправленный фрагмент кода, который делает то, что мне нужно, выглядит следующим образом:
from Tkinter import *
root = Tk()
w = Text(root)
w.pack()
x = Button(root, text="Hi there!")
x.pack(anchor=W)
y = Text(root)
y.pack(side=BOTTOM)
root.mainloop()
Сюда x
больше не «претендует» на левую сторону, он просто выровнен по левому краю (или западу) внутри своего блока пространства.
Упаковка происходит в том порядке, в котором вызываются методы .pack, поэтому, как только x «заберет» левую часть, все — он займет левую часть своего родителя, а все остальное внутри его родителя окажется справа от него.Вам нужен фрейм для «посредничества», например....:
from Tkinter import *
root = Tk()
w = Button(root, text="Mysterious W")
w.pack()
f = Frame(root)
x = Button(f, text="Hi there!")
x.pack()
y = Button(f, text="I be Y")
y.pack(side=BOTTOM)
f.pack(side=LEFT)
root.mainloop()
(Тексты изменены на кнопки только для более непосредственной видимости макета - Tkinter на этом Mac не отображает тексты четко, пока они не находятся в фокусе, но кнопки довольно четкие ;-).
Сделайте это так же, как WebView, используя внутренние элементы наборов виджетов Mosaic Canvas (которые очень похожи на Tk).Хитрость в том, что второй объект Frame с идентичным именем работает как Float уровня блока (inline:block;) для всего, что находится после него, и все, что вызывает «fr», уже автоматически начинается внутри него.
Вы можете сделать это для многих виджетов, выровненных по TOP, и просто добавить еще один кадр с идентичным именем, в котором вы хотите разбить Side=LEFT.Работает и после Bottom.
fr=Frame(root)
fr.pack(fill=X, side=TOP)
block1=Label(fr)
block1.pack(side=LEFT)
block2=Label(fr)
block2.pack(side=LEFT)
block3=Button(fr)
block3.pack(side=LEFT)
# NAME IT THE SAME ID NAME AS THE FIRST MAIN FRAME...
fr=Frame(root)
fr.pack(fill=X, side=TOP)
# These NOW jump into the second Frame breaking the side=LEFT in new Frame
block4=Label(fr)
block4.pack(side=LEFT)
block5=Label(fr)
block5.pack(side=LEFT)
# AND THEY CONTINUE GOING side=LEFT AFTERWARDS.