Чтобы исправить эту проблему, можно было бы попробовать вызывать функцию перенаправления в дочернем потоке выполнения, например, изменив функцию launch в примере 10.28, как показано ниже (этот фрагмент взят из сценария pipe—gui2-thread.py, входящего в состав пакета с примерами для книги):
def launch(): import _thread _thread.start_new_thread(redirectedGuiShellCmd, (‘python -u pipe-nongui.py’,))
Но тогда графический интерфейс будет обновляться из дочернего потока выполнения, что, как мы уже знаем, заканчивается плохо. Параллельные попытки обновления графического интерфейса могут нанести ему вред.
Фактически, после внесения предложенных изменений на моем ноутбуке с Windows 7, графический интерфейс зависает сразу же после первого щелчка на кнопке GO!, становясь совершенно неотзывчивым, и его приходится закрывать принудительно. Это происходит до (или, может быть, во время) создания нового окна с текстовым виджетом. Когда этот пример запускался в Windows XP, во время работы над предыдущим изданием книги, он также иногда подвисал при первом щелчке на кнопке GO!, а несколько щелчков на кнопке гарантированно подвешивали его и в этой системе — процесс приходилось останавливать принудительно. Прямое обновление графического интерфейса из дочерних потоков выполнения не является приемлемым решением.
Как вариант, можно было бы попробовать использовать функцию Python select.select (описывается в главе 12) для реализации проверки наличия данных в канале — к сожалению, в настоящее время функция select в Windows работает только с сокетами (в Unix она также работает с каналами и с дескрипторами файлов).
В некоторых контекстах графический интерфейс, запускаемый отдельно, мог бы использовать сигналы для информирования программы командной строки о наступлении момента обмена данными, и наоборот (с помощью модуля signal и функции os.kill, представленных в главе 5). Недостаток такого решения состоит в том, что он требует добавлять обработку сигналов в реализацию программы командной строки.
Альтернативой сокетам в примерах с 10.23 по 10.25 могли бы в определенных ситуациях служить именованные каналы (файлы fifo, представленные в главе 5), но сокеты работают в стандартной версии Python для Windows, а именованные каналы — нет (функция os.mkfifo недоступна в версии Python 3.1 для Windows, хотя она имеется в версии Cygwin Python). Но даже там, где они работают, нам все еще необходимо использовать цикл обработки событий от таймера на основе метода after, чтобы избежать блокирования графического интерфейса.
Также мы могли бы использовать функцию createfilehandler из библиотеки tkinter, чтобы зарегистрировать обработчик, который будет вызываться при появлении данных в канале:
def callback(file, mask):
…чтение данных из файла…
import _tkinter, tkinter
_tkinter.createfilehandler(file, tkinter.READABLE, callback)
Операция регистрации обработчика доступна в виде функции в модуле tkinter и в виде метода экземпляра класса Tk. К сожалению, как уже отмечалось в конце главы 9, эта функция недоступна в Windows и может служить альтернативой только в Unix.
Использованная литература:
Марк Лутц — Программирование на Python, 4-е издание, I том, 2011