Tkinter как скрыть окно
На этом шаге мы рассмотрим процесс создания таких окон .
Модальное вторичное окно , в отличие от обычного, при открытии блокирует все остальные окна приложения, в результате чего пользователь теряет доступ к ним. Остальные окна становятся доступными только после закрытия модального окна. Такие окна обычно применяются в качестве диалоговых окон для запроса каких-либо данных, необходимых для дальнейшей работы приложения.
К сожалению, библиотека Tkinter не предоставляет никаких инструментов для вывода модальных окон. Однако мы можем превратить обычное вторичное окно в модальное, вызвав у его контейнера или у него самого метод grab_set() (см. 11 шаг). Этот метод задает для контейнера или окна режим перехвата событий, в результате чего остальные окна перестают реагировать на действия пользователя. Как только окно, для которого был установлен перехват событий, закрывается и удаляется из памяти, перехват событий перестает работать, и остальные окна приложения становятся доступными для пользователя.
Есть еще один неприятный момент, связанный с реализацией модальных окон. Если после запуска приложения и вывода модального вторичного окна мы посмотрим на панель задач Windows , то увидим, что там присутствуют оба окна: и главное, и вторичное. Но присутствие модального окна на панели задач говорит о плохом стиле программирования. Чтобы скрыть вторичное окно, следует вызвать у него метод transient() (см. 21 шаг), передав ему ссылку на главное окно. Этот метод, в частности, отменит представление вторичного окна на панели задач.
Вот фрагмент кода контейнера, который превратит окно, в котором выведен, в модальное:
class Secondary(tkinter.ttk.Frame): def __init__(self, master=None): . . . . . self.master.transient(parent) self.grab_set()
В качестве примера давайте модифицируем приложение из предыдущего шага таким образом, чтобы выводимое им вторичное окно стало модальным. Текст ниже показывает исправленный код класса контейнера вторичного окна.
import tkinter import tkinter.ttk # Объявляем класс контейнера для вторичного окна class Secondary(tkinter.ttk.Frame): # Конструктор этого класса поддерживает дополнительный параметр # parent, с которым передается ссылка на главное окно. Она # понадобится нам, чтобы вывести занесенное значение в главном окне def __init__(self, master=None, parent=None): super().__init__(master) # Сохраним ссылку на главное окно в атрибуте self.parent = parent self.pack() self.create_widgets() self.master.title("Вторичное окно") self.master.transient(parent) self.grab_set() def create_widgets(self): self.varValue = tkinter.StringVar() self.varValue.set("Значение") entValue = tkinter.ttk.Entry(self, textvariable=self.varValue) entValue.pack() btnOK = tkinter.ttk.Button(self, text="OK", command=self.ok) btnOK.pack(side="left") btnCancel = tkinter.ttk.Button(self, text="Отмена", command=self.master.destroy) btnCancel.pack(side="right") def ok(self): self.parent.lblValue["text"] = self.varValue.get() self.master.destroy() def show_value(self): self.parent.lblValue["text"] = self.varValue.get() class Application(tkinter.ttk.Frame): def __init__(self, master=None): super().__init__(master) self.pack() self.create_widgets() self.master.title("Модальное вторичное окно") self.master.resizable(False, False) def create_widgets(self): btnShowWindow = tkinter.ttk.Button(self, text="Вывести окно", command=self.show_window) btnShowWindow.pack() # Опция width компонента Label задает ширину надписи # в символах текста self.lblValue = tkinter.ttk.Label(self, text="", width=50) self.lblValue.pack() def show_window(self): # Выводим вторичное окно, не забыв указать в параметре parent # конструктора ссыпку на главное окно Secondary(master=tkinter.Toplevel(), parent=self) root = tkinter.Tk() app = Application(master=root) root.mainloop()
Архив с файлом можно взять здесь.
Результат работы приложения приведен на рисунке 1.
Рис.1. Результат работы приложения
На следующем шаге мы рассмотрим управление жизненным циклом приложения .
Окна в tkinter
В этом уроке рассмотрим основные настройки окон, в которых располагаются виджеты. Обычные окна в Tkinter порождаются не только от класса Tk , но и Toplevel . От Tk принято создавать главное окно. Если создается многооконное приложение, то остальные окна создаются от Toplevel. Методы обоих классов схожи.
Размер и положение окна
По умолчанию окно приложения появляется в верхнем левом углу экрана. Его размер (ширина и высота) определяется совокупностью размеров расположенных в нем виджетов. В случае если окно пустое, то tkinter устанавливает его размер в 200 на 200 пикселей.
С помощью метода geometry можно изменить как размер окна, так и его положение. Метод принимает строку определенного формата.
from tkinter import * root = Tk() root.geometry('600x400+200+100') root.mainloop()
Первые два числа в строке-аргументе geometry задают ширину и высоту окна. Вторая пара чисел обозначает смещение на экране по осям x и y . В примере окно размерностью 600 на 400 будет смещено от верхней левой точки экрана на 200 пикселей вправо и на 100 пикселей вниз.
Если перед обоими смещениями вместо плюса указывается минус, то расчет происходит от нижних правых углов экрана и окна. Так выражение root.geometry(‘600×400-0-0’) заставит окно появиться в нижнем правом углу.
В аргументе метода geometry можно не указывать либо размер, либо смещение. Например, чтобы сместить окно, но не менять его размер, следует написать root.geometry(‘+200+100’) .
Бывает удобно, чтобы окно появлялось в центре экрана. Методы winfo_screenwidth и winfo_screenheight возвращают количество пикселей экрана, на котором появляется окно. Рассмотрим, как поместить окно в центр, если размер окна известен:
from tkinter import * root = Tk() w = root.winfo_screenwidth() h = root.winfo_screenheight() w = w // 2 # середина экрана h = h // 2 w = w - 200 # смещение от середины h = h - 200 root.geometry(f'400x400++') root.mainloop()
Здесь мы вычитаем половину ширины и высоты окна (по 200 пикселей). Иначе в центре экрана окажется верхний левый угол окна, а не его середина.
Если размер окна неизвестен, то его можно получить с помощью того же метода geometry , но без аргументов. В этом случае метод возвращает строку, содержащую сведения о размерах и смещении, из которой можно извлечь ширину и высоту окна.
from tkinter import * root = Tk() Button(text="Button", width=20).pack() Label(text="Label", width=20, height=3).pack() Button(text="Button", width=20).pack() root.update_idletasks() s = root.geometry() s = s.split('+') s = s[0].split('x') width_root = int(s[0]) height_root = int(s[1]) w = root.winfo_screenwidth() h = root.winfo_screenheight() w = w // 2 h = h // 2 w = w - width_root // 2 h = h - height_root // 2 root.geometry('+<>+<>'.format(w, h)) root.mainloop()
Метод update_idletasks позволяет перезагрузить данные об окне после размещения на нем виджетов. Иначе geometry вернет строку, где ширина и высота равняются по одному пикселю. Видимо таковы параметры на момент запуска приложения.
По умолчанию пользователь может разворачивать окно на весь экран, а также изменять его размер, раздвигая границы. Эти возможности можно отключить с помощью метода resizable . Так root.resizable(False, False) запретит изменение размеров главного окна как по горизонтали, так и вертикали. Развернуть на весь экран его также будет невозможно, при этом соответствующая кнопка разворота исчезает.
Заголовок окна
По умолчанию в заголовке окна находится надпись «tk». Для установки собственного названия используется метод title .
… root.title("Главное окно") …
Если необходимо, заголовок окна можно вообще убрать. В программе ниже второе окно ( Toplevel ) открывается при клике на кнопку, оно не имеет заголовка, так как к нему был применен метод overrideredirect с аргументом True . Через пять секунд данное окно закрывается методом destroy .
from tkinter import * def about(): a = Toplevel() a.geometry('200x150') a['bg'] = 'grey' a.overrideredirect(True) Label(a, text="About this").pack(expand=1) a.after(5000, lambda: a.destroy()) root = Tk() root.title("Главное окно") Button(text="Button", width=20).pack() Label(text="Label", width=20, height=3).pack() Button(text="About", width=20, command=about).pack() root.mainloop()
Практическая работа
Напишите программу, в которой на главном окне находятся холст и кнопка «Добавить фигуру». Кнопка открывает второе окно, включающее четыре поля для ввода координат и две радиокнопки для выбора, рисовать ли на холсте прямоугольник или овал. Здесь же находится кнопка «Нарисовать», при клике на которую соответствующая фигура добавляется на холст, а второе окно закрывается. Проверку корректности ввода в поля можно опустить.
Курс с примерами решений практических работ: pdf-версия
X Скрыть Наверх
Tkinter. Программирование GUI на Python
Как скрыть виджеты после их нажатия и выполнения действия?
На главном окне у меня будут: кнопка, поля ввода и несколько label. Как сделать так, чтобы после нажатия кнопки она исчезала(скрывалась), а затем появлялись остальные объекты.
То есть как авторизация: вводим логин, пароль, нажимаем «войти», всё скрывается и появляются другие компоненты, то есть будто вход выполнен.
Искал в интернете, но нашел только скрыть кнопку по нажатию или скрыть/показать label по нажатию на кнопку.
Можно ли, когда нажму на кнопку, сначала выполниться действие(показ всех остальных элементов), а потом все компоненты авторизации исчезнут?(или наоборот, сначала все исчезает, а потом появляются компоненты). То есть можно ли как-то скрыть все компоненты по 1 нажатию кнопки и как это реализовать или есть какой-то другой способ?
Функция hide_me скрывает кнопку через bind(), если кнопка не привязана к координатам, а так ничего не происходит.
Попробовал через lambda сделать, чтобы выполнить 2 функции сразу, но появляется ошибка.
btn=Button(text=»Авторизация», command=lambda:(AUTO(),hide_me()))
TypeError: hide_me() missing 1 required positional argument: ‘event’
from tkinter import * def hide_me(event): event.widget.pack_forget() root = Tk() root.title("Авторизация") root.geometry("500x250") root.resizable(False, False) l = "admin" p = "password" LOGIN = "Введите логин:" LOGIN = Label(text=LOGIN, justify=LEFT) LOGIN.place(x=0, y=0) lineLOGIN = Entry() lineLOGIN.pack() lineLOGIN.place(x=80, y=30, anchor="c") PASS = "Введите пароль:" PASS = Label(text=PASS, justify=LEFT) PASS.place(x=0, y=60) linePASS = Entry() linePASS.pack() linePASS.place(x=80, y=90, anchor="c") def AUTO(): if lineLOGIN.get() == l and linePASS.get() == p: linePA = Entry() linePA.pack() linePA.place(x=200, y=70, anchor="c") else: print("Неверный логин/пароль") btn=Button(text="Авторизация", command=lambda:(AUTO(),hide_me())) #btn.bind('', hide_me) btn.pack() btn.place(x=60, y=120, anchor="c") root.mainloop()
- Вопрос задан более двух лет назад
- 727 просмотров
Спрятать окно tkinter
Проблема следующая. Пользуюсь модулем messagebox, который, помимо окна диалога, создает пустое окно root. Чтобы спрятать это окно, на форумах советуют использовать root.withdraw(). Однако мой скрипт создает много разнообразных окон tkinter, и после того, как я что-нибудь нажал в диалоге messagebox, tkinter не может создать новое окно (терминал просто висит) — видимо, ждет, когда я закрою пустое окно root (оно, напомню, спрятано, т.е. мне остается только Ctrl+C). root_destroy() в конце таких процедур как ниже не помогает. Если же withdraw убрать, tkinter начинает плодить сущности: 1, 2 — некрасиво. Как можно решить этот вопрос?
from tkinter import * from tkinter import filedialog from tkinter import messagebox # Диалог "Вы уверены. " def ok_cancel(message): root=Tk() # Иначе будет показываться лишнее пустое окно #root.withdraw() if messagebox.askokcancel('Подтвердите:',message): return True; else: return False #root.destroy()
Deleted
01.04.13 06:05:21 MSK
Никак. В Tk обязательно есть root-окно. Цикл wm (основной цикл для Windows Manager’a Tk) обязательно должен содержать рутовое окно (иначе просто не к чему привязать цикл). Ну, и для Tk использование диалоговых окон вне цикла — просто не возможно. Дело в том, что исходя из архитектуры Tk, элемент message box — это просто дочернее окно (следовательно, оно должно быть привязано к root’овому), у которого заранее заданы определенные параметры.
Я такую проблему обходил просто — сделал класс, который описывал нужный мне диалог и создавал/убивал окно по мере надобности (каждый раз это было просто заново создаваемое root’овое окно). Для Tk это более правильный подход, нежели скрывать корневое окно, а потом плодить мессадж-боксы.
silver-bullet-bfg ★★
( 01.04.13 08:05:07 MSK )
Второй путь: забить на Tk и юзать Gtk 🙂
Novator ★★★★★
( 01.04.13 10:34:18 MSK )
Ответ на: комментарий от Novator 01.04.13 10:34:18 MSK
А смысл? Для маленьких скриптов — tk самое оно. Тем более, что Tkinter — гвидоугодный.
silver-bullet-bfg ★★
( 01.04.13 13:03:24 MSK )
Ответ на: комментарий от silver-bullet-bfg 01.04.13 08:05:07 MSK
Пример класса можете привести?
Deleted
( 01.04.13 13:28:31 MSK )
Ответ на: комментарий от silver-bullet-bfg 01.04.13 08:05:07 MSK
Для Tk это более правильный подход, нежели скрывать корневое окно, а потом плодить мессадж-боксы.
Т.е. предлагаете написать свой messagebox?
Deleted
( 01.04.13 13:30:22 MSK )
не знаю как оно на питоне, но в tcl так:
wm withdraw .
Bad_ptr ★★★★★
( 01.04.13 13:32:36 MSK )
Bad_ptr ★★★★★
( 01.04.13 13:34:29 MSK )
Ответ на: комментарий от silver-bullet-bfg 01.04.13 13:03:24 MSK
А смысл? Для маленьких скриптов — tk самое оно.
Gtk в любом линуксе есть всегда. А вот tk не всегда.
В виндовс Gtk (без туфты) занимает 14Мб в распакованном виде. В архиве того меньше. Сколько tk в винде занимает — не знаю.
А смысл в том, что Gtk выглядит поприятнее, более популярно и лучше документировано. Еще в том, что описанной проблемы можно избежать без выкрутасов.
Ни в коем случае не уговариваю. Решать топикстартеру.
Novator ★★★★★
( 01.04.13 13:59:36 MSK )
Ответ на: комментарий от Novator 01.04.13 13:59:36 MSK
Согласен, что GTK-диалоги приятнее, но очень обломно изучать Tkinter ради простого скрипта, и в итоге из-за маленькой проблемы разучивать что-то другое.
Deleted
( 01.04.13 14:38:04 MSK )
Ответ на: комментарий от Deleted 01.04.13 13:30:22 MSK
Ну да, что тут сложного? Сделаете за 3 минуты. Python очень плохо помню, но что-то вроде:
class WmDialog def __init__(self): self.root=Tk() self.bntOk=ttk.Button (root, text="Hello, world", ).grid() def cmdOk(self): return "ok" def liveWnd (self,isLive): self.root.mainloop() def deadWnd (self): self.root.destroy()
silver-bullet-bfg ★★
( 01.04.13 14:53:51 MSK )
Ответ на: комментарий от silver-bullet-bfg 01.04.13 14:53:51 MSK
* self.bntOk=ttk.Button (root, text=«Hello, world», command=self.cmdOk).grid()
silver-bullet-bfg ★★
( 01.04.13 14:55:04 MSK )
Ответ на: комментарий от Deleted 01.04.13 14:38:04 MSK
Tkinter гвидоугоден зато ^_^. На нем можно не только для скриптов гуй рисовать.
silver-bullet-bfg ★★
( 01.04.13 14:55:51 MSK )
Ответ на: комментарий от silver-bullet-bfg 01.04.13 14:55:04 MSK
Я Питон недавно начал изучать. Можете объяснить, как применить этот класс, чтобы не было пустых окон?
Deleted
( 02.04.13 00:40:42 MSK )
Ответ на: комментарий от Novator 01.04.13 10:34:18 MSK
А есть какой-нибудь простой конструктор типа zenity, вместо того, чтобы кучу непонятных функций писать?
Deleted
( 02.04.13 00:53:31 MSK )
Ответ на: комментарий от Deleted 02.04.13 00:40:42 MSK
Там все просто, вот смотри какая идея:
from Tkinter import * class WmDialog: def __init__(self): self.root=Tk() self.bntOk=Button (self.root, text="Kill Dialog",command=self.root.destroy).grid() self.bntCancel=Button (self.root, text="Exit from Script", command=exit).grid() def liveWnd (self): self.root.mainloop() dlg=WmDialog() dlg.liveWnd() print "I am destroy 1-st window" dlg=WmDialog() dlg.liveWnd() print "I am destroy 2-st window" print "Exit"
Что тут происходит: ты создаешь класс с именем WmDialog (как раз аналог диалог-бокса, я его настраивать не стал — сам сделаешь, тут не сложно), который позволяет либо продолжить выполнение скрипта дальше (если нажмешь bntOk), либо вырубит скрипт (если нажмешь btnCancel). Когда ты нажимаешь bntOk вызывается метод destroy для окна dlg.root, которое и является корневым для цикла Tk(). После вывода первой надписи («I am destroy 1-st window»), мы снова создаем объект-окно. Ну и так далее :). Дальше сам придумаешь, я думаю.
silver-bullet-bfg ★★
( 02.04.13 09:26:40 MSK )
Ответ на: комментарий от silver-bullet-bfg 01.04.13 14:53:51 MSK
Какими местом для опроса диалога Вы хотите присобачить класс О_О?
from Tkinter import * from tkMessageBox import * def dialog(f, *L, **D): root=Tk(); root.withdraw() try: return f(*L, **D) finally: root.destroy() print dialog( askokcancel, '111', '222') print dialog( askokcancel, '333', '444')
dialog подходит для вызова любого dialog-а. Для питона3 ТС думаю сможет адаптировать сам.
ЗЫ: ТС destroy вызывал ПОСЛЕ return, ясно что рут не закрывался;-) Ну и конструкции вида
if True: return True else: return False
доставляют;-)
AIv ★★★★★
( 02.04.13 09:27:11 MSK )
Ответ на: комментарий от Novator 01.04.13 13:59:36 MSK
В винде Tk будет ставится вместе в Python (а вот Gtk придется отдельно доставлять), да и лучше документации чем по Tk в принципе не видел. Просто надо смотреть не только Tkinter доки, но и Tk чистого доки.
silver-bullet-bfg ★★
( 02.04.13 09:30:47 MSK )
Ответ на: комментарий от AIv 02.04.13 09:27:11 MSK
Ваше решение красивее:) Эх. Привык я уже все делать через ООП-извращения 🙁
silver-bullet-bfg ★★
( 02.04.13 09:33:26 MSK )
Ответ на: комментарий от silver-bullet-bfg 02.04.13 09:26:40 MSK
И даже если делать диалог ручками — накой там класс то? По дизайну это ф-я, вызвал — она все нарисовала — висит ждет решения юзера — закрылась и вернула результат. Классы тут актуальны либо когда есть несколько вызовов для работы с одними и теми же данными, либо когда нужно сделать какой то полуфабрикат (сложный виджет) для повторного испольщования.
AIv ★★★★★
( 02.04.13 09:34:09 MSK )
Ответ на: комментарий от silver-bullet-bfg 02.04.13 09:33:26 MSK
Дык переходите на ФП! Даже для гуйни оно иногда бывает весьма полезно;-)
AIv ★★★★★
( 02.04.13 09:35:02 MSK )
Ответ на: комментарий от AIv 02.04.13 09:35:02 MSK
Вот и хочу на ФП перейти, только пока не получается. А вы случайно не подскажите, какой ФП язык наиболее хорошо работает с гуйней? Сейчас как раз пишу «домашний» проект на C#+Gtk, но честно — уже воротит от этой дряни (C#/ООП).
silver-bullet-bfg ★★
( 02.04.13 10:11:52 MSK )
Ответ на: комментарий от AIv 02.04.13 09:34:09 MSK
Ну по привычке я бы загнал вообще весь скрипт в объект, сделал бы не только диалоги как классы, но и саму программу как класс, который работает с объектами других классов..
silver-bullet-bfg ★★
( 02.04.13 10:13:17 MSK )
Ответ на: комментарий от silver-bullet-bfg 02.04.13 10:11:52 MSK
Ну я пишу на питоне, ФП там есть, и в гуйне оно вполне актуально