Форум сайта python.su
Я сам не эксперт, но вопрос, который задам, вероятно, требует отличных знаний tkinter, поэтому публикую в форуме экспертов. Есть GUI, написанный на tkinter (проверял нижеприведенный код на Python 3.4 и tk 8.5 и 8.6). Задача - многократное использование Toplevel и сидящих на нем виджетов. Образец:
#!/usr/bin/python3 import tkinter as tk class Top: def __init__(self): self.widget = tk.Toplevel(h_widgets.root()) self.widget.title('Today is the day') self.widget.protocol("WM_DELETE_WINDOW",self.close) self.tk_trigger = tk.BooleanVar() def close(self,*args): self.widget.withdraw() self.tk_trigger.set(True) def show(self): self.widget.deiconify() self.tk_trigger = tk.BooleanVar() self.widget.wait_variable(self.tk_trigger) class Widgets: def __init__(self): self._root = self._top = self._textbox = None def root(self): if not self._root: self._root = tk.Tk() self._root.withdraw() return self._root def start(self): self.root() self._root.withdraw() def end(self): self.root().destroy() self._root.mainloop() def top(self): if not self._top: self._top = Top() return self._top h_widgets = Widgets() if __name__ == '__main__': h_widgets.start() h_widgets.top().show() h_widgets.end()
#!/usr/bin/python3 from sample1 import Widgets h_widgets = Widgets() h_widgets.start() h_widgets.top().show() h_widgets.end()
class TopTrigger: def add(self,obj): self.obj = obj self.obj.tk_trigger = tk.BooleanVar() def on_close(self,*args): self.obj.tk_trigger.set(True) def on_show(self): self.obj.tk_trigger = tk.BooleanVar() self.obj.widget.wait_variable(self.obj.tk_trigger)
Отредактировано vanvanov (Фев. 18, 2017 09:37:07)
Офлайн
Адище…
1. Зачем Вам wait_variable?
2. Вообще не понятно зачем все это. Вы хотите использовать top в другом модуле?
Офлайн
4kpt_IVЧто именно не так?
Адище…
4kpt_IVЯ делаю диалоговое окно, в котором пользователь должен нажать на кнопку. Чтобы дождаться этого, есть методы wait_variable или wait_window (по крайней мере, я знаю только эти). Второй требует destroy. Но я не хочу уничтожать виджет и потом пересоздавать его, а хочу повторно его использовать (например, с помощью withdraw и deiconify). Таким образом, остается первый.
Зачем Вам wait_variable
4kpt_IVА что тут такого? Я написал модуль-обертку для некоторых стандартных виджетов tkinter. Я могу, например, по умолчанию центрировать их по экрану, делать на них фокус и т.п. Большинство из классов требуют Toplevel.
Вы хотите использовать top в другом модуле
Офлайн
vanvanovЯ не сказал, что это плохо, я просто задал вопрос. Т.е. Вы хотите чтобы появлялось диалоговое окно в котором пользователь должен нажать на кнопку? А зачем Вы это событие ждете?
Я делаю диалоговое окно, в котором пользователь должен нажать на кнопку.
Отредактировано 4kpt_IV (Фев. 19, 2017 13:55:38)
Офлайн
4kpt_IV
А как иначе? Посмотрите на этот код:
#!/usr/bin/python3 import tkinter as tk root = tk.Tk() root.withdraw() top = tk.Toplevel(root) txt = tk.Text(top) txt.insert('1.0','Готово!') txt.pack() txt.focus_set() top.wait_window() root.destroy() root.mainloop()
Офлайн
А почему после нажатия кнопки на toplevel нельзя сделать root.destroy() раз Вы ждете от пользователя этого?
Офлайн
4kpt_IV
Я не жду от пользователя root.destroy(). Он делается предпоследним перед mainloop, чтобы пользователю не пришлось вручную уничтожать рутовое окно (оно все время скрыто, а показывается Toplevel).
Вот, посмотрите, например:
#!/usr/bin/python3 import tkinter as tk class Root: def __init__(self): self.widget = tk.Tk() def kill(self): self.widget.destroy() def run(self): self.widget.mainloop() def close(self): self.widget.withdraw() def show(self): self.widget.deiconify() class Top: def __init__(self,parent_obj): self.parent_obj = parent_obj self.widget = tk.Toplevel(self.parent_obj.widget) self.widget.protocol("WM_DELETE_WINDOW",self.close) self.tk_trigger = tk.BooleanVar() # Здесь мы по умолчанию делаем центровку, ну и другие вкусные вещи self.center() def title(self,arg=None): if arg: self.widget.title(arg) def center(self,*args): pass def show(self,*args): self.widget.deiconify() self.widget.wait_variable(self.tk_trigger) def close(self,*args): self.widget.withdraw() self.tk_trigger.set(True) class TextBox: def __init__(self,parent_obj): self.parent_obj = parent_obj self.widget = tk.Text(self.parent_obj.widget) self.widget.pack() self.focus() def clear_text(self,*args): self.widget.delete('1.0',tk.END) def insert(self,arg): if arg: self.widget.insert('1.0',arg) def update(self,arg): self.clear_text() self.insert(arg) def title(self,arg=None,*args): self.parent_obj.title(arg) def focus(self,*args): self.widget.focus_set() def show(self,*args): self.parent_obj.show() def close(self,*args): self.parent_obj.close() class Widgets: def __init__(self): self._root = self._txt = None def root(self): if not self._root: self._root = Root() return self._root def start(self): self.root() self._root.close() def end(self): self.root().kill() self._root.run() def txt(self): if not self._txt: top = Top(parent_obj=self.root()) self._txt = TextBox(parent_obj=top) return self._txt widgets = Widgets() widgets.start() txt = widgets.txt() txt.title('1') txt.update('Здесь был Вася.') txt.show() txt.title('2') txt.update('А потом сюда пришел Петя.') txt.show() widgets.end()
Отредактировано vanvanov (Фев. 19, 2017 14:58:44)
Офлайн
А почему нельзя просто скрыть текстовый виджет?
vanvanov
Я не жду от пользователя root.destroy(). Он делается предпоследним перед mainloop, чтобы пользователю не пришлось вручную уничтожать рутовое окно (оно все время скрыто, а показывается Toplevel).
Офлайн
4kpt_IVПотому что TextBox - это не примитив. Там могут быть кнопки, фреймы, скроллбары, еще много всякой всячины. Если я сделаю pack_forget на фрейме, то Toplevel будет висеть впереди. Поскольку я делаю такие виджеты-конструкторы на основе Toplevel, то и скрывать надо Toplevel, а, как я уже говорил, я знаю только 2 метода для этого, причем один из них не подходит.
А почему нельзя просто скрыть текстовый виджет?
4kpt_IVЗачем? Я же говорю, мне надо не разрушать виджет, а скрывать его (то бишь вместе с привязанным к нему Toplevel).
Вот и сделайте ему внутри какого-нибудь обработчика toplevel метод destroy().
4kpt_IVУ меня иногда бывает так, что фокус теряется (скорее всего, на скрытый root переходит) даже при перезапуске программы. Т.е. при 1-м запуске фокус есть, а при 2-м его может и не быть. Я грешу на DM.
Потому как уход фокуса “в никуда” очень настораживает.
4kpt_IVВы написали, но никак не обосновали. Я бы с удовольствием научился бы писать *правильный* код, но для этого нужны комментарии по существу.
В первой фразе записал, что это Адище
Офлайн
По существу…
Ну ок. Зачем переименовывать существующие методы? Поясните смысловую нагрузку этого?
# class Root: def __init__(self): self.type = 'Root' self.widget = tk.Tk() def run(self): self.widget.mainloop() def show(self): self.widget.deiconify() def close(self): self.widget.withdraw() def destroy(self): self.kill() def kill(self): self.widget.destroy() def update(self): self.widget.update()
if obj_type_str == 'str': obj_type_str = globs['mes'].type_str elif obj_type_str == 'list': obj_type_str = globs['mes'].type_lst elif obj_type_str == 'dict': obj_type_str = globs['mes'].type_dic elif obj_type_str == 'tuple': obj_type_str = globs['mes'].type_tuple elif obj_type_str == 'set' or obj_type_str == 'frozenset': obj_type_str = globs['mes'].type_set elif obj_type_str == 'int': obj_type_str = globs['mes'].type_int elif obj_type_str == 'long': obj_type = globs['mes'].type_long_int elif obj_type_str == 'float': obj_type_str = globs['mes'].type_float elif obj_type_str == 'complex': obj_type_str = globs['mes'].type_complex elif obj_type_str == 'bool': obj_type_str = globs['mes'].type_bool
Офлайн