Add call_on_qt_thread to fix project loading crashing on rendering

CURA-4839

See comments...
This commit is contained in:
Lipu Fei 2018-01-22 14:59:04 +01:00
parent 2eef282b40
commit 3fb9877a30

View file

@ -31,10 +31,42 @@ import zipfile
import io import io
import configparser import configparser
import os import os
import threading
i18n_catalog = i18nCatalog("cura") i18n_catalog = i18nCatalog("cura")
#
# HACK:
#
# In project loading, when override the existing machine is selected, the stacks and containers that are correctly
# active in the system will be overridden at runtime. Because the project loading is done in a different thread than
# the Qt thread, something else can kick in the middle of the process. One of them is the rendering. It will access
# the current stacks and container, which have not completely been updated yet, so Cura will crash in this case.
#
# This "@call_on_qt_thread" decorator makes sure that a function will always be called on the Qt thread (blocking).
# It is applied to the read() function of project loading so it can be guaranteed that only after the project loading
# process is completely done, everything else that needs to occupy the QT thread will be executed.
#
class InterCallObject:
def __init__(self):
self.finish_event = threading.Event()
self.result = None
def call_on_qt_thread(func):
def _call_on_qt_thread_wrapper(*args, **kwargs):
def _handle_call(ico, *args, **kwargs):
ico.result = func(*args, **kwargs)
ico.finish_event.set()
inter_call_object = InterCallObject()
new_args = tuple([inter_call_object] + list(args)[:])
CuraApplication.getInstance().callLater(_handle_call, *new_args, **kwargs)
inter_call_object.finish_event.wait()
return inter_call_object.result
return _call_on_qt_thread_wrapper
## Base implementation for reading 3MF workspace files. ## Base implementation for reading 3MF workspace files.
class ThreeMFWorkspaceReader(WorkspaceReader): class ThreeMFWorkspaceReader(WorkspaceReader):
def __init__(self): def __init__(self):
@ -401,6 +433,7 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
# containing global.cfg / extruder.cfg # containing global.cfg / extruder.cfg
# #
# \param file_name # \param file_name
@call_on_qt_thread
def read(self, file_name): def read(self, file_name):
archive = zipfile.ZipFile(file_name, "r") archive = zipfile.ZipFile(file_name, "r")