Threading.py 1.8 KB

1234567891011121314151617181920212223242526272829303132333435363738394041
  1. import functools
  2. import threading
  3. from cura.CuraApplication import CuraApplication
  4. #
  5. # HACK:
  6. #
  7. # In project loading, when override the existing machine is selected, the stacks and containers that are currently
  8. # active in the system will be overridden at runtime. Because the project loading is done in a different thread than
  9. # the Qt thread, something else can kick in the middle of the process. One of them is the rendering. It will access
  10. # the current stacks and container, which have not completely been updated yet, so Cura will crash in this case.
  11. #
  12. # This "@call_on_qt_thread" decorator makes sure that a function will always be called on the Qt thread (blocking).
  13. # It is applied to the read() function of project loading so it can be guaranteed that only after the project loading
  14. # process is completely done, everything else that needs to occupy the QT thread will be executed.
  15. #
  16. class InterCallObject:
  17. def __init__(self):
  18. self.finish_event = threading.Event()
  19. self.result = None
  20. def call_on_qt_thread(func):
  21. @functools.wraps(func)
  22. def _call_on_qt_thread_wrapper(*args, **kwargs):
  23. # If the current thread is the main thread, which is the Qt thread, directly call the function.
  24. current_thread = threading.current_thread()
  25. if isinstance(current_thread, threading._MainThread):
  26. return func(*args, **kwargs)
  27. def _handle_call(ico, *args, **kwargs):
  28. ico.result = func(*args, **kwargs)
  29. ico.finish_event.set()
  30. inter_call_object = InterCallObject()
  31. new_args = tuple([inter_call_object] + list(args)[:])
  32. CuraApplication.getInstance().callLater(_handle_call, *new_args, **kwargs)
  33. inter_call_object.finish_event.wait()
  34. return inter_call_object.result
  35. return _call_on_qt_thread_wrapper