Как запустить два метода одновременно, если один из них использует объекты Tkinter?
Настройка проблемы: Метод A останавливает метод B по истечении заданного времени.До этого метод A отображает оставшееся время в метке Tkinter.
Проблема: Методы не запускаются одновременно.
Версия Python: 2.7
ОС: Windows 7
Я использую потоки для реализации параллелизма.Я читал, что в Python есть нечто, называемое Global Interpreter Lock, которое заставляет потоки работать последовательно.Я предполагаю, что это вызывает проблему.
Обходной путь должен был бы использовать Процессы.Это невозможно, поскольку объекты Tkinter не могут быть превращены в символьные потоки («pickle»).Я получаю эту ошибку, когда пытаюсь использовать процессы: PicklingError: Невозможно выбрать объект 'tkapp'.
Приведенный ниже пример запуска имитирует реальную программу, которая намного больше.По этой причине он использует шаблон проектирования Model-View-Controller.Я скопировал некоторый код из Тайм-аут при вызове функции .
Вариант использования: Пользователь нажимает кнопку.Это запускает фоновую задачу, которая может занять много времени.Во время выполнения фоновой задачи интерфейс постоянно сообщает пользователю, сколько времени осталось до тех пор, пока программа не отменит фоновую задачу.
Поток, работающий в фоновом режиме, не реализован, поэтому его можно остановить.Но это не то, что мне интересно, так или иначе.
from Tkinter import *
from time import sleep
from threading import Thread, Timer
class Frontend(Tk):
def __init__(self):
Tk.__init__(self)
self.label = Label(self, text = "", font = ("Courier", 12))
self.button = Button(self, text = "Run thread in background.", font = ("Courier", 12))
self.label.grid()
self.button.grid(sticky = "nsew")
class Backend:
def background_task(self):
print "Background task is executing."
sleep(6)
print "Finished."
class Controller:
def __init__(self):
self.INTERRUPT_AFTER = 4
self.done = True
self.backend = Backend()
self.frontend = Frontend()
self.frontend.button.configure(command = self.run_something_in_background)
class Decorator(object):
def __init__(self, instance, time):
self.instance = instance
self.time = time
def exit_after(self):
def outer(fn):
def inner():
timer = Timer(self.time, self.quit_function)
timer.start()
fn()
return timer
return inner
return outer
def quit_function(self):
if not self.instance.done:
self.instance.display_message("Interrupted background task.")
self.instance.set_done(True)
def run_something_in_background(self):
backendThread = Thread(target = self.backend.background_task)
decorator = self.Decorator(self, self.INTERRUPT_AFTER)
countdown = decorator.exit_after()(self.countdown) # exit_after returns the function with which we decorate.
self.set_done(False)
countdown()
backendThread.start()
backendThread.join()
self.set_done(True)
def countdown(self):
seconds = self.INTERRUPT_AFTER
while seconds > 0 and not self.done:
message = "Interrupting background task in {} seconds\nif not finished.".format(str(seconds))
self.display_message(message)
seconds -= 1
sleep(1)
def set_done(self, val):
self.done = val
def display_message(self, message):
self.frontend.label.config(text = message)
self.frontend.update_idletasks()
def run(self):
self.frontend.mainloop()
app = Controller()
app.run()