Text – многострочное текстовое поле

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

В tkinter многострочное текстовое поле создается от класса Text. По умолчанию его размер равен 80-ти знакоместам по горизонтали и 24-м по вертикали.

Размер текстового поля

Однако эти свойства можно изменять с помощью опций width и height. Есть возможность конфигурировать шрифт, цвета и другое.

from tkinter import *
root = Tk()
 
text = Text(width=25, height=5, bg="darkgreen", fg='white', wrap=WORD)
 
text.pack()
root.mainloop()

Свойства виджета Text

Значение WORD опции wrap позволяет переносить слова на новую строку целиком, а не по буквам.

Text и Scrollbar

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

В tkinter скроллеры производятся от класса Scrollbar. Объект-скроллер связывают с виджетом, которому он требуется. Это не обязательно многострочное текстовое поле. Часто полосы прокрутки бывают нужны спискам, которые будут рассмотрены позже.

from tkinter import *
root = Tk()
 
text = Text(width=20, height=7)
text.pack(side=LEFT)
 
scroll = Scrollbar(command=text.yview)
scroll.pack(side=LEFT, fill=Y)
 
text.config(yscrollcommand=scroll.set)
 
root.mainloop()

Text и Scrollbar

Здесь создается скроллер, к которому с помощью опции command привязывается прокрутка текстового поля по оси ytext.yview. В свою очередь текстовому полю опцией yscrollcommand устанавливается ранее созданный скроллер – scroll.set.

Методы Text

Основные методы у Text такие же как у Entry – get(), insert(), delete(). Однако, если в случае однострочного текстового поля было достаточно указать один индекс элемента при вставке или удалении, то в случае многострочного надо указывать два – номер строки и номер символа в этой строке (другими словами, номер столбца). При этом нумерация строк начинается с единицы, а столбцов – с нуля.

from tkinter import *
 
def insertText():
    s = "Hello World"
    text.insert(1.0, s)
 
def getText():
    s = text.get(1.0, END)
    label['text'] = s
 
def deleteText():
    text.delete(1.0, END)
 
root = Tk()
 
text = Text(width=25, height=5)
text.pack()
 
frame = Frame()
frame.pack()
 
b_insert = Button(frame, text="Вставить", command=insertText)
b_insert.pack(side=LEFT)
 
b_get = Button(frame, text="Взять", command=getText)
b_get.pack(side=LEFT)
 
b_delete = Button(frame, text="Удалить", command=deleteText)
b_delete.pack(side=LEFT)
 
label = Label()
label.pack()
 
root.mainloop()

Методы текстового поля

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

Теги

Особенностью текстового поля библиотеки Tk является возможность форматировать текст в нем, то есть придавать его разным частям разное оформление. Делается это с помощью методов tag_add() и tag_config(). Первый добавляет тег, при этом надо указать его произвольное имя и отрезок текста, к которому он будет применяться. Метод tag_config() настраивает тегу стили оформления.

from tkinter import *
root = Tk()
 
text = Text(width=50, height=10)
text.pack()
text.insert(1.0, "Hello world!\nline two")
 
text.tag_add('title', 1.0, '1.end')
text.tag_config('title', font=("Verdana", 24, 'bold'), justify=CENTER)
 
root.mainloop()

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

Вставка виджетов в текстовое поле

В Text можно вставлять другие виджеты помощью метода window_creat(). Потребность в этом не велика, однако может быть интересна с объектами типа Canvas. Данный класс будет изучен позже. В примере ниже вставляется метка в текущую (INSERT) позицию курсора.

from tkinter import *
 
def smile():
    label = Label(text=":)", bg="yellow")
    text.window_create(INSERT, window=label)
 
root = Tk()
 
text = Text(width=50, height=10)
text.pack()
 
button = Button(text=":)", command=smile)
button.pack()
 
root.mainloop()

Метод window_create()

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

Практическая работа

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

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

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

Для выполнения практической работы вам понадобится функция open() языка Python и методы файловых объектов чтения и записи. Освежить знания о них можно здесь.

Пример приложения Tkinter

Курс с примерами решений практических работ: android-приложение, pdf-версия.

Комментарии

Ответ на от Alex

from tkinter import *
 
root = Tk()
 
img = PhotoImage(file='smile.gif')
txt = Text(width=20, height=10)
txt.image_create(1.0,image=img)
txt.pack()
 
root.mainloop()

В параметре file экземпляра PhotoImage указывается адрес к файлу. Раньше поддерживался только gif и еще один формат. Сейчас возможно больше.

