get rid of thread killing

Switching to a cooperative model.  This has the downside that a long
patch computation will cause the simulate window to take longer to
update, but in practice the longest this will be is a couple of
seconds (for a very complicated autofill region).
pull/10/head
Lex Neva 2017-12-30 21:15:34 -05:00
rodzic 7152caa14d
commit cb6df39773
1 zmienionych plików z 17 dodań i 41 usunięć

Wyświetl plik

@ -6,7 +6,7 @@ import sys
import json import json
import traceback import traceback
import time import time
from threading import Thread from threading import Thread, Event
from copy import copy from copy import copy
from cStringIO import StringIO from cStringIO import StringIO
import wx import wx
@ -18,40 +18,6 @@ from functools import partial
from itertools import groupby from itertools import groupby
from embroider_simulate import EmbroiderySimulator from embroider_simulate import EmbroiderySimulator
class KillableThread(Thread):
"""A subclass of threading.Thread, with a kill() method."""
def __init__(self, *args, **keywords):
Thread.__init__(self, *args, **keywords)
self.killed = False
def start(self):
"""Start the thread."""
self.__run_backup = self.run
self.run = self.__run # Force the Thread to install our trace.
Thread.start(self)
def __run(self):
"""Hacked run function, which installs the trace."""
sys.settrace(self.globaltrace)
self.__run_backup()
self.run = self.__run_backup
def globaltrace(self, frame, why, arg):
if why == 'call':
return self.localtrace
else:
return None
def localtrace(self, frame, why, arg):
if self.killed:
if why == 'line':
raise SystemExit()
return self.localtrace
def kill(self):
self.killed = True
def presets_path(): def presets_path():
try: try:
import appdirs import appdirs
@ -365,7 +331,7 @@ class SettingsFrame(wx.Frame):
self.simulate_window = None self.simulate_window = None
self.simulate_thread = None self.simulate_thread = None
self.thread_num = 0 self.simulate_refresh_needed = Event()
self.presets_box = wx.StaticBox(self, wx.ID_ANY, label="Presets") self.presets_box = wx.StaticBox(self, wx.ID_ANY, label="Presets")
@ -402,12 +368,18 @@ class SettingsFrame(wx.Frame):
self.simulate_window.stop() self.simulate_window.stop()
self.simulate_window.clear() self.simulate_window.clear()
if self.simulate_thread and self.simulate_thread.is_alive(): if not self.simulate_thread or not self.simulate_thread.is_alive():
self.simulate_thread.kill() self.simulate_thread = Thread(target=self.simulate_worker)
self.simulate_thread.daemon = True
self.simulate_thread.start()
self.simulate_thread = KillableThread(target=self.update_patches, name="Simulate%d" % self.thread_num) self.simulate_refresh_needed.set()
self.simulate_thread.start()
self.thread_num += 1 def simulate_worker(self):
while True:
self.simulate_refresh_needed.wait()
self.simulate_refresh_needed.clear()
self.update_patches()
def update_patches(self): def update_patches(self):
patches = self.generate_patches() patches = self.generate_patches()
@ -448,6 +420,10 @@ class SettingsFrame(wx.Frame):
try: try:
if tab.enabled() and not tab.is_dependent_tab(): if tab.enabled() and not tab.is_dependent_tab():
for node in tab.nodes: for node in tab.nodes:
if self.simulate_refresh_needed.is_set():
# cancel, we need to start over
return []
# Making a copy of the embroidery element is an easy # Making a copy of the embroidery element is an easy
# way to drop the cache in the @cache decorators used # way to drop the cache in the @cache decorators used
# for many params in embroider.py. # for many params in embroider.py.