Рубрики
Без рубрики

Разработка графического интерфейса Python с помощью Tkinter

Автор оригинала: Mateusz Dobrychlop.

Разработка графического интерфейса Python с помощью Tkinter

Это первая часть нашей многосерийной серии по разработке графических интерфейсов на Python с использованием Tkinter. Проверьте ссылки ниже для следующих частей этой серии:

  • Разработка графического интерфейса Python с помощью Tkinter
  • Разработка графического интерфейса Python с помощью Tkinter: Часть 2
  • Разработка графического интерфейса Python с помощью Tkinter: Часть 3

Вступление

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

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

Существует много пакетов для создания графических интерфейсов в Python, но есть только один такой пакет, который считается стандартом de facto и распространяется со всеми установками Python по умолчанию. Этот пакет называется Tkinter . Tkinter-это привязка Python к Tk – кросс-платформенному инструментарию GUI с открытым исходным кодом.

Создание вашего первого окна

Как уже упоминалось ранее, Tkinter доступен со стандартными установками Python, поэтому независимо от вашей операционной системы создание вашего первого окна должно быть очень быстрым. Все что вам нужно это 3 строки кода:

import tkinter

root = tkinter.Tk()

root.mainloop()

Выход:

После импорта пакета tkinter в строке 1 в строке 3 мы создаем виджет главного (корневого) окна нашего приложения. Чтобы программа работала правильно, в нашем интерфейсе должен быть только один виджет корневого окна, и, поскольку все остальные виджеты будут ниже в иерархии, чем root, он должен быть создан раньше любых других виджетов.

В строке 5 мы инициализируем корневой файл mainloop . Благодаря этой строке окно остается в цикле, который ожидает событий (например, взаимодействия с пользователем) и соответствующим образом обновляет интерфейс. Цикл заканчивается, когда пользователь закрывает окно или вызывается метод quit () .

Добавление простых виджетов в Корневое окно

В следующем примере мы познакомимся с общей двухэтапной философией создания виджетов, которая может быть применена ко всем виджетам, кроме Windows. Первым шагом является создание экземпляра класса конкретного виджета. На втором этапе мы должны использовать один из доступных методов для размещения нового виджета внутри другого уже существующего виджета (родительского виджета). Самый простой виджет , который вы можете поместить в свой интерфейс Tkinter, – это label , который просто отображает некоторый текст. В следующем примере создается простой виджет меток:

import tkinter

root = tkinter.Tk()

simple_label = tkinter.Label(root, text="Easy, right?")

simple_label.pack()

root.mainloop()

Выход:

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

Затем в строке 7 мы применяем метод ориентации нашей метки внутри корневого окна. Самый простой метод ориентации виджетов, который предлагает Tkinter, – это pack() . Метка-это единственный виджет внутри окна, поэтому он просто отображается в середине окна.

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

Добавление функциональной кнопки

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

import tkinter

root = tkinter.Tk()

root.title("Hello!")

simple_label = tkinter.Label(root, text="Easy, right?")
closing_button = tkinter.Button(root, text="Close window", command=root.destroy)

simple_label.pack()
closing_button.pack()

root.mainloop()

Выход:

В строке 8 мы создаем наш экземпляр класса Button очень похожим образом, как мы создали нашу метку. Однако, как вы, вероятно, можете видеть, мы добавили командный аргумент, в котором сообщаем программе, что должно произойти после нажатия кнопки. В этом случае вызывается драматически звучащий метод root destroy () , который при выполнении закроет наше окно.

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

Вы, наверное, заметили еще одну новую строчку. В строке 5 мы указываем корневое окно title . К сожалению, самый широкий виджет нашего интерфейса недостаточно широк, чтобы заголовок окна стал видимым. Давайте что-нибудь с этим делать.

Управление размером окна

Давайте взглянем на три новые строки, которые позволят нам легко изменить размер нашего окна.

import tkinter

root = tkinter.Tk()

root.title("Hello!")

root.resizable(width="false", height="false")

root.minsize(width=300, height=50)
root.maxsize(width=300, height=50)

simple_label = tkinter.Label(root, text="Easy, right?")
closing_button = tkinter.Button(root, text="Close window", command=root.destroy)

simple_label.pack()
closing_button.pack()

root.mainloop()

Выход:

В строке 7 мы определяем, должен ли пользователь программы иметь возможность изменять ширину и высоту окна . В этом случае оба аргумента имеют значение “false” , поэтому размер окна зависит только от нашего кода. Если бы не строки 9 и 10, это зависело бы от размеров виджетов, ориентированных внутри окна.

Однако в этом примере мы используем методы root minsize и maxsize для управления максимальными и минимальными значениями ширины и высоты нашего окна. Здесь мы точно определяем, насколько широким и высоким должно быть окно, но я рекомендую вам поиграть с этими тремя строками, чтобы увидеть, как работает изменение размера в зависимости от размера наших виджетов и от того, какие минимальные и максимальные значения мы определяем.

Подробнее об ориентации виджета

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

Во-первых, давайте, может быть, дадим pack() еще один шанс. Изменив строки 15 и 16 из предыдущего примера, мы можем немного улучшить наш интерфейс:

simple_label.pack(fill="x")
closing_button.pack(fill="x")

Выход:

Таким простым способом мы приказываем методу pack() растянуть метку и кнопку вдоль горизонтальной оси. Мы также можем изменить способ pack() бросает новые виджеты внутри окна. Например, используя следующий аргумент:

simple_label.pack(side="left")
closing_button.pack(side="left")

Выход:

Мы можем упаковать виджеты в один ряд, начиная с левой стороны окна. Однако pack () – это не единственный метод ориентации виджетов внутри их родительских виджетов. Метод, который дает самые красивые результаты, вероятно, является методом grid () , который позволяет нам упорядочивать виджеты в строках и столбцах. Взгляните на следующий пример.