from tkinter import*
import pickle
root = Tk()
root.title('Txt file opener')#Название окна
#Создаем три фрэйма, чтобы упорядочить виджеты
#f_1 = Frame(root, width=200, height=200)
f_2 = Frame(root, width=200, height=200)
f_3 = Frame(root, width=200, height=200)
#f_1.pack(side=BOTTOM, anchor=W)
f_2.pack(side=BOTTOM, anchor=W)
f_3.pack(side=BOTTOM, anchor=W)
#Поле ввода имени файла
entry = Entry(f_3, width=30)
entry.pack(side=LEFT, padx=5, pady=10, anchor=W)
#Поле, где появляется текст
text_1 = Text(f_2)
text_1.pack(side=LEFT)
 
scroll_1 = Scrollbar(f_2, command=text_1.yview)
scroll_1.pack(side=LEFT, fill=Y)
 
text_1.config(yscrollcommand=scroll_1.set)
 
'''scroll_2 = Scrollbar(f_2, command=text_1.xview)
scroll_2.pack(side=BOTTOM)
#не работает по оси икс второй скроллбар( что делать?
text_1.config(xscrollcommand=scroll_2.set)'''
 
def open_file():
    text_1.delete(1.0, END)
    for i in open(entry.get(), encoding='UTF-8'):
        text_1.insert(END, i)
def save_file():
    get = text_1.get(1.0, END)
    file = open(entry.get(), encoding='UTF-8', mode='w')
    file.write(str(get))
    file.close()
but_1 = Button(f_3,
               width=20,
               text='Открыть',
               command=open_file)
but_2 = Button(f_3,
               text='Сохранить',
               width=20,
               command=save_file)
but_1.pack(side=LEFT)
but_2.pack(side=LEFT, padx=5)
 
 
 
mainloop()

# Все работает, но я не уверен, что сделал это правильно
 
from tkinter import *
 
def op():
    file = path.get()
    print(file)
    f = open("C:/Users/Сергей/.PyCharmCE2018.3/config/scratches/" + file)
    text.insert(1.0, f.read())
def sv():
    s = text.get(1.0, END)
    f2 = path.get()
    f = open("C:/Users/Сергей/.PyCharmCE2018.3/config/scratches/" + f2, "w+")
    f.write(s)
 
root = Tk()
 
frame = Frame()
frame.pack()
 
path = Entry(frame)
path.pack(side = LEFT, padx = 5, pady = 5)
 
b1 = Button(frame, width = 10, height = 1, text = 'Открыть', command = op)
b1.pack(side = LEFT, padx = 5, pady = 5)
 
b2 = Button(frame, width = 10, height = 1, text = 'Сохранить', command = sv)
b2.pack(side = LEFT, padx = 5, pady = 5)
 
text = Text(width = 40, height = 10)
text.pack(padx = 5, pady = 5)
 
root.mainloop()

Ответ на от Гость

Добрый день. Сама только учусь, но может мои подсказки вам помогут. При попытке воспроизвести код, ругается на ссылку: C:/Users/Сергей/.PyCharmCE2018.3/config/scratches/ (если убрать, то работает) Из того, что ещё увидела: 1. padx и pady не являются параметрами метода pack(), 2. нет Scrollbar, 3. данные из файлов при открытии ДОписываются в тестовое поле (лучше предыдущий текст предварительно стирать) 4. нет обработки исключений (просто выдаётся ошибка)

Практическая работа. С упаковщиком grid не смогла настроить красивый Scrollbar.
from tkinter import *
root=Tk()
 
def open_file():
    try:
        f=open(entry.get(), "r")
        text.delete(0.0, END)
        text.insert(0.0, f.read())
        f.close()
    except FileNotFoundError:
        text.delete(0.0, END)
        text.insert(0.0, "Такого файла не существует.")
 
def save_file():
    f=open(entry.get(), "w")
    lines=text.get(0.0, END)
    f.writelines(lines)
    f.close()
 
 
entry=Entry(width=30)
entry.grid(row=0, column=0, sticky=W) 
but_open=Button(text="Открыть", command=open_file)
but_open.grid(row=0, column=1, sticky=E)
but_save=Button(text="Сохранить", command=save_file)
but_save.grid(row=0, column=2, sticky=W)
text=Text(width=40, height=20, wrap=WORD)
text.grid(row=1, column=0, columnspan=3)
yscroll=Scrollbar(command=text.yview)
xscroll=Scrollbar(orient=HORIZONTAL, command=text.xview)
yscroll.grid(row=1, column=3, rowspan=3, ipady=100 )
xscroll.grid(row=2, columnspan=3, ipadx=100)
text.config(yscrollcommand=yscroll.set,xscrollcommand=xscroll.set)
 
root.mainloop()

#!/usr/bin/python3
 
from tkinter import *
from tkinter import messagebox
 
