Browse Source

Add call_on_qt_thread to fix project loading crashing on rendering

CURA-4839

See comments...
Lipu Fei 7 years ago
parent
commit
3fb9877a30
1 changed files with 33 additions and 0 deletions
  1. 33 0
      plugins/3MFReader/ThreeMFWorkspaceReader.py

+ 33 - 0
plugins/3MFReader/ThreeMFWorkspaceReader.py

@@ -31,10 +31,42 @@ import zipfile
 import io
 import configparser
 import os
+import threading
 
 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.
 class ThreeMFWorkspaceReader(WorkspaceReader):
     def __init__(self):
@@ -401,6 +433,7 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
     #   containing global.cfg / extruder.cfg
     #
     #   \param file_name
+    @call_on_qt_thread
     def read(self, file_name):
         archive = zipfile.ZipFile(file_name, "r")