Tkinter. Работа с текстом и курсором

Работа с текстом – одна из главных составляющих в Python. Без нее невозможно создание почти любой программы, которая использует операции ввода. Например, не получится создать ни текстовый документ, ни интерактивную текстовую игру. Даже банальный калькулятор не всегда получится создать, поскольку ввод сложных формул все же требует использования операторов ввода.

Изменение иконки курсора

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

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

Чтобы изменить иконку курсора, используется параметр cursor. Здесь мы используем значение watch, отвечающее за показ встроенной в операционную систему иконку загрузки. Чтобы отобразить стандартную стрелку со знаком вопроса, используется тип курсора question_arrow.

import tkinter as tk

class App(tk.Tk):

    def __init__(self):

        super().__init__()

        self.title(«Демо иконки курсора»)

        self.resizable(0, 0)

        self.label = tk.Label(self, text=»Нажмите для старта»)

        self.btn_launch = tk.Button(self, text=»Старт   !»,

                                    command=self.perform_action)

        self.btn_help = tk.Button(self, text=»Помощь»,

                                  cursor=»question_arrow»)

 

        btn_opts = {«side»: tk.LEFT, «expand»: True, «fill»: tk.X,

                    «ipadx»: 30, «padx»: 20, «pady»: 5}

        self.label.pack(pady=10)

        self.btn_launch.pack(**btn_opts)

        self.btn_help.pack(**btn_opts)

 

    def perform_action(self):

        self.btn_launch.config(state=tk.DISABLED)

        self.btn_help.config(state=tk.DISABLED)

        self.label.config(text=»Запуск…»)

        self.after(3000, self.end_action)

        self.config(cursor=»watch»)

 

    def end_action(self):

        self.btn_launch.config(state=tk.NORMAL)

        self.btn_help.config(state=tk.NORMAL)

        self.label.config(text=»Готово!»)

        self.config(cursor=»arrow»)

 

    def set_watch_cursor(self, widget):

        widget._old_cursor = widget.cget(«cursor»)

        widget.config(cursor=»watch»)

        for w in widget.winfo_children():

            self.set_watch_cursor(w)

 

    def restore_cursor(self, widget):

        widget.config(cursor=widget.old_cursor)

        for w in widget.winfo_children():

            self.restore_cursor(w)

 

if __name__ == «__main__»:

    app = App()

    app.mainloop()

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

Изменение курсора: базовый принцип

Если в виджете не описывается параметр cursor, используется тот, который задан в родительском объекте. То есть, чтобы задать внешний вид курсора для всех дочерних элементов, необходимо указать его один раз в родительском. Чтобы это сделать, используется команда set_watch_cursor() в качестве метода perform_action() в нашем примере. 

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

Обратите внимание! Если пользователь нажмет на кнопку «Старт» и разместит курсор мыши над кнопкой «Помощь» до того, как этот метод будет вызван, то будет использоваться значение help. Дело как раз в том, что приоритет имеет элемент в дочернем контейнере.

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

    def perform_action(self):

        self.set_watch_cursor(self)

        # …

 

    def end_action(self):

        self.restore_cursor(self)

        # …

 

    def set_watch_cursor(self, widget):

        widget._old_cursor = widget.cget(«cursor»)

        widget.config(cursor=»watch»)

        for w in widget.winfo_children():

            self.set_watch_cursor(w)

 

    def restore_cursor(self, widget):

        widget.config(cursor=widget.old_cursor)

        for w in widget.winfo_children():

            self.restore_cursor(w)

Обратите внимание на то, что в этом отрезке кода свойство _old_cursor добавляется к каждому элементу окна. Если захотите использовать такой алгоритм, то не забывайте о невозможности осуществить вызов restore_cursor() до set_watch_cursor().

Виджет Text

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

Давайте рассмотрим простой пример использования виджета Text, где осуществляется добавление текста и удаление средствами программы.Tkinter. Работа с текстом и курсором

Кроме элемента класса Text, эта программа включает три кнопки: «Очистить», «Вставить», «Печать».

Первая используется для удаления всего текста из поля. Вторая – для добавления текстовой строки в том месте, где размещается курсор.

На практике программа реализуется следующим образом.

import tkinter as tk

 

class App(tk.Tk):

    def __init__(self):

        super().__init__()

        self.title(«Демо виджета Text»)

        self.resizable(0, 0)

        self.text = tk.Text(self, width=50, height=10)

        self.btn_clear = tk.Button(self, text=»Очистить»,

                                   command=self.clear_text)

        self.btn_insert = tk.Button(self, text=»Вставить»,

                                    command=self.insert_text)

        self.btn_print = tk.Button(self, text=»Печать»,

                                   command=self.print_selection)

        self.text.pack()

        self.btn_clear.pack(side=tk.LEFT, expand=True, pady=10)

        self.btn_insert.pack(side=tk.LEFT, expand=True, pady=10)

        self.btn_print.pack(side=tk.LEFT, expand=True, pady=10)

 

    def clear_text(self):

        self.text.delete(«1.0», tk.END)

 

    def insert_text(self):

        self.text.insert(tk.INSERT, «Hello, world»)

 

    def print_selection(self):

        selection = self.text.tag_ranges(tk.SEL)

        if selection:

            content = self.text.get(*selection)

            print(content)

 

if __name__ == «__main__»:

    app = App()

    app.mainloop()

Принцип работы текстового виджета

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

Так, для удаления содержимого используется метод delete(). В нем используется два аргумента:

  1. Стартовая позиция. Находится на первой позиции. 
  2. Конечная позиция. Располагается на второй позиции.

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

В приведенном нами примере мы убираем все содержимое текстового поля, начиная с индекса 1.0, что соответствует первой строке и нулевому столбцу вплоть до конца строки (чтобы ее задать, мы использовали tk.END).

def clear_text(self):

    self.text.delete(«1.0», tk.END)

Вставка строки осуществлялась с использованием метода insert() с двумя аргументами – местом, в которое должен вставляться текст (index) и непосредственно самим текстом, в качестве которого может выступать сама строка или переменная строкового типа. 

def insert_text(self):

    self.text.insert(tk.INSERT, «Hello, world»)

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

Также этот метод был объединен с методом get(), с помощью которого можно получить текст из поля. 

Добавление HTML-тегов

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

import tkinter as tk

import webbrowser

 

class App(tk.Tk):

    def __init__(self):

        super().__init__()

        self.title(«Демо HTML тегов»)

        self.text = tk.Text(self, width=50, height=10)

        self.btn_link = tk.Button(self, text=»Добавить ссылку»,

                                  command=self.add_hyperlink)

 

        self.text.tag_config(«link», foreground=»blue», underline=1)

        self.text.tag_bind(«link», «», self.open_link)

        self.text.tag_bind(«link», «»,

                           lambda _: self.text.config(cursor=»hand2″))

        self.text.tag_bind(«link», «»,

                           lambda e: self.text.config(cursor=»»))

 

        self.text.pack()

        self.btn_link.pack(expand=True)

 

    def add_hyperlink(self):

        selection = self.text.tag_ranges(tk.SEL)

        if selection:

            self.text.tag_add(«link», *selection)

 

    def open_link(self, event):

        position = «@{},{} + 1c».format(event.x, event.y)

        index = self.text.index(position)

        prevrange = self.text.tag_prevrange(«link», index)

        url = self.text.get(*prevrange)

        webbrowser.open(url)

 

if __name__ == «__main__»:

    app = App()

    app.mainloop()

Обратите внимание, что нами была импортирована библиотека webbrowser, с помощью которой становится возможным открытие ссылок в рамках приложения.

ОфисГуру
Adblock
detector