#python #python_3x #tkinter
Всем привет! для обновления окна использую root.update() Но если программа делает например запрос к серверу и долго не получает ответ- программа подвисает на время ожидания ответа. Как этого избежать и сделать так, чтобы программа была всегда активна? Спасибо! пример кода: root=tk.Tk() def work (): requests.get(url) time.sleep(5) work() root.mainloop()
Ответы
Ответ 1
Никогда никогда не используйте time.sleep(5) внутри цикла событий -- это замораживает ваше GUI на ~5 секунд. Используйте root.after(5000, some_action), чтобы выполнить код через 5 секунд в Tkinter. Чтобы выполнить работу, которая требует не фиксированное время (такую как отправка/чтение данных из сети, используя requests.get()), следует либо вынести в фоновый поток (threading), либо встроить операции ввода/вывода в сам цикл событий (Widget.tk.createfilehandler()). Примеры кода по ссылкам можно посмотреть: Мультизадачность на Python: выполнить две долгие функции одновременно, не блокируя GUI Конкретно, вот пример для tkinter, который читает вывод из внешней команды (self.proc.stdout.readline()), не блокируя цикл событий: вариант с фоновым потоком вариант без потока с createfilehandler(). Вот полный пример кода, который запускает requests.get() в фоновом потоке и обновляет показ текущего времени каждые 100 миллисекунд, пока запрос не завершится: #!/usr/bin/env python3 import time import tkinter from datetime import datetime from queue import Empty, Queue from threading import Thread import requests def display_result(label, q): try: label['text'] = q.get(block=False) except Empty: # update time at 100ms boundary label.after(round(100 - (1000 * time.time()) % 100), display_result, label, q) label['text'] = str(datetime.now().strftime("%H:%M:%S.%f")[:-3]) def get_result(q): # blocking function q.put('ip=' + requests.get('https://httpbin.org/delay/3').json()['origin']) # get result in a background thread result_queue = Queue() Thread(target=get_result, args=[result_queue], daemon=True).start() # display result root = tkinter.Tk() label = tkinter.Label(font=(None, 100)) label.pack() display_result(label, result_queue) # center window root.eval('tk::PlaceWindow %s center' % root.winfo_pathname(root.winfo_id())) root.mainloop() Чтобы обновить надпись (tkinter.Label) из другого потока, самый безопасный метод это использовать queue.Queue, как показано выше (то есть обращаться к label, только из потока с root.mainloop(), который её создал). Но если показывать прогресс не нужно (надпись прежняя остаётся пока блокирующий запрос не завершится), то в простых случаях можно обратиться и из другого потока напрямую: #!/usr/bin/env python3 import tkinter from threading import Thread import requests def fetch(url, on_response): requests.get(url, hooks=dict(response=on_response)) # blocking function def on_response(response, **unused_kwargs): # NOTE: it is called from another thread label['text'] = response.json()['origin'] # get result in a background thread root = tkinter.Tk() label = tkinter.Label(font=(None, 100), text='no result yet') label.pack() url = 'https://httpbin.org/delay/3' Thread(target=fetch, args=[url, on_response], daemon=True).start() root.mainloop() Современная реализация tkinter модуля в CPython пытается поддерживать обращение к tkinter объектам из других потоков. К примеру, если не включены потоки в Tcl интерпретаторе, то mutex расставляются, чтобы только один поток имел доступ к Tcl интерпретатору одновременно. Если включены потоки (tkinter.Tcl().eval('info exists ::tcl_platform(threaded)') == '1'), то обращения к Tcl из других потоков в очередь автоматически внутри tkinter добавляются и выполняются, когда управление вернётся на поток, где запущен Tcl интерпретатор (see all the gory details). Возможны баги, поэтому при возникновении любых проблем, обычно проще использовать упрощённую модель: все обращения к GUI объектам только в GUI потоке производятся.
Комментариев нет:
Отправить комментарий