Диалоговые окна в Tkinter. Урок 11

Методическая разработка урока
Элективный курс: Модуль tkinter. Создание графического интерфейса пользователя с помощью языка программирования Python
Уровень: Программирование для начинающих
Версии Python: 3.*

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

Диалоговые окна весьма разнообразны. В данном уроке будут рассмотрены лишь несколько.

Рассмотрим, как запрограммировать с помощью Tkinter вызов диалоговых окон открытия и сохранения файлов и работу с ними. При этом требуется дополнительно импортировать "подмодуль" Tkinter - tkinter.filedialog, в котором описаны классы для окон данного типа.

from tkinter import *
from tkinter.filedialog import *
 
root = Tk()
op = askopenfilename()
sa = asksaveasfilename()
 
root.mainloop()

Здесь создаются два объекта (op и sa): один вызывает диалоговое окно "Открыть", а другой "Сохранить как...". При выполнении скрипта, они друг за другом выводятся на экран после появления главного окна. Если не создать root, то оно все-равно появится на экране, однако при попытке его закрытия в конце возникнет ошибка.

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

from tkinter import *
from tkinter.filedialog import *
 
root = Tk()
txt = Text(root,width=40,height=15,font="12")
txt.pack()
 
op = askopenfilename()
 
root.mainloop() 

При запуске скрипта появляется окно с текстовым полем и сразу диалоговое окно "Открыть". Однако, если мы попытаемся открыть какой-нибудь текстовый файл, то в лучшем случае ничего не произойдет. Как же связать содержимое текстового файла с текстовым полем через диалог "Открыть"?

Что если просто вставить содержимое переменной op в текстовое поле:

txt.insert(END,op)

После запуска скрипта и попытки открытия файла в текстовом поле оказывается адрес файла. Значит содержимое файла надо прочитать каким-то методом (функцией).

Метод input модуля fileinput может принимать в качестве аргумента адрес файла, читать его содержимое, формируя список строк. Далее с помощью цикла for можно извлекать строки последовательно и помещать их, например, в текстовое поле.

.....
import fileinput
.....
for i in fileinput.input(op):
     txt.insert(END,i)
.....

Обратите внимание на то, как происходит обращение к функции input модуля fileinput и его импорт. Дело в том, что в Python уже встроена своя функция input (ее назначение абсолютно иное) и во избежание "конфликта" требуется четко указать, какую именно функцию мы имеем ввиду. Поэтому вариант импорта 'from fileinput import input' здесь не подходит.

Окно "Открыть" запускается сразу при выполнении скрипта. На самом деле так не должно быть. Необходимо связать запуск окна с каким-нибудь событием. Пусть это будет щелчок на пункте меню.

from tkinter import *
from tkinter.filedialog import *
import fileinput
 
def _open():
     op = askopenfilename()
     for l in fileinput.input(op):
          txt.insert(END,l)
 
root = Tk()
 
m = Menu(root)
root.config(menu=m)
 
fm = Menu(m)
m.add_cascade(label="File",menu=fm)
fm.add_command(label="Open...",command=_open)
 
txt = Text(root,width=40,height=15,font="12")
txt.pack()
 
root.mainloop() 

Теперь попробуем сохранять текст, набранный в текстовом поле. Добавим в код пункт меню и следующую функцию:

def _save():
     sa = asksaveasfilename()
     letter = txt.get(1.0,END)
     f = open(sa,"w")
     f.write(letter)
     f.close()

В переменной sa храниться адрес файла, куда будет производиться запись. В переменной letter – текст, "полученный" из текстового поля. Затем файл открывается для записи, в него записывается содержимое переменной letter, и файл закрывается (на всякий случай).

Еще одна группа диалоговых окон описана в модуле tkinter.messagebox. Это достаточно простые диалоговые окна для вывода сообщений, предупреждений, получения от пользователя ответа "да" или "нет" и т. п.

Дополним нашу программу пунктом Exit в подменю File и пунктом About program в подменю Help.

from tkinter.messagebox import *
….
def close_win():
     if askyesno("Exit", "Do you want to quit?"):
          root.destroy()
 
def about():
     showinfo("Editor", "This is text editor.\n(test version)")
...
fm.add_command(label="Exit",command=close_win)
....
hm = Menu(m)
m.add_cascade(label="Help",menu=hm)
hm.add_command(label="About",command=about)
... 