import tkinter

root = tkinter.Tk()

simple_label = tkinter.Label(root, text="Easy, right?")
another_label = tkinter.Label(root, text="More text")
closing_button = tkinter.Button(root, text="Close window", command=root.destroy)
another_button = tkinter.Button(root, text="Do nothing")

simple_label.grid(column=0, row=0, sticky="ew")
another_label.grid(column=0, row=1, sticky="ew")
closing_button.grid(column=1, row=0, sticky="ew")
another_button.grid(column=1, row=1, sticky="ew")

root.mainloop()

Выход:

Чтобы сделать этот пример немного понятнее, мы избавились от строк, которые изменили заголовок и размер корневого окна. В строках 6 и 8 мы добавили еще одну метку и еще одну кнопку (обратите внимание, что нажатие на нее ничего не даст, так как мы не прикрепили к ней никакой команды).

Однако самое главное, что pack() был заменен grid() во всех случаях. Как вы, вероятно, легко можете понять, аргументы column и row позволяют нам определить, какую ячейку сетки будет занимать наш виджет. Имейте в виду, что если вы определяете одни и те же координаты для двух разных виджетов, то один из них, отображаемый далее в вашем коде, будет отображаться поверх другого.

Аргумент sticky , вероятно, не так очевиден. Используя эту опцию, мы можем прикрепить края наших виджетов к краям соответствующих ячеек сетки – n orthern (вверху), s outhern (внизу), e astern (справа) и w estern (слева). Мы делаем это, передавая простую строку, содержащую конфигурацию букв n , s , e и w .

В нашем примере мы приклеиваем края всех четырех виджетов к краям их ячеек eastern и western , поэтому строка имеет вид ew . Это приводит к тому, что виджеты растягиваются горизонтально. Вы можете играть с различными конфигурациями этих четырех букв. Их порядок в строке не имеет значения.

Теперь, когда вы знаете два разных метода ориентации виджетов, имейте в виду, что вы никогда не должны смешивать grid() и pack() внутри одного и того же контейнера.

Кадры

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

Давайте попробуем сделать это с помощью наших четырех простых виджетов:

import tkinter

root = tkinter.Tk()

frame_labels = tkinter.Frame(root, borderwidth="2", relief="ridge")
frame_buttons = tkinter.Frame(root, borderwidth="2", relief="ridge")

simple_label = tkinter.Label(frame_labels, text="Easy, right?")
another_label = tkinter.Label(frame_labels, text="More text")

closing_button = tkinter.Button(frame_buttons, text="Close window", command=root.destroy)
another_button = tkinter.Button(frame_buttons, text="Do nothing")

frame_labels.grid(column=0, row=0, sticky="ns")
frame_buttons.grid(column=1, row=0)

simple_label.grid(column=0, row=0, sticky="ew")
another_label.grid(column=0, row=1, sticky="ew")

closing_button.pack(fill="x")
another_button.pack(fill="x")

root.mainloop()

Выход:

Давайте внимательно рассмотрим пример, показанный выше. В строках 5 и 6 мы определяем два новых виджета Frame . Очевидно, что в первом аргументе мы указываем на их родительский виджет, который является корневым окном.

По умолчанию границы фреймов невидимы, но допустим, мы хотели бы видеть, где именно они расположены. Чтобы показать их границы, мы должны дать им определенную ширину (в нашем примере 2 пикселя) и стиль рельефа (своего рода 3D-эффект), в котором будет нарисована граница. Есть 5 различных стилей рельефа на выбор – в нашем примере мы используем гребень .

Определения Label и Button также были слегка изменены (строки 8-12). Мы хотели разместить ваши метки в вашем frame_labels фрейме, а наши кнопки-в нашем frame_buttons фрейме. Таким образом, мы должны были заменить их предыдущего родителя, root , на их соответствующих новых родителей фрейма.

В строках 14 и 15 мы ориентируем фреймы внутри корневого окна с помощью метода grid () . Затем мы используем метод grid() для ориентации меток (строки 17-18) и метод pack() для ориентации кнопок (строки 20-21). Метки и кнопки теперь находятся в отдельных контейнерах, поэтому ничто не мешает нам ориентировать виджеты с помощью различных методов.

Окна Верхнего Уровня

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

import tkinter

root = tkinter.Tk()

new_window = tkinter.Toplevel()
new_window.withdraw()

frame_labels = tkinter.Frame(root, borderwidth="2", relief="ridge")
frame_buttons = tkinter.Frame(root, borderwidth="2", relief="ridge")

simple_label = tkinter.Label(frame_labels, text="Easy, right?")
another_label = tkinter.Label(frame_labels, text="More text")

closing_button = tkinter.Button(frame_buttons, text="Close window", command=root.destroy)
window_button = tkinter.Button(frame_buttons, text="Show new window", command=new_window.deiconify)

frame_labels.grid(column=0, row=0, sticky="ns")
frame_buttons.grid(column=1, row=0)

simple_label.grid(column=0, row=0, sticky="ew")
another_label.grid(column=0, row=1, sticky="ew")

closing_button.pack(fill="x")
window_button.pack(fill="x")

root.mainloop()

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

Мы хотели бы показать новое окно после нажатия кнопки. В строке 5 он отображается сразу же, поэтому мы используем метод withdraw() в строке 6, чтобы скрыть его. Затем мы изменяем определение кнопки в строке 15.

Помимо нового имени переменной и текста, кнопка теперь выполняет команду – метод объекта new_window , deiconify , которая заставит окно появиться снова после того, как пользователь нажмет кнопку window_button .

Выводы

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

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