Tkinter. Создание скроллбаров

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

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

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

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

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

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

Как добавлять скроллинг куда-угодно?

Давайте попробуем показать, как работают классы Canvas и Scrollbar вместе. Для этого создадим программу, которая будет загружать изображение, которое и изменяет размеры контейнера. После того, как нажать на кнопку «Загрузить изображения», она исчезает, а в элемент класса Canvas добавляется картинка, размер которой больше, чем у самого контейнера. В качестве такого изображения может использоваться любой графический файл.Tkinter. Создание скроллбаров

Вот так будет выглядеть окно программы, если в нее будет загружено изображение.Tkinter. Создание скроллбаров

Обратите внимание на скроллбары, которые появились после того, как был вставлен в окно логотип Python. 

Интерфейс скроллинга стандартный у виджета Canvas. Кроме этого, данный элемент обладает методом create_window(), который предусматривает, что файл будет располагаться в той же папке.

import tkinter as tk

class App(tk.Tk):

    def __init__(self):

        super().__init__()

        self.scroll_x = tk.Scrollbar(self, orient=tk.HORIZONTAL)

        self.scroll_y = tk.Scrollbar(self, orient=tk.VERTICAL)

        self.canvas = tk.Canvas(self, width=300, height=100,

                                xscrollcommand=self.scroll_x.set,

                                yscrollcommand=self.scroll_y.set)

        self.scroll_x.config(command=self.canvas.xview)

        self.scroll_y.config(command=self.canvas.yview)

        self.frame = tk.Frame(self.canvas)

        self.btn = tk.Button(self.frame, text=»Загрузить изображение»,

                             command=self.load_image)

        self.btn.pack()

        self.canvas.create_window((0, 0), window=self.frame,

                                  anchor=tk.N + tk.W)

        self.canvas.grid(row=0, column=0, sticky=»nswe»)

        self.scroll_x.grid(row=1, column=0, sticky=»we»)

        self.scroll_y.grid(row=0, column=1, sticky=»ns»)

        self.rowconfigure(0, weight=1)

        self.columnconfigure(0, weight=1)

        self.bind(«», self.resize)

        self.update_idletasks()

        self.minsize(self.winfo_width(), self.winfo_height())

    def resize(self, event):

        region = self.canvas.bbox(tk.ALL)

        self.canvas.configure(scrollregion=region)

    def load_image(self):

        self.btn.destroy()

        self.image = tk.PhotoImage(file=»python.gif»)

        tk.Label(self.frame, image=self.image).pack()

if __name__ == «__main__»:

    app = App()

    app.mainloop()

Теперь давайте разбирать этот код подробно, шаг за шагом. Рассмотрим принципы работы скроллбаров в Tkinter, и разберемся, как сделать окно адаптивным в случае, если, например, изменяется его размер. 

Принцип работы скроллбаров в Tkinter

С помощью первых строк программы мы создаем скроллбары и добавляем их к элементу Canvas с использованием параметров xscrollcommand и yscrollcommand. В свою очередь, они ссылаются на метод set объектов scroll_x и scroll_y. С помощью этого метода мы управляем перемещением слайдера.

Кроме этого, необходимо задать параметр Command у каждого из scrollbars после того, как мы определили объект Canvas.

Эта задача достигается с помощью такого фрагмента кода. Рассмотрим его. 

self.scroll_x = tk.Scrollbar(self, orient=tk.HORIZONTAL)

self.scroll_y = tk.Scrollbar(self, orient=tk.VERTICAL)

self.canvas = tk.Canvas(self, width=300, height=100,

                                  xscrollcommand=self.scroll_x.set,

                                  yscrollcommand=self.scroll_y.set)

self.scroll_x.config(command=self.canvas.xview)

self.scroll_y.config(command=self.canvas.yview)

Пользователь также может задать параметры объекта Canvas уже после того, как будут созданы скроллбары. Как говорят англичане, It’s up to you (на ваше усмотрение). В этом плане Python – удобный язык программирования, поскольку позволяет выполнять некоторые действия не в строгой последовательности, а так, как требует того сама задача.

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

Собственно, если говорить об аргументах, то их необходимо передать два. Первый – это расположение виджета, который передается в виде аргумента window. Так, как оси абсцисс и ординат виджета находятся в левой части экрана сверху, то, соответственно, координаты виджета должны быть 0 по оси X и 0 по оси Y. Выравнивание же выставляем следующее – anchor=tk.NW

self.frame = tk.Frame(self.canvas)

# …

self.canvas.create_window((0, 0), window=self.frame, anchor=tk.NW)

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

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

self.rowconfigure(0, weight=1)

self.columnconfigure(0, weight=1)

self.bind(«<Configure>», self.resize)

После этого указываем минимальный размер основного окна с теми параметрами, которые есть на данный момент. Чтобы получить актуальную ширину и высоту, необходимо использовать методы winfo_width() и winfo_height().

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

self.update_idletasks()

self.minsize(self.winfo_width(), self.winfo_height())

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

Теперь предположим, нам надо обработать непосредственно изменение размеров окна и изменить параметр scrollregion в соответствии с изменившимся характеристиками. Для этого используется метод resize. С его помощью мы определяем область скроллинга после того, как был изменен размер окна. 

Для повторных вычислений используется метод bbox() с константой ALL. Так можно вернуть окружающий размер всего виджета.

def resize(self, event):

    region = self.canvas.bbox(tk.ALL)

    self.canvas.configure(scrollregion=region)

Нет необходимости вызывать resize() в конце метода __init__, поскольку при запуске программы ряд событий <Configure> будут вызваны автоматом. 

Только несколько виджетов поддерживают скроллинг самостоятельно. Так, если использовать Listbox, Text и Canvas, то возможно использование xscrollcommand и yscrollcommand. А вот в поле ввода Entry возможно только реализовать скроллинг по оси X.

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

Обратите внимание, что в этом примере нами не был вызван geometry manager. Дело в том, что create_window() это может делать сам по себе. 

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

Таким образом, реализовать скроллинг – не так уже и сложно. Главное – быть внимательным и не допускать ошибок в написании кода. А если даже так случилось, что где-то была она допущена, необходимо своевременно запускать инструменты отладки, чтобы ее своевременно исправить. Вообще, почаще пользуйтесь отладчиком, такая рекомендация. И нашим сайтом, если появляются какие-то вопросы. Удачи.

ОфисГуру
Adblock
detector