В функции about происходит вызов окна showinfo, позволяющее выводить сообщение для пользователя с кнопкой OK. Первый аргумент — это то, что выведется в заголовке окна, а второй — то, что будет содержаться в теле сообщения. В функции close_win вызывается окно askyesno, которое позволяет получить от пользователя два ответа (true и false). В данном случае при положительном ответе сработает ветка if и главное окно будет закрыто. В случае нажатия пользователем кнопки "No" окно просто закроется (хотя можно было запрограммировать в ветке else какое-либо действие).

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

  1. Напишите программу, описанную в уроке.
  2. Измените программу: пусть после нажатия пункта Exit пользователю выводилось не окно с вопросом "выйти или нет", а окно с вопросом "сохранить или нет". В случае положительного ответа должна вызываться функция _save и только затем завершаться приложение.
  3. Если в текстовом поле что-то содержится, то при открытии файла оно не удаляется, а содержимое файла просто дописывается. Исправьте этот недостаток (перед открытием файла содержимое текстового поля должно удаляться).

Решение практической, в

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

from tkinter import *
from tkinter.filedialog import *
from tkinter.messagebox import *
import fileinput
 
class Window_blank:
    def __init__(self):
        main_menu = Menu(root)
        root.config(menu=main_menu)
        first_menu = Menu(main_menu)
        main_menu.add_cascade(label="File", menu=first_menu)
        first_menu.add_command(label="Open", command=self.open_file)
        first_menu.add_command(label="Save as", command=self.save_file)
        first_menu.add_command(label="About", command=self.about)
        first_menu.add_command(label="Exit", command=self.close_win)
        self.txt = Text(root, width=45, height=15)
        self.txt.pack()
 
    def open_file(self):
        op = askopenfilename()
        try:
            self.txt.delete(1.0, END)
            for i in fileinput.input(op):
                self.txt.insert(END, i)
        except:
            pass
 
    def save_file(self):
        save_as = asksaveasfilename()
        try:
            letter = self.txt.get(END, 1.0)
            f = open(save_as, "w")
            f.write(letter)
            f.close()
        except:
            pass
 
    def close_win(self):
        if askyesno("Сохранение программы", "Хотите ли сохранить данные?"):
            self.save_file()
            root.destroy()
        else:
            root.destroy()
 
    def about(self):
        showinfo("Editor Authors", "Разработано в 2014 (с)")
 
root = Tk()
root.title("Easy Text Editor")
 
 
obj_menu = Window_blank()
 
root.mainloop()

як привязати команду відкрити

як привязати команду відкрити з файлу до поля рисування, в мене ніяк не получається((

def open(self):
        """Завантажує рисунок з файлу"""
        op = askopenfilename()
        for l in fileinput.input(op):
            self.canv.insert(END,l) вже перепробував різні способи

ось моє поле для малювання

self.canv = Canvas(self, bg="grey")  # Создаем поле для рисования, устанавливаем белый фон
        self.canv.grid(row=2, column=0, columnspan=7,
                       padx=5, pady=5, sticky=E+W+S+N)  # Прикрепляем канвас методом grid. Он будет находится в 3м ряду, первой колонке, и будет занимать 7 колонок, задаем отступы по X и Y в 5 пикселей, и заставляем растягиваться при растягивании всего окна
        self.canv.bind("<B1-Motion>", self.draw) # Привязываем обработчик к канвасу. <B1-Motion> означает "при движении зажатой левой кнопки мыши" вызывать функцию draw

для сохранения нужно написать

для сохранения нужно написать в строке letter = self.txt.get(1.0,END)

Подскажите, пожалуйста, есть

Подскажите, пожалуйста, есть ли в Tkinter Text опция автоматической прокрутки вывода текста?
Если текстовый файл очень велик, мы увидим только его начало, а хочется наоборот, выводить его конец.

Думаю, подобное можно

Думаю, подобное можно реализовать программным путем.

Хорошие очень уроки. Я вот

Хорошие очень уроки. Я вот столкнулся с такой вот проблемой, что при загрузке больших файлов пишет MemoryError, не подскажете какие ограничения по размеру или количеству строк файла, а также как это обойти.

"tkinter.filedialog" для Phyton 2

у меня Phyton 2 и он не распознает "from tkinter.filedialog import *"
пишет:"ImportError: No module named filedialog"...Подскажите,пожулауйста,какой запрос необходимо использовать?

Для Python 2 первые две

Для Python 2 первые две строчки выглядят так:

from Tkinter import *
from tkFileDialog import *
...

from tkinter.messagebox

from tkinter.messagebox import * вот эта штука для 2 версии тоже не работает

tkinter - это третий питон во

tkinter - это третий питон
во втором - Tkinter с большой буквы

Для Python 2.7: from

Для Python 2.7: from tkMessageBox import *

Спасибо!

Спасибо!