Ryoga
Фев. 1, 2014 18:07:45
Добрый день! Сразу скажу, что я новичок и с Тkinter практически дела не имел.
Ситуация - имеется программа, сравнительно объемная и со сложной логикой, несколько модулей, множество функций (это важно). Периодически программа запрашивает данные у пользователя в интерактивном режиме через консоль, получив, обрабатывает и продолжает логику дальше.
Необходимо перевести ее на графический интерфейс с простейшей структурой - т.е. некое окошко, в нем несколько Entry для ввода данных и Text для вывода. Т.е. задача, вроде бы, тривиальная - переопределить пользовательскую функцию, отвечающую за ввод, чтобы данные эти она получала из Entry, а соответствующую функцию, отвечающую за вывод, чтобы выводила в Text. Предварительно создав это все, разумеется (еще проще было бы сделать через модальные диалоги, но тогда оно не сильно отличалось бы от консольной версии)
Тем не менее, уперся в очевидную проблему - после запуска mainloop() последний прекращает свою работу только с закрытием всего интерфейса. Т.е. реализовать схему <основная программа>-<вызов фунции ввода/вывода>-<возврат в основную программу> не представляется возможным. Как вошли в цикл обработки графического интерфейса, так в нем и остались, максимум - можем вызвать отдельные подпрограммы обработки событий, не более того.
Сделать из основного цикла программы “сопрограмму”, возвращающую управление в графический интерфейс по yield, невозможно - как уже говорил, там не линейная программа, а множество модулей/функций. Сделать два параллельных потока - во-первых, муторно слишком, во-вторых нерационально с точки зрения ресурсов, т.к. когда вычисления происходят, интерфейс не нужен, и наоборот.
Вот и задумался, а нельзя ли как-нибудь этот самый mainloop() приостановить? т.е. при необходимости использования интерфейса его запускать, а после получения и обработки данных через какой-нибудь Event (нажатие кнопки “продолжить”, например) возвращать управление в основную программу?
Если нет, может, кто подскажет альтернативное решение проблемы? Буду очень благодарен.
Ryoga
Фев. 3, 2014 00:35:15
В общем, решение нашел. Как поставить Tk.mainloop() “на паузу”, к сожалению, не узнал, но гугль подкинул идейку вообще его не запускать, а воспользоваться Tk.update() в условном цикле. Проверил - работает, не только интерфейс перерисовывает, но и события отлавливаются, так что написание функций ввода-вывода становится делом очевидным.
4kpt_II
Фев. 3, 2014 15:24:51
Иногда придется еще использовать .update_idletasks().
P.S. Существует еще множество решений Вашей задачи. Я бы использовал другое. Проще не отображать базовое окно, а отображать Toplevel в нужный момент (для запроса данных, например). После получения данных можно было бы закрывать Toplevel и продолжать дальнейшее выполнение программы. Но это мое личное мнение
![](/static/djangobb_forum/img/smilies/smile.png)
Будут вопросы - пишите…
Ryoga
Фев. 4, 2014 11:52:10
Проще не отображать базовое окно, а отображать Toplevel в нужный момент (для запроса данных, например). После получения данных можно было бы закрывать Toplevel и продолжать дальнейшее выполнение программы. Но это мое личное мнение
Но тогда же придется каждый раз перерисовывать все виджеты на Toplevel, а также запоминать перед закрытием и восстанавливать информацию в них (например, в Text хранится лог событий). Не говоря уж о том, что окошко постоянно будет “мигать”. Чисто эстетически постоянное приложения красивее.
Иногда придется еще использовать .update_idletasks().
Насколько я понял из описания:
update()
Processes all pending events, calls event callbacks, completes any pending geometry management, redraws widgets as necessary, and calls all pending idle tasks. This method should be used with care, since it may lead to really nasty race conditions if called from the wrong place (from within an event callback, for example, or from a function that can in any way be called from an event callback, etc.). When in doubt, use update_idletasks instead.
основные проблемы при использовании метода update() возникают при вызове его тем или иным способом из обработчиков событий (подозреваю рекурсию в результате). В моей же ситуации он не будет вызываться нигде, кроме как из основной программы.
К слову, при замене update() на update_idletasks() в нижеприведенном случае:
# -*- coding: utf-8 -*-
import Tkinter
from Tkinter import *
def end():
root.flag = False
root.destroy()
root = Tk()
root.protocol('WM_DELETE_WINDOW', end)
root.flag = True
textbox = Text(root, font='Verdana 14',wrap=WORD, width=50, height=10)
textbox.pack(side='left')
b_yes = Button(root, text = u'Проверка',
command=lambda: textbox.insert(END, u'Работает! '))
b_yes.pack()
while root.flag:
root.update()
последний не только не обрабатывает события, но даже не перерисовывает виджеты, хотя нужда в том явно есть - их же вообще еще никто не отобразил. Так и висит белое окно Tk
4kpt_II
Фев. 4, 2014 12:05:33
Этот метод не для этого…
Я не писал, что они взаимозаменяемые.
Вы поставили совершенно другую задачу. Ни о каком логе речь не шла.
Ryoga
Т.е. реализовать схему <основная программа>-<вызов фунции ввода/вывода>-<возврат в основную программу> не представляется возможным. Как вошли в цикл обработки графического интерфейса, так в нем и остались, максимум - можем вызвать отдельные подпрограммы обработки событий, не более того.Сделать из основного цикла программы “сопрограмму”, возвращающую управление в графический интерфейс по yield, невозможно - как уже говорил, там не линейная программа, а множество модулей/функций. Сделать два параллельных потока - во-первых, муторно слишком, во-вторых нерационально с точки зрения ресурсов, т.к. когда вычисления происходят, интерфейс не нужен, и наоборот.Вот и задумался, а нельзя ли как-нибудь этот самый mainloop() приостановить? т.е. при необходимости использования интерфейса его запускать, а после получения и обработки данных через какой-нибудь Event (нажатие кнопки “продолжить”, например) возвращать управление в основную программу? Если нет, может, кто подскажет альтернативное решение проблемы? Буду очень благодарен.
Где тут речь про лог?
Вы поставили одну задачу, а решили другую
![](/static/djangobb_forum/img/smilies/smile.png)
Неспортивно
![](/static/djangobb_forum/img/smilies/smile.png)
Задача поставлена по другому:
1. Есть основной цикл обработки событий (там проводятся все рассчеты и т.п.)
2. После определенных рассчетов необходимо вывести их результат на экран в виде окон.
3. Пользователь может корректировать результат или вносить поправки и рассчет продолжается.
Ryoga
Фев. 4, 2014 16:36:50
Ну это да, как-то упустил из виду
![](/static/djangobb_forum/img/smilies/smile.png)
В Text сохраняется история ввода-вывода,а в Label'ах промежуточные результаты отображаются, до кучи.
Xyanide
Фев. 13, 2014 09:16:59
Что-то вы путаете. Ничего функция mainloop не тормозит - создали кнопку, забиндили на окошко с Entry, вызвали его нажатием кнопки, ввели данные, вернули с помощью get().
4kpt_II
Фев. 13, 2014 10:11:28
Вы не поняли вопроса. Имелось ввиду: "можно ли затормозить работу mainloop".