В моей маленькой программе потенциально длительный процесс. Это не проблема, когда вы делаете это с консоли, но теперь я хочу добавить графический интерфейс. В идеале я хочу использовать Tkinter (a), потому что это просто, и (b), потому что это может быть легче реализовать на разных платформах. Из того, что я прочитал и испытал, (почти) все GUI страдают от одной и той же проблемы. 1 - где основной рабочий процесс опрашивает (например, ожидает получения данных), и 2 - где рабочий процесс выполняет большую работу (например, копирует файлы в цикле for). Моя программа попадает в последнюю.
Мой код имеет "иерархию" классов.
Класс MIGUI обрабатывает графический интерфейс и взаимодействует с интерфейсным классом MediaImporter. Класс MediaImporter является интерфейсом между пользовательским интерфейсом (консоль или GUI) и рабочими классами. Класс Import является долговременным работником. Он не знает, что интерфейс или классы GUI существуют.
Проблема: после нажатия кнопки «Пуск» графический интерфейс блокируется, поэтому я не могу нажать кнопку «Прервать». Как будто я вообще не использую потоки. Я подозреваю, что проблема связана с тем, как я запускаю многопоточность в методе startCallback.
Я также пробовал подход многопоточности всего класса MediaImporter. Смотрите закомментированные строки.
import tkinter as tk
from tkinter import ttk
from tkinter import filedialog
import threading
import time
class MIGUI():
def __init__(self, master):
self.master = master
self.mediaImporter = MediaImporter()
self.startButton = ttk.Button(self.master, text='Start', command=self.startCallback)
self.startButton.pack()
self.abortButton = ttk.Button(self.master, text='Abort', command=self.abortCallback)
self.abortButton.state(['disabled'])
self.abortButton.pack()
def startCallback(self):
print('startCallback')
self.abortButton.state(['!disabled'])
self.startButton.state(['disabled'])
self.abortButton.update() # forcing the update seems unnecessary
self.startButton.update()
#print(self.startButton.state())
#print(self.abortButton.state())
self.x = threading.Thread(target=self.mediaImporter.startImport)
self.x.start()
self.x.join()
#self.mediaImporter.startImport()
self.startButton.state(['!disabled'])
self.abortButton.state(['disabled'])
self.abortButton.update()
self.startButton.update()
#print(self.startButton.state())
#print(self.abortButton.state())
def abortCallback(self):
print('abortCallback')
self.mediaImporter.abortImport()
self.startButton.state(['!disabled'])
self.abortButton.state(['disabled'])
class MediaImporter():
#class MediaImporter(threading.Thread):
""" Interface between user (GUI / console) and worker classes """
def __init__(self):
#threading.Thread.__init__(self)
self.Import = Import()
#other worker classes exist too
def startImport(self):
print('mediaImporter - startImport')
self.Import.start()
def abortImport(self):
print('mediaImporter - abortImport')
self.Import.abort()
class Import():
""" Worker
Does not know anything about other non-worker classes or UI.
"""
def __init__(self):
self._wantAbort = False
def start(self):
print('import - start')
self._wantAbort = False
self.doImport()
def abort(self):
print('import - abort')
self._wantAbort = True
def doImport(self):
print('doImport')
for i in range(0,10):
#actual code has nested for..loops
print(i)
time.sleep(.25)
if self._wantAbort:
print('doImport - abort')
return
def main():
gui = True
console = False
if gui:
root = tk.Tk()
app = MIGUI(root)
root.mainloop()
if console:
#do simple console output without tkinter - threads not necessary
pass
if __name__ == '__main__':
main()