diff --git a/cura/Utils/Threading.py b/cura/Utils/Threading.py new file mode 100644 index 0000000000..3cd6200513 --- /dev/null +++ b/cura/Utils/Threading.py @@ -0,0 +1,34 @@ +import threading + +from cura.CuraApplication import CuraApplication + + +# +# 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 diff --git a/plugins/3MFReader/ThreeMFWorkspaceReader.py b/plugins/3MFReader/ThreeMFWorkspaceReader.py index 633142187c..212df59294 100755 --- a/plugins/3MFReader/ThreeMFWorkspaceReader.py +++ b/plugins/3MFReader/ThreeMFWorkspaceReader.py @@ -4,7 +4,6 @@ from configparser import ConfigParser import zipfile import os -import threading import xml.etree.ElementTree as ET @@ -27,43 +26,13 @@ from cura.Settings.ExtruderStack import ExtruderStack from cura.Settings.GlobalStack import GlobalStack from cura.Settings.CuraContainerStack import _ContainerIndexes from cura.CuraApplication import CuraApplication +from cura.Utils.Threading import call_on_qt_thread from .WorkspaceDialog import WorkspaceDialog 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 - - class ContainerInfo: def __init__(self, file_name: str, serialized: str, parser: ConfigParser): self.file_name = file_name diff --git a/plugins/3MFWriter/ThreeMFWorkspaceWriter.py b/plugins/3MFWriter/ThreeMFWorkspaceWriter.py index 3f5e69317e..e948f62337 100644 --- a/plugins/3MFWriter/ThreeMFWorkspaceWriter.py +++ b/plugins/3MFWriter/ThreeMFWorkspaceWriter.py @@ -6,16 +6,18 @@ from io import StringIO import zipfile from UM.Application import Application -from UM.Logger import Logger from UM.Preferences import Preferences from UM.Settings.ContainerRegistry import ContainerRegistry from UM.Workspace.WorkspaceWriter import WorkspaceWriter +from cura.Utils.Threading import call_on_qt_thread + class ThreeMFWorkspaceWriter(WorkspaceWriter): def __init__(self): super().__init__() + @call_on_qt_thread def write(self, stream, nodes, mode=WorkspaceWriter.OutputMode.BinaryMode): application = Application.getInstance() machine_manager = application.getMachineManager()