root = Tk()
 
 
def file_open():
    try:
        f = open(entry.get(), 'r')
        text.delete(1.0, END)
        if f.name == entry.get():
            for i in f:
                text.insert(END, i)
            f.close()
 
    except FileNotFoundError:
        messagebox.showerror("Ошибка", "Такой файл не существует!")
 
 
def file_save():
    str = text.get(1.0, END)
    f = open(entry.get(), 'w')
    if not f.closed:
        f.write(str)
        text.delete(1.0, END)
        messagebox.showinfo("Сообщение", "Ваш файл {0}, был удачно сохранён!".format(entry.get()))
        f.close()
 
 
f_top = Frame(root)
f_bot = Frame(root)
 
f_top.pack()
f_bot.pack(expand=1, fill=BOTH)
 
entry = Entry(f_top, width=30)
entry.insert(0, 'text.txt')
entry.pack(side=LEFT, padx=2)
 
b_open = Button(f_top, text="Открыть", padx=3, command=file_open)
b_open.pack(side=RIGHT, padx=2)
 
b_save = Button(f_top, text="Сохранить", padx=3, command=file_save)
b_save.pack(side=RIGHT, padx=2)
 
scroll_y = Scrollbar(f_bot, orient=VERTICAL)
scroll_y.pack(side=RIGHT, fill=Y)
 
scroll_x = Scrollbar(f_bot, orient=HORIZONTAL)
scroll_x.pack(side=BOTTOM, fill=X, anchor=W)
 
text = Text(f_bot, width=45, height=20, wrap=NONE, yscrollcommand=scroll_y.set, xscrollcommand=scroll_x.set)
text.pack(expand=1, fill=BOTH)
 
scroll_y.config(command=text.yview)
scroll_x.config(command=text.xview)
 
root.title("Блокнот")
root.mainloop()

from tkinter import *
 
 
root = Tk()
 
 
def open_file():
    file_1 = open(file_name.get())
    file_content.insert(1.0, file_1.read())
 
def save_file():
    file_2 = open(file_name.get(), 'w')
    file_2.write(file_content.get(1.0, END))
 
 
root.title('Copying')
 
frame = Frame()
frame_file_content = Frame()
 
file_name = Entry(frame, bd=4, relief=GROOVE, width='25')
 
button_open = Button(frame, text=' Open the old file ', command=open_file)
button_save = Button(frame, text=' Save to new one ', command=save_file)
 
file_content = Text(frame_file_content, bg='#FFFFE0', width='50', height='20', wrap=NONE)
 
Yscroll = Scrollbar(frame_file_content, command=file_content.yview)
Xscroll = Scrollbar(orient=HORIZONTAL, command=file_content.xview)
 
file_content.configure(yscrollcommand=Yscroll.set, xscrollcommand=Xscroll.set)
 
 
frame.pack()
file_name.pack(side=LEFT)
button_open.pack(side=LEFT)
button_save.pack(side=LEFT)
frame_file_content.pack(fill=BOTH, expand=1)
file_content.pack(side=LEFT, fill=BOTH, expand=1)
Yscroll.pack(side=LEFT, fill=Y)
Xscroll.pack(side=BOTTOM, fill=X)
 
 
root.mainloop()

from tkinter import *
from filelib import *
 
root = Tk()
 
def SaveFile():
    fm = FileManeg(e.get())
    fm.SaveFil(text.get(1.0, END))
 
def OpenFile():
    fm = FileManeg(e.get())
    text.delete(0.0, END)
    text.insert(0.0, fm.ReadFile())
 
frameTop = Frame(root)
frameTop.pack(side = TOP, padx = 1, pady = 1, fill = X)
 
frameBottom = Frame(root)
frameBottom.pack(side = TOP,expand = 1, fill = BOTH)
 
sframeBottom = Frame(root)
sframeBottom.pack(side = TOP, fill = X)
 
e = Entry(frameTop)
e.pack(side = LEFT, padx = 5, pady = 5, expand = 1, fill =X)
 
text = Text(frameBottom, wrap= 'none')
text.pack(side = LEFT, expand = 1, fill = BOTH)
 
yScr = Scrollbar(frameBottom,orient=VERTICAL, command = text.yview)
yScr.pack(side = LEFT, fill = Y)
xScr = Scrollbar(sframeBottom, orient=HORIZONTAL, command = text.xview)
xScr.pack(side = BOTTOM, fill = X)
 
text.config(yscrollcommand = yScr.set, xscrollcommand = xScr.set)
 
butOpen = Button(frameTop, width=10, height=1, text= "Open", command = OpenFile)
butOpen.pack(side = RIGHT)
butSave = Button(frameTop, width=10, height=1, text= "Save", command = SaveFile)
butSave.pack(side = RIGHT)
 
root.mainloop()