mirror of
https://github.com/Ultimaker/Cura.git
synced 2025-07-06 22:47:29 -06:00
WIP: Make application initialization and start up more clear
- Create SingleInstance class to handling single instance stuff. - Instead of calling getInstance() everywhere, initialize each object explicitly in order when application starts and getInstance()s do not create instances any more and they merely return the created instances. - Only set initial values in construtor functions __init__(). Move the initialization of context-aware (i.e. things that depend on other things) to separate functions. - Split application creation and initialziation into several steps and them should be called explicitly in the correct order.
This commit is contained in:
parent
c8f73d303e
commit
051dd7a6e9
14 changed files with 521 additions and 471 deletions
|
@ -1,14 +1,16 @@
|
||||||
# Copyright (c) 2018 Ultimaker B.V.
|
# Copyright (c) 2018 Ultimaker B.V.
|
||||||
# Cura is released under the terms of the LGPLv3 or higher.
|
# Cura is released under the terms of the LGPLv3 or higher.
|
||||||
|
import math
|
||||||
|
from typing import List, Optional
|
||||||
|
|
||||||
|
import numpy
|
||||||
|
|
||||||
|
from PyQt5.QtCore import QTimer
|
||||||
|
|
||||||
from cura.Scene.CuraSceneNode import CuraSceneNode
|
|
||||||
from cura.Settings.ExtruderManager import ExtruderManager
|
|
||||||
from UM.Settings.ContainerRegistry import ContainerRegistry
|
|
||||||
from UM.i18n import i18nCatalog
|
from UM.i18n import i18nCatalog
|
||||||
from UM.Scene.Platform import Platform
|
from UM.Scene.Platform import Platform
|
||||||
from UM.Scene.Iterator.BreadthFirstIterator import BreadthFirstIterator
|
from UM.Scene.Iterator.BreadthFirstIterator import BreadthFirstIterator
|
||||||
from UM.Scene.SceneNode import SceneNode
|
from UM.Scene.SceneNode import SceneNode
|
||||||
from UM.Application import Application
|
|
||||||
from UM.Resources import Resources
|
from UM.Resources import Resources
|
||||||
from UM.Mesh.MeshBuilder import MeshBuilder
|
from UM.Mesh.MeshBuilder import MeshBuilder
|
||||||
from UM.Math.Vector import Vector
|
from UM.Math.Vector import Vector
|
||||||
|
@ -18,16 +20,14 @@ from UM.Math.AxisAlignedBox import AxisAlignedBox
|
||||||
from UM.Math.Polygon import Polygon
|
from UM.Math.Polygon import Polygon
|
||||||
from UM.Message import Message
|
from UM.Message import Message
|
||||||
from UM.Signal import Signal
|
from UM.Signal import Signal
|
||||||
from PyQt5.QtCore import QTimer
|
|
||||||
from UM.View.RenderBatch import RenderBatch
|
from UM.View.RenderBatch import RenderBatch
|
||||||
from UM.View.GL.OpenGL import OpenGL
|
from UM.View.GL.OpenGL import OpenGL
|
||||||
|
|
||||||
|
from cura.Scene.CuraSceneNode import CuraSceneNode
|
||||||
|
from cura.Settings.ExtruderManager import ExtruderManager
|
||||||
|
|
||||||
catalog = i18nCatalog("cura")
|
catalog = i18nCatalog("cura")
|
||||||
|
|
||||||
import numpy
|
|
||||||
import math
|
|
||||||
|
|
||||||
from typing import List, Optional
|
|
||||||
|
|
||||||
# Setting for clearance around the prime
|
# Setting for clearance around the prime
|
||||||
PRIME_CLEARANCE = 6.5
|
PRIME_CLEARANCE = 6.5
|
||||||
|
|
||||||
|
@ -36,8 +36,10 @@ PRIME_CLEARANCE = 6.5
|
||||||
class BuildVolume(SceneNode):
|
class BuildVolume(SceneNode):
|
||||||
raftThicknessChanged = Signal()
|
raftThicknessChanged = Signal()
|
||||||
|
|
||||||
def __init__(self, parent = None):
|
def __init__(self, application, parent = None):
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
|
self._application = application
|
||||||
|
self._machine_manager = self._application.getMachineManager()
|
||||||
|
|
||||||
self._volume_outline_color = None
|
self._volume_outline_color = None
|
||||||
self._x_axis_color = None
|
self._x_axis_color = None
|
||||||
|
@ -80,14 +82,14 @@ class BuildVolume(SceneNode):
|
||||||
" with printed models."), title = catalog.i18nc("@info:title", "Build Volume"))
|
" with printed models."), title = catalog.i18nc("@info:title", "Build Volume"))
|
||||||
|
|
||||||
self._global_container_stack = None
|
self._global_container_stack = None
|
||||||
Application.getInstance().globalContainerStackChanged.connect(self._onStackChanged)
|
self._application.globalContainerStackChanged.connect(self._onStackChanged)
|
||||||
self._onStackChanged()
|
self._onStackChanged()
|
||||||
|
|
||||||
self._engine_ready = False
|
self._engine_ready = False
|
||||||
Application.getInstance().engineCreatedSignal.connect(self._onEngineCreated)
|
self._application.engineCreatedSignal.connect(self._onEngineCreated)
|
||||||
|
|
||||||
self._has_errors = False
|
self._has_errors = False
|
||||||
Application.getInstance().getController().getScene().sceneChanged.connect(self._onSceneChanged)
|
self._application.getController().getScene().sceneChanged.connect(self._onSceneChanged)
|
||||||
|
|
||||||
#Objects loaded at the moment. We are connected to the property changed events of these objects.
|
#Objects loaded at the moment. We are connected to the property changed events of these objects.
|
||||||
self._scene_objects = set()
|
self._scene_objects = set()
|
||||||
|
@ -105,14 +107,14 @@ class BuildVolume(SceneNode):
|
||||||
# Must be after setting _build_volume_message, apparently that is used in getMachineManager.
|
# Must be after setting _build_volume_message, apparently that is used in getMachineManager.
|
||||||
# activeQualityChanged is always emitted after setActiveVariant, setActiveMaterial and setActiveQuality.
|
# activeQualityChanged is always emitted after setActiveVariant, setActiveMaterial and setActiveQuality.
|
||||||
# Therefore this works.
|
# Therefore this works.
|
||||||
Application.getInstance().getMachineManager().activeQualityChanged.connect(self._onStackChanged)
|
self._machine_manager.activeQualityChanged.connect(self._onStackChanged)
|
||||||
|
|
||||||
# This should also ways work, and it is semantically more correct,
|
# This should also ways work, and it is semantically more correct,
|
||||||
# but it does not update the disallowed areas after material change
|
# but it does not update the disallowed areas after material change
|
||||||
Application.getInstance().getMachineManager().activeStackChanged.connect(self._onStackChanged)
|
self._machine_manager.activeStackChanged.connect(self._onStackChanged)
|
||||||
|
|
||||||
# Enable and disable extruder
|
# Enable and disable extruder
|
||||||
Application.getInstance().getMachineManager().extruderChanged.connect(self.updateNodeBoundaryCheck)
|
self._machine_manager.extruderChanged.connect(self.updateNodeBoundaryCheck)
|
||||||
|
|
||||||
# list of settings which were updated
|
# list of settings which were updated
|
||||||
self._changed_settings_since_last_rebuild = []
|
self._changed_settings_since_last_rebuild = []
|
||||||
|
@ -122,7 +124,7 @@ class BuildVolume(SceneNode):
|
||||||
self._scene_change_timer.start()
|
self._scene_change_timer.start()
|
||||||
|
|
||||||
def _onSceneChangeTimerFinished(self):
|
def _onSceneChangeTimerFinished(self):
|
||||||
root = Application.getInstance().getController().getScene().getRoot()
|
root = self._application.getController().getScene().getRoot()
|
||||||
new_scene_objects = set(node for node in BreadthFirstIterator(root) if node.callDecoration("isSliceable"))
|
new_scene_objects = set(node for node in BreadthFirstIterator(root) if node.callDecoration("isSliceable"))
|
||||||
if new_scene_objects != self._scene_objects:
|
if new_scene_objects != self._scene_objects:
|
||||||
for node in new_scene_objects - self._scene_objects: #Nodes that were added to the scene.
|
for node in new_scene_objects - self._scene_objects: #Nodes that were added to the scene.
|
||||||
|
@ -181,7 +183,7 @@ class BuildVolume(SceneNode):
|
||||||
if not self._shader:
|
if not self._shader:
|
||||||
self._shader = OpenGL.getInstance().createShaderProgram(Resources.getPath(Resources.Shaders, "default.shader"))
|
self._shader = OpenGL.getInstance().createShaderProgram(Resources.getPath(Resources.Shaders, "default.shader"))
|
||||||
self._grid_shader = OpenGL.getInstance().createShaderProgram(Resources.getPath(Resources.Shaders, "grid.shader"))
|
self._grid_shader = OpenGL.getInstance().createShaderProgram(Resources.getPath(Resources.Shaders, "grid.shader"))
|
||||||
theme = Application.getInstance().getTheme()
|
theme = self._application.getTheme()
|
||||||
self._grid_shader.setUniformValue("u_plateColor", Color(*theme.getColor("buildplate").getRgb()))
|
self._grid_shader.setUniformValue("u_plateColor", Color(*theme.getColor("buildplate").getRgb()))
|
||||||
self._grid_shader.setUniformValue("u_gridColor0", Color(*theme.getColor("buildplate_grid").getRgb()))
|
self._grid_shader.setUniformValue("u_gridColor0", Color(*theme.getColor("buildplate_grid").getRgb()))
|
||||||
self._grid_shader.setUniformValue("u_gridColor1", Color(*theme.getColor("buildplate_grid_minor").getRgb()))
|
self._grid_shader.setUniformValue("u_gridColor1", Color(*theme.getColor("buildplate_grid_minor").getRgb()))
|
||||||
|
@ -201,7 +203,7 @@ class BuildVolume(SceneNode):
|
||||||
## For every sliceable node, update node._outside_buildarea
|
## For every sliceable node, update node._outside_buildarea
|
||||||
#
|
#
|
||||||
def updateNodeBoundaryCheck(self):
|
def updateNodeBoundaryCheck(self):
|
||||||
root = Application.getInstance().getController().getScene().getRoot()
|
root = self._application.getController().getScene().getRoot()
|
||||||
nodes = list(BreadthFirstIterator(root))
|
nodes = list(BreadthFirstIterator(root))
|
||||||
group_nodes = []
|
group_nodes = []
|
||||||
|
|
||||||
|
@ -289,11 +291,11 @@ class BuildVolume(SceneNode):
|
||||||
if not self._width or not self._height or not self._depth:
|
if not self._width or not self._height or not self._depth:
|
||||||
return
|
return
|
||||||
|
|
||||||
if not Application.getInstance()._engine:
|
if not self._application._qml_engine:
|
||||||
return
|
return
|
||||||
|
|
||||||
if not self._volume_outline_color:
|
if not self._volume_outline_color:
|
||||||
theme = Application.getInstance().getTheme()
|
theme = self._application.getTheme()
|
||||||
self._volume_outline_color = Color(*theme.getColor("volume_outline").getRgb())
|
self._volume_outline_color = Color(*theme.getColor("volume_outline").getRgb())
|
||||||
self._x_axis_color = Color(*theme.getColor("x_axis").getRgb())
|
self._x_axis_color = Color(*theme.getColor("x_axis").getRgb())
|
||||||
self._y_axis_color = Color(*theme.getColor("y_axis").getRgb())
|
self._y_axis_color = Color(*theme.getColor("y_axis").getRgb())
|
||||||
|
@ -465,7 +467,7 @@ class BuildVolume(SceneNode):
|
||||||
maximum = Vector(max_w - bed_adhesion_size - 1, max_h - self._raft_thickness - self._extra_z_clearance, max_d - disallowed_area_size + bed_adhesion_size - 1)
|
maximum = Vector(max_w - bed_adhesion_size - 1, max_h - self._raft_thickness - self._extra_z_clearance, max_d - disallowed_area_size + bed_adhesion_size - 1)
|
||||||
)
|
)
|
||||||
|
|
||||||
Application.getInstance().getController().getScene()._maximum_bounds = scale_to_max_bounds
|
self._application.getController().getScene()._maximum_bounds = scale_to_max_bounds
|
||||||
|
|
||||||
self.updateNodeBoundaryCheck()
|
self.updateNodeBoundaryCheck()
|
||||||
|
|
||||||
|
@ -518,7 +520,7 @@ class BuildVolume(SceneNode):
|
||||||
for extruder in extruders:
|
for extruder in extruders:
|
||||||
extruder.propertyChanged.disconnect(self._onSettingPropertyChanged)
|
extruder.propertyChanged.disconnect(self._onSettingPropertyChanged)
|
||||||
|
|
||||||
self._global_container_stack = Application.getInstance().getGlobalContainerStack()
|
self._global_container_stack = self._application.getGlobalContainerStack()
|
||||||
|
|
||||||
if self._global_container_stack:
|
if self._global_container_stack:
|
||||||
self._global_container_stack.propertyChanged.connect(self._onSettingPropertyChanged)
|
self._global_container_stack.propertyChanged.connect(self._onSettingPropertyChanged)
|
||||||
|
@ -561,7 +563,7 @@ class BuildVolume(SceneNode):
|
||||||
|
|
||||||
if setting_key == "print_sequence":
|
if setting_key == "print_sequence":
|
||||||
machine_height = self._global_container_stack.getProperty("machine_height", "value")
|
machine_height = self._global_container_stack.getProperty("machine_height", "value")
|
||||||
if Application.getInstance().getGlobalContainerStack().getProperty("print_sequence", "value") == "one_at_a_time" and len(self._scene_objects) > 1:
|
if self._application.getGlobalContainerStack().getProperty("print_sequence", "value") == "one_at_a_time" and len(self._scene_objects) > 1:
|
||||||
self._height = min(self._global_container_stack.getProperty("gantry_height", "value"), machine_height)
|
self._height = min(self._global_container_stack.getProperty("gantry_height", "value"), machine_height)
|
||||||
if self._height < machine_height:
|
if self._height < machine_height:
|
||||||
self._build_volume_message.show()
|
self._build_volume_message.show()
|
||||||
|
|
|
@ -12,7 +12,6 @@ from UM.Scene.Selection import Selection
|
||||||
from UM.Scene.Iterator.BreadthFirstIterator import BreadthFirstIterator
|
from UM.Scene.Iterator.BreadthFirstIterator import BreadthFirstIterator
|
||||||
from UM.Operations.GroupedOperation import GroupedOperation
|
from UM.Operations.GroupedOperation import GroupedOperation
|
||||||
from UM.Operations.RemoveSceneNodeOperation import RemoveSceneNodeOperation
|
from UM.Operations.RemoveSceneNodeOperation import RemoveSceneNodeOperation
|
||||||
from UM.Operations.SetTransformOperation import SetTransformOperation
|
|
||||||
from UM.Operations.TranslateOperation import TranslateOperation
|
from UM.Operations.TranslateOperation import TranslateOperation
|
||||||
|
|
||||||
from cura.Operations.SetParentOperation import SetParentOperation
|
from cura.Operations.SetParentOperation import SetParentOperation
|
||||||
|
|
|
@ -1,9 +1,19 @@
|
||||||
# Copyright (c) 2018 Ultimaker B.V.
|
# Copyright (c) 2018 Ultimaker B.V.
|
||||||
# Cura is released under the terms of the LGPLv3 or higher.
|
# Cura is released under the terms of the LGPLv3 or higher.
|
||||||
|
|
||||||
from PyQt5.QtCore import QObject, QTimer
|
import copy
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
|
||||||
|
import numpy
|
||||||
|
|
||||||
|
from PyQt5.QtCore import QObject, QTimer, QUrl, pyqtSignal, pyqtProperty, QEvent, Q_ENUMS
|
||||||
from PyQt5.QtNetwork import QLocalServer
|
from PyQt5.QtNetwork import QLocalServer
|
||||||
from PyQt5.QtNetwork import QLocalSocket
|
from PyQt5.QtGui import QColor, QIcon
|
||||||
|
from PyQt5.QtWidgets import QMessageBox
|
||||||
|
from PyQt5.QtQml import qmlRegisterUncreatableType, qmlRegisterSingletonType, qmlRegisterType
|
||||||
|
|
||||||
from UM.Qt.QtApplication import QtApplication
|
from UM.Qt.QtApplication import QtApplication
|
||||||
from UM.Scene.SceneNode import SceneNode
|
from UM.Scene.SceneNode import SceneNode
|
||||||
|
@ -74,6 +84,7 @@ from cura.Settings.SimpleModeSettingsManager import SimpleModeSettingsManager
|
||||||
|
|
||||||
from cura.Machines.VariantManager import VariantManager
|
from cura.Machines.VariantManager import VariantManager
|
||||||
|
|
||||||
|
from .SingleInstance import SingleInstance
|
||||||
from . import PlatformPhysics
|
from . import PlatformPhysics
|
||||||
from . import BuildVolume
|
from . import BuildVolume
|
||||||
from . import CameraAnimation
|
from . import CameraAnimation
|
||||||
|
@ -93,22 +104,10 @@ from cura.Settings.ContainerManager import ContainerManager
|
||||||
|
|
||||||
from cura.ObjectsModel import ObjectsModel
|
from cura.ObjectsModel import ObjectsModel
|
||||||
|
|
||||||
from PyQt5.QtCore import QUrl, pyqtSignal, pyqtProperty, QEvent, Q_ENUMS
|
|
||||||
from UM.FlameProfiler import pyqtSlot
|
from UM.FlameProfiler import pyqtSlot
|
||||||
from PyQt5.QtGui import QColor, QIcon
|
|
||||||
from PyQt5.QtWidgets import QMessageBox
|
|
||||||
from PyQt5.QtQml import qmlRegisterUncreatableType, qmlRegisterSingletonType, qmlRegisterType
|
|
||||||
|
|
||||||
import sys
|
|
||||||
import numpy
|
|
||||||
import copy
|
|
||||||
import os
|
|
||||||
import argparse
|
|
||||||
import json
|
|
||||||
import time
|
|
||||||
|
|
||||||
|
|
||||||
numpy.seterr(all="ignore")
|
numpy.seterr(all = "ignore")
|
||||||
|
|
||||||
MYPY = False
|
MYPY = False
|
||||||
if not MYPY:
|
if not MYPY:
|
||||||
|
@ -143,19 +142,164 @@ class CuraApplication(QtApplication):
|
||||||
|
|
||||||
Q_ENUMS(ResourceTypes)
|
Q_ENUMS(ResourceTypes)
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
|
super().__init__(name = "cura",
|
||||||
|
version = CuraVersion,
|
||||||
|
buildtype = CuraBuildType,
|
||||||
|
is_debug_mode = CuraDebugMode,
|
||||||
|
tray_icon_name = "cura-icon-32.png",
|
||||||
|
**kwargs)
|
||||||
|
|
||||||
|
self.default_theme = "cura-light"
|
||||||
|
|
||||||
self._boot_loading_time = time.time()
|
self._boot_loading_time = time.time()
|
||||||
|
|
||||||
|
self._currently_loading_files = []
|
||||||
|
self._non_sliceable_extensions = []
|
||||||
|
|
||||||
|
# Variables set from CLI
|
||||||
|
self._files_to_open = []
|
||||||
|
self._use_single_instance = False
|
||||||
|
self._trigger_early_crash = False # For debug only
|
||||||
|
|
||||||
|
self._single_instance = None
|
||||||
|
|
||||||
|
self._cura_package_manager = None
|
||||||
|
|
||||||
|
self._machine_action_manager = None
|
||||||
|
|
||||||
|
self.empty_container = None
|
||||||
|
self.empty_definition_changes_container = None
|
||||||
|
self.empty_variant_container = None
|
||||||
|
self.empty_material_container = None
|
||||||
|
self.empty_quality_container = None
|
||||||
|
self.empty_quality_changes_container = None
|
||||||
|
|
||||||
|
self._variant_manager = None
|
||||||
|
self._material_manager = None
|
||||||
|
self._quality_manager = None
|
||||||
|
self._machine_manager = None
|
||||||
|
self._extruder_manager = None
|
||||||
|
self._container_manager = None
|
||||||
|
|
||||||
|
self._object_manager = None
|
||||||
|
self._build_plate_model = None
|
||||||
|
self._multi_build_plate_model = None
|
||||||
|
self._setting_visibility_presets_model = None
|
||||||
|
self._setting_inheritance_manager = None
|
||||||
|
self._simple_mode_settings_manager = None
|
||||||
|
self._cura_scene_controller = None
|
||||||
|
self._machine_error_checker = None
|
||||||
|
|
||||||
|
self._quality_profile_drop_down_menu_model = None
|
||||||
|
self._custom_quality_profile_drop_down_menu_model = None
|
||||||
|
|
||||||
|
self._physics = None
|
||||||
|
self._volume = None
|
||||||
|
self._output_devices = {}
|
||||||
|
self._print_information = None
|
||||||
|
self._previous_active_tool = None
|
||||||
|
self._platform_activity = False
|
||||||
|
self._scene_bounding_box = AxisAlignedBox.Null
|
||||||
|
|
||||||
|
self._job_name = None
|
||||||
|
self._center_after_select = False
|
||||||
|
self._camera_animation = None
|
||||||
|
self._cura_actions = None
|
||||||
|
self.started = False
|
||||||
|
|
||||||
|
self._message_box_callback = None
|
||||||
|
self._message_box_callback_arguments = []
|
||||||
|
self._preferred_mimetype = ""
|
||||||
|
self._i18n_catalog = None
|
||||||
|
|
||||||
|
self._currently_loading_files = []
|
||||||
|
self._non_sliceable_extensions = []
|
||||||
|
self._additional_components = {} # Components to add to certain areas in the interface
|
||||||
|
|
||||||
|
self._open_file_queue = [] # A list of files to open (after the application has started)
|
||||||
|
|
||||||
|
self._update_platform_activity_timer = None
|
||||||
|
|
||||||
|
self._need_to_show_user_agreement = True
|
||||||
|
|
||||||
|
from cura.Settings.CuraContainerRegistry import CuraContainerRegistry
|
||||||
|
self._container_registry_class = CuraContainerRegistry
|
||||||
|
|
||||||
|
# Adds command line options to the command line parser. This should be called after the application is created and
|
||||||
|
# before the pre-start.
|
||||||
|
def addCommandLineOptions(self):
|
||||||
|
super().addCommandLineOptions()
|
||||||
|
self._cli_parser.add_argument("--help", "-h",
|
||||||
|
action = "store_true",
|
||||||
|
default = False,
|
||||||
|
help = "Show this help message and exit.")
|
||||||
|
self._cli_parser.add_argument("--single-instance",
|
||||||
|
dest = "single_instance",
|
||||||
|
action = "store_true",
|
||||||
|
default = False)
|
||||||
|
# >> For debugging
|
||||||
|
# Trigger an early crash, i.e. a crash that happens before the application enters its event loop.
|
||||||
|
self._cli_parser.add_argument("--trigger-early-crash",
|
||||||
|
dest = "trigger_early_crash",
|
||||||
|
action = "store_true",
|
||||||
|
default = False,
|
||||||
|
help = "FOR TESTING ONLY. Trigger an early crash to show the crash dialog.")
|
||||||
|
self._cli_parser.add_argument("file", nargs = "*", help = "Files to load after starting the application.")
|
||||||
|
|
||||||
|
def parseCliOptions(self):
|
||||||
|
super().parseCliOptions()
|
||||||
|
|
||||||
|
if self._cli_args.help:
|
||||||
|
self._cli_parser.print_help()
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
self._use_single_instance = self._cli_args.single_instance
|
||||||
|
self._trigger_early_crash = self._cli_args.trigger_early_crash
|
||||||
|
for filename in self._cli_args.file:
|
||||||
|
self._files_to_open.append(os.path.abspath(filename))
|
||||||
|
|
||||||
|
def initialize(self) -> None:
|
||||||
|
super().initialize()
|
||||||
|
|
||||||
|
# Initialize the package manager to remove and install scheduled packages.
|
||||||
|
from cura.CuraPackageManager import CuraPackageManager
|
||||||
|
self._cura_package_manager = CuraPackageManager(self)
|
||||||
|
self._cura_package_manager.initialize()
|
||||||
|
|
||||||
|
self.__sendCommandToSingleInstance()
|
||||||
|
self.__addExpectedResourceDirsAndSearchPaths()
|
||||||
|
self.__initializeSettingDefinitionsAndFunctions()
|
||||||
|
self.__addAllResourcesAndContainerResources()
|
||||||
|
self.__addAllEmptyContainers()
|
||||||
|
self.__setLatestResouceVersionsForVersionUpgrade()
|
||||||
|
|
||||||
|
self._machine_action_manager = MachineActionManager.MachineActionManager(self)
|
||||||
|
self._machine_action_manager.initialize()
|
||||||
|
|
||||||
|
def __sendCommandToSingleInstance(self):
|
||||||
|
self._single_instance = SingleInstance(self, self._files_to_open)
|
||||||
|
|
||||||
|
# If we use single instance, try to connect to the single instance server, send commands, and then exit.
|
||||||
|
# If we cannot find an existing single instance server, this is the only instance, so just keep going.
|
||||||
|
if self._use_single_instance:
|
||||||
|
if self._single_instance.startClient():
|
||||||
|
Logger.log("i", "Single instance commands were sent, exiting")
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
# Adds expected directory names and search paths for Resources.
|
||||||
|
def __addExpectedResourceDirsAndSearchPaths(self):
|
||||||
# this list of dir names will be used by UM to detect an old cura directory
|
# this list of dir names will be used by UM to detect an old cura directory
|
||||||
for dir_name in ["extruders", "machine_instances", "materials", "plugins", "quality", "quality_changes", "user", "variants"]:
|
for dir_name in ["extruders", "machine_instances", "materials", "plugins", "quality", "quality_changes", "user", "variants"]:
|
||||||
Resources.addExpectedDirNameInData(dir_name)
|
Resources.addExpectedDirNameInData(dir_name)
|
||||||
|
|
||||||
Resources.addSearchPath(os.path.join(QtApplication.getInstallPrefix(), "share", "cura", "resources"))
|
Resources.addSearchPath(os.path.join(self._app_install_dir, "share", "cura", "resources"))
|
||||||
if not hasattr(sys, "frozen"):
|
if not hasattr(sys, "frozen"):
|
||||||
Resources.addSearchPath(os.path.join(os.path.abspath(os.path.dirname(__file__)), "..", "resources"))
|
Resources.addSearchPath(os.path.join(os.path.abspath(os.path.dirname(__file__)), "..", "resources"))
|
||||||
|
|
||||||
self._use_gui = True
|
# Adds custom property types, settings types, and extra operators (functions) that need to be registered in
|
||||||
self._open_file_queue = [] # Files to open when plug-ins are loaded.
|
# SettingDefinition and SettingFunction.
|
||||||
|
def __initializeSettingDefinitionsAndFunctions(self):
|
||||||
# Need to do this before ContainerRegistry tries to load the machines
|
# Need to do this before ContainerRegistry tries to load the machines
|
||||||
SettingDefinition.addSupportedProperty("settable_per_mesh", DefinitionPropertyType.Any, default = True, read_only = True)
|
SettingDefinition.addSupportedProperty("settable_per_mesh", DefinitionPropertyType.Any, default = True, read_only = True)
|
||||||
SettingDefinition.addSupportedProperty("settable_per_extruder", DefinitionPropertyType.Any, default = True, read_only = True)
|
SettingDefinition.addSupportedProperty("settable_per_extruder", DefinitionPropertyType.Any, default = True, read_only = True)
|
||||||
|
@ -180,7 +324,8 @@ class CuraApplication(QtApplication):
|
||||||
SettingFunction.registerOperator("extruderValue", ExtruderManager.getExtruderValue)
|
SettingFunction.registerOperator("extruderValue", ExtruderManager.getExtruderValue)
|
||||||
SettingFunction.registerOperator("resolveOrValue", ExtruderManager.getResolveOrValue)
|
SettingFunction.registerOperator("resolveOrValue", ExtruderManager.getResolveOrValue)
|
||||||
|
|
||||||
## Add the 4 types of profiles to storage.
|
# Adds all resources and container related resources.
|
||||||
|
def __addAllResourcesAndContainerResources(self) -> None:
|
||||||
Resources.addStorageType(self.ResourceTypes.QualityInstanceContainer, "quality")
|
Resources.addStorageType(self.ResourceTypes.QualityInstanceContainer, "quality")
|
||||||
Resources.addStorageType(self.ResourceTypes.QualityChangesInstanceContainer, "quality_changes")
|
Resources.addStorageType(self.ResourceTypes.QualityChangesInstanceContainer, "quality_changes")
|
||||||
Resources.addStorageType(self.ResourceTypes.VariantInstanceContainer, "variants")
|
Resources.addStorageType(self.ResourceTypes.VariantInstanceContainer, "variants")
|
||||||
|
@ -191,20 +336,64 @@ class CuraApplication(QtApplication):
|
||||||
Resources.addStorageType(self.ResourceTypes.DefinitionChangesContainer, "definition_changes")
|
Resources.addStorageType(self.ResourceTypes.DefinitionChangesContainer, "definition_changes")
|
||||||
Resources.addStorageType(self.ResourceTypes.SettingVisibilityPreset, "setting_visibility")
|
Resources.addStorageType(self.ResourceTypes.SettingVisibilityPreset, "setting_visibility")
|
||||||
|
|
||||||
ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.QualityInstanceContainer, "quality")
|
self._container_registry.addResourceType(self.ResourceTypes.QualityInstanceContainer, "quality")
|
||||||
ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.QualityChangesInstanceContainer, "quality_changes")
|
self._container_registry.addResourceType(self.ResourceTypes.QualityChangesInstanceContainer, "quality_changes")
|
||||||
ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.VariantInstanceContainer, "variant")
|
self._container_registry.addResourceType(self.ResourceTypes.VariantInstanceContainer, "variant")
|
||||||
ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.MaterialInstanceContainer, "material")
|
self._container_registry.addResourceType(self.ResourceTypes.MaterialInstanceContainer, "material")
|
||||||
ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.UserInstanceContainer, "user")
|
self._container_registry.addResourceType(self.ResourceTypes.UserInstanceContainer, "user")
|
||||||
ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.ExtruderStack, "extruder_train")
|
self._container_registry.addResourceType(self.ResourceTypes.ExtruderStack, "extruder_train")
|
||||||
ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.MachineStack, "machine")
|
self._container_registry.addResourceType(self.ResourceTypes.MachineStack, "machine")
|
||||||
ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.DefinitionChangesContainer, "definition_changes")
|
self._container_registry.addResourceType(self.ResourceTypes.DefinitionChangesContainer, "definition_changes")
|
||||||
|
|
||||||
## Initialise the version upgrade manager with Cura's storage paths.
|
Resources.addType(self.ResourceTypes.QmlFiles, "qml")
|
||||||
# Needs to be here to prevent circular dependencies.
|
Resources.addType(self.ResourceTypes.Firmware, "firmware")
|
||||||
import UM.VersionUpgradeManager
|
|
||||||
|
|
||||||
UM.VersionUpgradeManager.VersionUpgradeManager.getInstance().setCurrentVersions(
|
# Adds all empty containers.
|
||||||
|
def __addAllEmptyContainers(self) -> None:
|
||||||
|
# Add empty variant, material and quality containers.
|
||||||
|
# Since they are empty, they should never be serialized and instead just programmatically created.
|
||||||
|
# We need them to simplify the switching between materials.
|
||||||
|
empty_container = self._container_registry.getEmptyInstanceContainer()
|
||||||
|
self.empty_container = empty_container
|
||||||
|
|
||||||
|
empty_definition_changes_container = copy.deepcopy(empty_container)
|
||||||
|
empty_definition_changes_container.setMetaDataEntry("id", "empty_definition_changes")
|
||||||
|
empty_definition_changes_container.addMetaDataEntry("type", "definition_changes")
|
||||||
|
self._container_registry.addContainer(empty_definition_changes_container)
|
||||||
|
self.empty_definition_changes_container = empty_definition_changes_container
|
||||||
|
|
||||||
|
empty_variant_container = copy.deepcopy(empty_container)
|
||||||
|
empty_variant_container.setMetaDataEntry("id", "empty_variant")
|
||||||
|
empty_variant_container.addMetaDataEntry("type", "variant")
|
||||||
|
self._container_registry.addContainer(empty_variant_container)
|
||||||
|
self.empty_variant_container = empty_variant_container
|
||||||
|
|
||||||
|
empty_material_container = copy.deepcopy(empty_container)
|
||||||
|
empty_material_container.setMetaDataEntry("id", "empty_material")
|
||||||
|
empty_material_container.addMetaDataEntry("type", "material")
|
||||||
|
self._container_registry.addContainer(empty_material_container)
|
||||||
|
self.empty_material_container = empty_material_container
|
||||||
|
|
||||||
|
empty_quality_container = copy.deepcopy(empty_container)
|
||||||
|
empty_quality_container.setMetaDataEntry("id", "empty_quality")
|
||||||
|
empty_quality_container.setName("Not Supported")
|
||||||
|
empty_quality_container.addMetaDataEntry("quality_type", "not_supported")
|
||||||
|
empty_quality_container.addMetaDataEntry("type", "quality")
|
||||||
|
empty_quality_container.addMetaDataEntry("supported", False)
|
||||||
|
self._container_registry.addContainer(empty_quality_container)
|
||||||
|
self.empty_quality_container = empty_quality_container
|
||||||
|
|
||||||
|
empty_quality_changes_container = copy.deepcopy(empty_container)
|
||||||
|
empty_quality_changes_container.setMetaDataEntry("id", "empty_quality_changes")
|
||||||
|
empty_quality_changes_container.addMetaDataEntry("type", "quality_changes")
|
||||||
|
empty_quality_changes_container.addMetaDataEntry("quality_type", "not_supported")
|
||||||
|
self._container_registry.addContainer(empty_quality_changes_container)
|
||||||
|
self.empty_quality_changes_container = empty_quality_changes_container
|
||||||
|
|
||||||
|
# Initializes the version upgrade manager with by providing the paths for each resource type and the latest
|
||||||
|
# versions.
|
||||||
|
def __setLatestResouceVersionsForVersionUpgrade(self):
|
||||||
|
self._version_upgrade_manager.setCurrentVersions(
|
||||||
{
|
{
|
||||||
("quality_changes", InstanceContainer.Version * 1000000 + self.SettingVersion): (self.ResourceTypes.QualityChangesInstanceContainer, "application/x-uranium-instancecontainer"),
|
("quality_changes", InstanceContainer.Version * 1000000 + self.SettingVersion): (self.ResourceTypes.QualityChangesInstanceContainer, "application/x-uranium-instancecontainer"),
|
||||||
("machine_stack", ContainerStack.Version * 1000000 + self.SettingVersion): (self.ResourceTypes.MachineStack, "application/x-cura-globalstack"),
|
("machine_stack", ContainerStack.Version * 1000000 + self.SettingVersion): (self.ResourceTypes.MachineStack, "application/x-cura-globalstack"),
|
||||||
|
@ -216,46 +405,9 @@ class CuraApplication(QtApplication):
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
self._currently_loading_files = []
|
# Runs preparations that needs to be done before the starting process.
|
||||||
self._non_sliceable_extensions = []
|
def startSlashWindowPhase(self):
|
||||||
|
super().startSlashWindowPhase()
|
||||||
self._machine_action_manager = MachineActionManager.MachineActionManager()
|
|
||||||
self._machine_manager = None # This is initialized on demand.
|
|
||||||
self._extruder_manager = None
|
|
||||||
self._material_manager = None
|
|
||||||
self._quality_manager = None
|
|
||||||
self._object_manager = None
|
|
||||||
self._build_plate_model = None
|
|
||||||
self._multi_build_plate_model = None
|
|
||||||
self._setting_visibility_presets_model = None
|
|
||||||
self._setting_inheritance_manager = None
|
|
||||||
self._simple_mode_settings_manager = None
|
|
||||||
self._cura_scene_controller = None
|
|
||||||
self._machine_error_checker = None
|
|
||||||
|
|
||||||
self._additional_components = {} # Components to add to certain areas in the interface
|
|
||||||
|
|
||||||
super().__init__(name = "cura",
|
|
||||||
version = CuraVersion,
|
|
||||||
buildtype = CuraBuildType,
|
|
||||||
is_debug_mode = CuraDebugMode,
|
|
||||||
tray_icon_name = "cura-icon-32.png",
|
|
||||||
**kwargs)
|
|
||||||
|
|
||||||
# Initialize the package manager to remove and install scheduled packages.
|
|
||||||
from cura.CuraPackageManager import CuraPackageManager
|
|
||||||
self._cura_package_manager = CuraPackageManager(self)
|
|
||||||
self._cura_package_manager.initialize()
|
|
||||||
|
|
||||||
self.initialize()
|
|
||||||
|
|
||||||
# FOR TESTING ONLY
|
|
||||||
if kwargs["parsed_command_line"].get("trigger_early_crash", False):
|
|
||||||
assert not "This crash is triggered by the trigger_early_crash command line argument."
|
|
||||||
|
|
||||||
self._variant_manager = None
|
|
||||||
|
|
||||||
self.default_theme = "cura-light"
|
|
||||||
|
|
||||||
self.setWindowIcon(QIcon(Resources.getPath(Resources.Images, "cura-icon.png")))
|
self.setWindowIcon(QIcon(Resources.getPath(Resources.Images, "cura-icon.png")))
|
||||||
|
|
||||||
|
@ -289,23 +441,6 @@ class CuraApplication(QtApplication):
|
||||||
"SelectionTool",
|
"SelectionTool",
|
||||||
"TranslateTool"
|
"TranslateTool"
|
||||||
])
|
])
|
||||||
self._physics = None
|
|
||||||
self._volume = None
|
|
||||||
self._output_devices = {}
|
|
||||||
self._print_information = None
|
|
||||||
self._previous_active_tool = None
|
|
||||||
self._platform_activity = False
|
|
||||||
self._scene_bounding_box = AxisAlignedBox.Null
|
|
||||||
|
|
||||||
self._job_name = None
|
|
||||||
self._center_after_select = False
|
|
||||||
self._camera_animation = None
|
|
||||||
self._cura_actions = None
|
|
||||||
self.started = False
|
|
||||||
|
|
||||||
self._message_box_callback = None
|
|
||||||
self._message_box_callback_arguments = []
|
|
||||||
self._preferred_mimetype = ""
|
|
||||||
self._i18n_catalog = i18nCatalog("cura")
|
self._i18n_catalog = i18nCatalog("cura")
|
||||||
|
|
||||||
self._update_platform_activity_timer = QTimer()
|
self._update_platform_activity_timer = QTimer()
|
||||||
|
@ -318,53 +453,10 @@ class CuraApplication(QtApplication):
|
||||||
self.getController().contextMenuRequested.connect(self._onContextMenuRequested)
|
self.getController().contextMenuRequested.connect(self._onContextMenuRequested)
|
||||||
self.getCuraSceneController().activeBuildPlateChanged.connect(self.updatePlatformActivityDelayed)
|
self.getCuraSceneController().activeBuildPlateChanged.connect(self.updatePlatformActivityDelayed)
|
||||||
|
|
||||||
Resources.addType(self.ResourceTypes.QmlFiles, "qml")
|
|
||||||
Resources.addType(self.ResourceTypes.Firmware, "firmware")
|
|
||||||
|
|
||||||
self.showSplashMessage(self._i18n_catalog.i18nc("@info:progress", "Loading machines..."))
|
self.showSplashMessage(self._i18n_catalog.i18nc("@info:progress", "Loading machines..."))
|
||||||
|
|
||||||
# Add empty variant, material and quality containers.
|
with self._container_registry.lockFile():
|
||||||
# Since they are empty, they should never be serialized and instead just programmatically created.
|
self._container_registry.loadAllMetadata()
|
||||||
# We need them to simplify the switching between materials.
|
|
||||||
empty_container = ContainerRegistry.getInstance().getEmptyInstanceContainer()
|
|
||||||
self.empty_container = empty_container
|
|
||||||
|
|
||||||
empty_definition_changes_container = copy.deepcopy(empty_container)
|
|
||||||
empty_definition_changes_container.setMetaDataEntry("id", "empty_definition_changes")
|
|
||||||
empty_definition_changes_container.addMetaDataEntry("type", "definition_changes")
|
|
||||||
ContainerRegistry.getInstance().addContainer(empty_definition_changes_container)
|
|
||||||
self.empty_definition_changes_container = empty_definition_changes_container
|
|
||||||
|
|
||||||
empty_variant_container = copy.deepcopy(empty_container)
|
|
||||||
empty_variant_container.setMetaDataEntry("id", "empty_variant")
|
|
||||||
empty_variant_container.addMetaDataEntry("type", "variant")
|
|
||||||
ContainerRegistry.getInstance().addContainer(empty_variant_container)
|
|
||||||
self.empty_variant_container = empty_variant_container
|
|
||||||
|
|
||||||
empty_material_container = copy.deepcopy(empty_container)
|
|
||||||
empty_material_container.setMetaDataEntry("id", "empty_material")
|
|
||||||
empty_material_container.addMetaDataEntry("type", "material")
|
|
||||||
ContainerRegistry.getInstance().addContainer(empty_material_container)
|
|
||||||
self.empty_material_container = empty_material_container
|
|
||||||
|
|
||||||
empty_quality_container = copy.deepcopy(empty_container)
|
|
||||||
empty_quality_container.setMetaDataEntry("id", "empty_quality")
|
|
||||||
empty_quality_container.setName("Not Supported")
|
|
||||||
empty_quality_container.addMetaDataEntry("quality_type", "not_supported")
|
|
||||||
empty_quality_container.addMetaDataEntry("type", "quality")
|
|
||||||
empty_quality_container.addMetaDataEntry("supported", False)
|
|
||||||
ContainerRegistry.getInstance().addContainer(empty_quality_container)
|
|
||||||
self.empty_quality_container = empty_quality_container
|
|
||||||
|
|
||||||
empty_quality_changes_container = copy.deepcopy(empty_container)
|
|
||||||
empty_quality_changes_container.setMetaDataEntry("id", "empty_quality_changes")
|
|
||||||
empty_quality_changes_container.addMetaDataEntry("type", "quality_changes")
|
|
||||||
empty_quality_changes_container.addMetaDataEntry("quality_type", "not_supported")
|
|
||||||
ContainerRegistry.getInstance().addContainer(empty_quality_changes_container)
|
|
||||||
self.empty_quality_changes_container = empty_quality_changes_container
|
|
||||||
|
|
||||||
with ContainerRegistry.getInstance().lockFile():
|
|
||||||
ContainerRegistry.getInstance().loadAllMetadata()
|
|
||||||
|
|
||||||
# set the setting version for Preferences
|
# set the setting version for Preferences
|
||||||
preferences = Preferences.getInstance()
|
preferences = Preferences.getInstance()
|
||||||
|
@ -411,13 +503,10 @@ class CuraApplication(QtApplication):
|
||||||
|
|
||||||
self.getCuraSceneController().setActiveBuildPlate(0) # Initialize
|
self.getCuraSceneController().setActiveBuildPlate(0) # Initialize
|
||||||
|
|
||||||
self._quality_profile_drop_down_menu_model = None
|
|
||||||
self._custom_quality_profile_drop_down_menu_model = None
|
|
||||||
|
|
||||||
CuraApplication.Created = True
|
CuraApplication.Created = True
|
||||||
|
|
||||||
def _onEngineCreated(self):
|
def _onEngineCreated(self):
|
||||||
self._engine.addImageProvider("camera", CameraImageProvider.CameraImageProvider())
|
self._qml_engine.addImageProvider("camera", CameraImageProvider.CameraImageProvider())
|
||||||
|
|
||||||
@pyqtProperty(bool)
|
@pyqtProperty(bool)
|
||||||
def needToShowUserAgreement(self):
|
def needToShowUserAgreement(self):
|
||||||
|
@ -515,10 +604,6 @@ class CuraApplication(QtApplication):
|
||||||
def setDefaultPath(self, key, default_path):
|
def setDefaultPath(self, key, default_path):
|
||||||
Preferences.getInstance().setValue("local_file/%s" % key, QUrl(default_path).toLocalFile())
|
Preferences.getInstance().setValue("local_file/%s" % key, QUrl(default_path).toLocalFile())
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def getStaticVersion(cls):
|
|
||||||
return CuraVersion
|
|
||||||
|
|
||||||
## Handle loading of all plugin types (and the backend explicitly)
|
## Handle loading of all plugin types (and the backend explicitly)
|
||||||
# \sa PluginRegistry
|
# \sa PluginRegistry
|
||||||
def _loadPlugins(self):
|
def _loadPlugins(self):
|
||||||
|
@ -543,127 +628,8 @@ class CuraApplication(QtApplication):
|
||||||
|
|
||||||
self._plugins_loaded = True
|
self._plugins_loaded = True
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def addCommandLineOptions(cls, parser, parsed_command_line = None):
|
|
||||||
if parsed_command_line is None:
|
|
||||||
parsed_command_line = {}
|
|
||||||
super().addCommandLineOptions(parser, parsed_command_line = parsed_command_line)
|
|
||||||
parser.add_argument("file", nargs="*", help="Files to load after starting the application.")
|
|
||||||
parser.add_argument("--single-instance", action="store_true", default=False)
|
|
||||||
|
|
||||||
# Set up a local socket server which listener which coordinates single instances Curas and accepts commands.
|
|
||||||
def _setUpSingleInstanceServer(self):
|
|
||||||
if self.getCommandLineOption("single_instance", False):
|
|
||||||
self.__single_instance_server = QLocalServer()
|
|
||||||
self.__single_instance_server.newConnection.connect(self._singleInstanceServerNewConnection)
|
|
||||||
self.__single_instance_server.listen("ultimaker-cura")
|
|
||||||
|
|
||||||
def _singleInstanceServerNewConnection(self):
|
|
||||||
Logger.log("i", "New connection recevied on our single-instance server")
|
|
||||||
remote_cura_connection = self.__single_instance_server.nextPendingConnection()
|
|
||||||
|
|
||||||
if remote_cura_connection is not None:
|
|
||||||
def readCommands():
|
|
||||||
line = remote_cura_connection.readLine()
|
|
||||||
while len(line) != 0: # There is also a .canReadLine()
|
|
||||||
try:
|
|
||||||
payload = json.loads(str(line, encoding="ASCII").strip())
|
|
||||||
command = payload["command"]
|
|
||||||
|
|
||||||
# Command: Remove all models from the build plate.
|
|
||||||
if command == "clear-all":
|
|
||||||
self.deleteAll()
|
|
||||||
|
|
||||||
# Command: Load a model file
|
|
||||||
elif command == "open":
|
|
||||||
self._openFile(payload["filePath"])
|
|
||||||
# WARNING ^ this method is async and we really should wait until
|
|
||||||
# the file load is complete before processing more commands.
|
|
||||||
|
|
||||||
# Command: Activate the window and bring it to the top.
|
|
||||||
elif command == "focus":
|
|
||||||
# Operating systems these days prevent windows from moving around by themselves.
|
|
||||||
# 'alert' or flashing the icon in the taskbar is the best thing we do now.
|
|
||||||
self.getMainWindow().alert(0)
|
|
||||||
|
|
||||||
# Command: Close the socket connection. We're done.
|
|
||||||
elif command == "close-connection":
|
|
||||||
remote_cura_connection.close()
|
|
||||||
|
|
||||||
else:
|
|
||||||
Logger.log("w", "Received an unrecognized command " + str(command))
|
|
||||||
except json.decoder.JSONDecodeError as ex:
|
|
||||||
Logger.log("w", "Unable to parse JSON command in _singleInstanceServerNewConnection(): " + repr(ex))
|
|
||||||
line = remote_cura_connection.readLine()
|
|
||||||
|
|
||||||
remote_cura_connection.readyRead.connect(readCommands)
|
|
||||||
|
|
||||||
## Perform any checks before creating the main application.
|
|
||||||
#
|
|
||||||
# This should be called directly before creating an instance of CuraApplication.
|
|
||||||
# \returns \type{bool} True if the whole Cura app should continue running.
|
|
||||||
@classmethod
|
|
||||||
def preStartUp(cls, parser = None, parsed_command_line = None):
|
|
||||||
if parsed_command_line is None:
|
|
||||||
parsed_command_line = {}
|
|
||||||
|
|
||||||
# Peek the arguments and look for the 'single-instance' flag.
|
|
||||||
if not parser:
|
|
||||||
parser = argparse.ArgumentParser(prog = "cura", add_help = False) # pylint: disable=bad-whitespace
|
|
||||||
CuraApplication.addCommandLineOptions(parser, parsed_command_line = parsed_command_line)
|
|
||||||
# Important: It is important to keep this line here!
|
|
||||||
# In Uranium we allow to pass unknown arguments to the final executable or script.
|
|
||||||
parsed_command_line.update(vars(parser.parse_known_args()[0]))
|
|
||||||
|
|
||||||
if parsed_command_line["single_instance"]:
|
|
||||||
Logger.log("i", "Checking for the presence of an ready running Cura instance.")
|
|
||||||
single_instance_socket = QLocalSocket()
|
|
||||||
Logger.log("d", "preStartUp(): full server name: " + single_instance_socket.fullServerName())
|
|
||||||
single_instance_socket.connectToServer("ultimaker-cura")
|
|
||||||
single_instance_socket.waitForConnected()
|
|
||||||
if single_instance_socket.state() == QLocalSocket.ConnectedState:
|
|
||||||
Logger.log("i", "Connection has been made to the single-instance Cura socket.")
|
|
||||||
|
|
||||||
# Protocol is one line of JSON terminated with a carriage return.
|
|
||||||
# "command" field is required and holds the name of the command to execute.
|
|
||||||
# Other fields depend on the command.
|
|
||||||
|
|
||||||
payload = {"command": "clear-all"}
|
|
||||||
single_instance_socket.write(bytes(json.dumps(payload) + "\n", encoding="ASCII"))
|
|
||||||
|
|
||||||
payload = {"command": "focus"}
|
|
||||||
single_instance_socket.write(bytes(json.dumps(payload) + "\n", encoding="ASCII"))
|
|
||||||
|
|
||||||
if len(parsed_command_line["file"]) != 0:
|
|
||||||
for filename in parsed_command_line["file"]:
|
|
||||||
payload = {"command": "open", "filePath": filename}
|
|
||||||
single_instance_socket.write(bytes(json.dumps(payload) + "\n", encoding="ASCII"))
|
|
||||||
|
|
||||||
payload = {"command": "close-connection"}
|
|
||||||
single_instance_socket.write(bytes(json.dumps(payload) + "\n", encoding="ASCII"))
|
|
||||||
|
|
||||||
single_instance_socket.flush()
|
|
||||||
single_instance_socket.waitForDisconnected()
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
def preRun(self):
|
|
||||||
# Last check for unknown commandline arguments
|
|
||||||
parser = self.getCommandlineParser()
|
|
||||||
parser.add_argument("--help", "-h",
|
|
||||||
action='store_true',
|
|
||||||
default = False,
|
|
||||||
help = "Show this help message and exit."
|
|
||||||
)
|
|
||||||
parsed_args = vars(parser.parse_args()) # This won't allow unknown arguments
|
|
||||||
if parsed_args["help"]:
|
|
||||||
parser.print_help()
|
|
||||||
sys.exit(0)
|
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
self.preRun()
|
container_registry = self._container_registry
|
||||||
|
|
||||||
container_registry = ContainerRegistry.getInstance()
|
|
||||||
|
|
||||||
Logger.log("i", "Initializing variant manager")
|
Logger.log("i", "Initializing variant manager")
|
||||||
self._variant_manager = VariantManager(container_registry)
|
self._variant_manager = VariantManager(container_registry)
|
||||||
|
@ -682,20 +648,25 @@ class CuraApplication(QtApplication):
|
||||||
Logger.log("i", "Initializing machine manager")
|
Logger.log("i", "Initializing machine manager")
|
||||||
self._machine_manager = MachineManager(self)
|
self._machine_manager = MachineManager(self)
|
||||||
|
|
||||||
|
Logger.log("i", "Initializing container manager")
|
||||||
|
self._container_manager = ContainerManager(self)
|
||||||
|
|
||||||
Logger.log("i", "Initializing machine error checker")
|
Logger.log("i", "Initializing machine error checker")
|
||||||
self._machine_error_checker = MachineErrorChecker(self)
|
self._machine_error_checker = MachineErrorChecker(self)
|
||||||
self._machine_error_checker.initialize()
|
self._machine_error_checker.initialize()
|
||||||
|
|
||||||
# Check if we should run as single instance or not
|
# Check if we should run as single instance or not. If so, set up a local socket server which listener which
|
||||||
self._setUpSingleInstanceServer()
|
# coordinates multiple Cura instances and accepts commands.
|
||||||
|
if self._use_single_instance:
|
||||||
|
self.__setUpSingleInstanceServer()
|
||||||
|
|
||||||
# Setup scene and build volume
|
# Setup scene and build volume
|
||||||
root = self.getController().getScene().getRoot()
|
root = self.getController().getScene().getRoot()
|
||||||
self._volume = BuildVolume.BuildVolume(self.getController().getScene().getRoot())
|
self._volume = BuildVolume.BuildVolume(self, root)
|
||||||
Arrange.build_volume = self._volume
|
Arrange.build_volume = self._volume
|
||||||
|
|
||||||
# initialize info objects
|
# initialize info objects
|
||||||
self._print_information = PrintInformation.PrintInformation()
|
self._print_information = PrintInformation.PrintInformation(self)
|
||||||
self._cura_actions = CuraActions.CuraActions(self)
|
self._cura_actions = CuraActions.CuraActions(self)
|
||||||
|
|
||||||
# Initialize setting visibility presets model
|
# Initialize setting visibility presets model
|
||||||
|
@ -704,7 +675,7 @@ class CuraApplication(QtApplication):
|
||||||
Preferences.getInstance().setDefault("general/visible_settings", ";".join(default_visibility_profile["settings"]))
|
Preferences.getInstance().setDefault("general/visible_settings", ";".join(default_visibility_profile["settings"]))
|
||||||
|
|
||||||
# Detect in which mode to run and execute that mode
|
# Detect in which mode to run and execute that mode
|
||||||
if self.getCommandLineOption("headless", False):
|
if self._is_headless:
|
||||||
self.runWithoutGUI()
|
self.runWithoutGUI()
|
||||||
else:
|
else:
|
||||||
self.runWithGUI()
|
self.runWithGUI()
|
||||||
|
@ -713,7 +684,6 @@ class CuraApplication(QtApplication):
|
||||||
self.initializationFinished.emit()
|
self.initializationFinished.emit()
|
||||||
Logger.log("d", "Booting Cura took %s seconds", time.time() - self._boot_loading_time)
|
Logger.log("d", "Booting Cura took %s seconds", time.time() - self._boot_loading_time)
|
||||||
|
|
||||||
|
|
||||||
# For now use a timer to postpone some things that need to be done after the application and GUI are
|
# For now use a timer to postpone some things that need to be done after the application and GUI are
|
||||||
# initialized, for example opening files because they may show dialogs which can be closed due to incomplete
|
# initialized, for example opening files because they may show dialogs which can be closed due to incomplete
|
||||||
# GUI initialization.
|
# GUI initialization.
|
||||||
|
@ -725,8 +695,12 @@ class CuraApplication(QtApplication):
|
||||||
|
|
||||||
self.exec_()
|
self.exec_()
|
||||||
|
|
||||||
|
def __setUpSingleInstanceServer(self):
|
||||||
|
if self._use_single_instance:
|
||||||
|
self._single_instance.startServer()
|
||||||
|
|
||||||
def _onPostStart(self):
|
def _onPostStart(self):
|
||||||
for file_name in self.getCommandLineOption("file", []):
|
for file_name in self._files_to_open:
|
||||||
self.callLater(self._openFile, file_name)
|
self.callLater(self._openFile, file_name)
|
||||||
for file_name in self._open_file_queue: # Open all the files that were queued up while plug-ins were loading.
|
for file_name in self._open_file_queue: # Open all the files that were queued up while plug-ins were loading.
|
||||||
self.callLater(self._openFile, file_name)
|
self.callLater(self._openFile, file_name)
|
||||||
|
@ -735,13 +709,10 @@ class CuraApplication(QtApplication):
|
||||||
|
|
||||||
## Run Cura without GUI elements and interaction (server mode).
|
## Run Cura without GUI elements and interaction (server mode).
|
||||||
def runWithoutGUI(self):
|
def runWithoutGUI(self):
|
||||||
self._use_gui = False
|
|
||||||
self.closeSplash()
|
self.closeSplash()
|
||||||
|
|
||||||
## Run Cura with GUI (desktop mode).
|
## Run Cura with GUI (desktop mode).
|
||||||
def runWithGUI(self):
|
def runWithGUI(self):
|
||||||
self._use_gui = True
|
|
||||||
|
|
||||||
self.showSplashMessage(self._i18n_catalog.i18nc("@info:progress", "Setting up scene..."))
|
self.showSplashMessage(self._i18n_catalog.i18nc("@info:progress", "Setting up scene..."))
|
||||||
|
|
||||||
controller = self.getController()
|
controller = self.getController()
|
||||||
|
@ -791,9 +762,6 @@ class CuraApplication(QtApplication):
|
||||||
# Hide the splash screen
|
# Hide the splash screen
|
||||||
self.closeSplash()
|
self.closeSplash()
|
||||||
|
|
||||||
def hasGui(self):
|
|
||||||
return self._use_gui
|
|
||||||
|
|
||||||
@pyqtSlot(result = QObject)
|
@pyqtSlot(result = QObject)
|
||||||
def getSettingVisibilityPresetsModel(self, *args) -> SettingVisibilityPresetsModel:
|
def getSettingVisibilityPresetsModel(self, *args) -> SettingVisibilityPresetsModel:
|
||||||
return self._setting_visibility_presets_model
|
return self._setting_visibility_presets_model
|
||||||
|
@ -808,7 +776,7 @@ class CuraApplication(QtApplication):
|
||||||
|
|
||||||
def getExtruderManager(self, *args):
|
def getExtruderManager(self, *args):
|
||||||
if self._extruder_manager is None:
|
if self._extruder_manager is None:
|
||||||
self._extruder_manager = ExtruderManager.createExtruderManager()
|
self._extruder_manager = ExtruderManager()
|
||||||
return self._extruder_manager
|
return self._extruder_manager
|
||||||
|
|
||||||
@pyqtSlot(result = QObject)
|
@pyqtSlot(result = QObject)
|
||||||
|
@ -932,7 +900,7 @@ class CuraApplication(QtApplication):
|
||||||
qmlRegisterType(QualitySettingsModel, "Cura", 1, 0, "QualitySettingsModel")
|
qmlRegisterType(QualitySettingsModel, "Cura", 1, 0, "QualitySettingsModel")
|
||||||
qmlRegisterType(MachineNameValidator, "Cura", 1, 0, "MachineNameValidator")
|
qmlRegisterType(MachineNameValidator, "Cura", 1, 0, "MachineNameValidator")
|
||||||
qmlRegisterType(UserChangesModel, "Cura", 1, 0, "UserChangesModel")
|
qmlRegisterType(UserChangesModel, "Cura", 1, 0, "UserChangesModel")
|
||||||
qmlRegisterSingletonType(ContainerManager, "Cura", 1, 0, "ContainerManager", ContainerManager.createContainerManager)
|
qmlRegisterSingletonType(ContainerManager, "Cura", 1, 0, "ContainerManager", ContainerManager.getInstance)
|
||||||
|
|
||||||
# As of Qt5.7, it is necessary to get rid of any ".." in the path for the singleton to work.
|
# As of Qt5.7, it is necessary to get rid of any ".." in the path for the singleton to work.
|
||||||
actions_url = QUrl.fromLocalFile(os.path.abspath(Resources.getPath(CuraApplication.ResourceTypes.QmlFiles, "Actions.qml")))
|
actions_url = QUrl.fromLocalFile(os.path.abspath(Resources.getPath(CuraApplication.ResourceTypes.QmlFiles, "Actions.qml")))
|
||||||
|
@ -1520,8 +1488,7 @@ class CuraApplication(QtApplication):
|
||||||
# see GroupDecorator._onChildrenChanged
|
# see GroupDecorator._onChildrenChanged
|
||||||
|
|
||||||
def _createSplashScreen(self):
|
def _createSplashScreen(self):
|
||||||
run_headless = self.getCommandLineOption("headless", False)
|
if self._is_headless:
|
||||||
if run_headless:
|
|
||||||
return None
|
return None
|
||||||
return CuraSplashScreen.CuraSplashScreen()
|
return CuraSplashScreen.CuraSplashScreen()
|
||||||
|
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
# Copyright (c) 2016 Ultimaker B.V.
|
# Copyright (c) 2018 Ultimaker B.V.
|
||||||
# Cura is released under the terms of the LGPLv3 or higher.
|
# Cura is released under the terms of the LGPLv3 or higher.
|
||||||
from UM.Logger import Logger
|
|
||||||
from UM.PluginRegistry import PluginRegistry # So MachineAction can be added as plugin type
|
|
||||||
|
|
||||||
from UM.Settings.ContainerRegistry import ContainerRegistry
|
|
||||||
from UM.Settings.DefinitionContainer import DefinitionContainer
|
|
||||||
|
|
||||||
from PyQt5.QtCore import QObject
|
from PyQt5.QtCore import QObject
|
||||||
|
|
||||||
from UM.FlameProfiler import pyqtSlot
|
from UM.FlameProfiler import pyqtSlot
|
||||||
|
from UM.Logger import Logger
|
||||||
|
from UM.PluginRegistry import PluginRegistry # So MachineAction can be added as plugin type
|
||||||
|
from UM.Settings.DefinitionContainer import DefinitionContainer
|
||||||
|
|
||||||
|
|
||||||
## Raised when trying to add an unknown machine action as a required action
|
## Raised when trying to add an unknown machine action as a required action
|
||||||
class UnknownMachineActionError(Exception):
|
class UnknownMachineActionError(Exception):
|
||||||
|
@ -20,23 +20,27 @@ class NotUniqueMachineActionError(Exception):
|
||||||
|
|
||||||
|
|
||||||
class MachineActionManager(QObject):
|
class MachineActionManager(QObject):
|
||||||
def __init__(self, parent = None):
|
def __init__(self, application, parent = None):
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
|
self._application = application
|
||||||
|
|
||||||
self._machine_actions = {} # Dict of all known machine actions
|
self._machine_actions = {} # Dict of all known machine actions
|
||||||
self._required_actions = {} # Dict of all required actions by definition ID
|
self._required_actions = {} # Dict of all required actions by definition ID
|
||||||
self._supported_actions = {} # Dict of all supported actions by definition ID
|
self._supported_actions = {} # Dict of all supported actions by definition ID
|
||||||
self._first_start_actions = {} # Dict of all actions that need to be done when first added by definition ID
|
self._first_start_actions = {} # Dict of all actions that need to be done when first added by definition ID
|
||||||
|
|
||||||
|
def initialize(self):
|
||||||
|
container_registry = self._application.getContainerRegistry()
|
||||||
|
|
||||||
# Add machine_action as plugin type
|
# Add machine_action as plugin type
|
||||||
PluginRegistry.addType("machine_action", self.addMachineAction)
|
PluginRegistry.addType("machine_action", self.addMachineAction)
|
||||||
|
|
||||||
# Ensure that all containers that were registered before creation of this registry are also handled.
|
# Ensure that all containers that were registered before creation of this registry are also handled.
|
||||||
# This should not have any effect, but it makes it safer if we ever refactor the order of things.
|
# This should not have any effect, but it makes it safer if we ever refactor the order of things.
|
||||||
for container in ContainerRegistry.getInstance().findDefinitionContainers():
|
for container in container_registry.findDefinitionContainers():
|
||||||
self._onContainerAdded(container)
|
self._onContainerAdded(container)
|
||||||
|
|
||||||
ContainerRegistry.getInstance().containerAdded.connect(self._onContainerAdded)
|
container_registry.containerAdded.connect(self._onContainerAdded)
|
||||||
|
|
||||||
def _onContainerAdded(self, container):
|
def _onContainerAdded(self, container):
|
||||||
## Ensure that the actions are added to this manager
|
## Ensure that the actions are added to this manager
|
||||||
|
|
|
@ -1,24 +1,24 @@
|
||||||
# Copyright (c) 2018 Ultimaker B.V.
|
# Copyright (c) 2018 Ultimaker B.V.
|
||||||
# Cura is released under the terms of the LGPLv3 or higher.
|
# Cura is released under the terms of the LGPLv3 or higher.
|
||||||
|
|
||||||
from typing import Dict
|
|
||||||
import math
|
|
||||||
import os.path
|
|
||||||
import unicodedata
|
|
||||||
import json
|
import json
|
||||||
|
import math
|
||||||
|
import os
|
||||||
|
import unicodedata
|
||||||
import re # To create abbreviations for printer names.
|
import re # To create abbreviations for printer names.
|
||||||
|
from typing import Dict
|
||||||
|
|
||||||
from PyQt5.QtCore import QObject, pyqtSignal, pyqtProperty, pyqtSlot
|
from PyQt5.QtCore import QObject, pyqtSignal, pyqtProperty, pyqtSlot
|
||||||
|
|
||||||
from UM.Application import Application
|
|
||||||
from UM.Logger import Logger
|
|
||||||
from UM.Qt.Duration import Duration
|
|
||||||
from UM.Preferences import Preferences
|
|
||||||
from UM.Scene.SceneNode import SceneNode
|
|
||||||
from UM.i18n import i18nCatalog
|
from UM.i18n import i18nCatalog
|
||||||
|
from UM.Logger import Logger
|
||||||
|
from UM.Preferences import Preferences
|
||||||
|
from UM.Qt.Duration import Duration
|
||||||
|
from UM.Scene.SceneNode import SceneNode
|
||||||
|
|
||||||
catalog = i18nCatalog("cura")
|
catalog = i18nCatalog("cura")
|
||||||
|
|
||||||
|
|
||||||
## A class for processing and calculating minimum, current and maximum print time as well as managing the job name
|
## A class for processing and calculating minimum, current and maximum print time as well as managing the job name
|
||||||
#
|
#
|
||||||
# This class contains all the logic relating to calculation and slicing for the
|
# This class contains all the logic relating to calculation and slicing for the
|
||||||
|
@ -47,8 +47,9 @@ class PrintInformation(QObject):
|
||||||
ActiveMachineChanged = 3
|
ActiveMachineChanged = 3
|
||||||
Other = 4
|
Other = 4
|
||||||
|
|
||||||
def __init__(self, parent = None):
|
def __init__(self, application, parent = None):
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
|
self._application = application
|
||||||
|
|
||||||
self.initializeCuraMessagePrintTimeProperties()
|
self.initializeCuraMessagePrintTimeProperties()
|
||||||
|
|
||||||
|
@ -59,10 +60,10 @@ class PrintInformation(QObject):
|
||||||
|
|
||||||
self._pre_sliced = False
|
self._pre_sliced = False
|
||||||
|
|
||||||
self._backend = Application.getInstance().getBackend()
|
self._backend = self._application.getBackend()
|
||||||
if self._backend:
|
if self._backend:
|
||||||
self._backend.printDurationMessage.connect(self._onPrintDurationMessage)
|
self._backend.printDurationMessage.connect(self._onPrintDurationMessage)
|
||||||
Application.getInstance().getController().getScene().sceneChanged.connect(self._onSceneChanged)
|
self._application.getController().getScene().sceneChanged.connect(self._onSceneChanged)
|
||||||
|
|
||||||
self._base_name = ""
|
self._base_name = ""
|
||||||
self._abbr_machine = ""
|
self._abbr_machine = ""
|
||||||
|
@ -71,7 +72,6 @@ class PrintInformation(QObject):
|
||||||
self._active_build_plate = 0
|
self._active_build_plate = 0
|
||||||
self._initVariablesWithBuildPlate(self._active_build_plate)
|
self._initVariablesWithBuildPlate(self._active_build_plate)
|
||||||
|
|
||||||
self._application = Application.getInstance()
|
|
||||||
self._multi_build_plate_model = self._application.getMultiBuildPlateModel()
|
self._multi_build_plate_model = self._application.getMultiBuildPlateModel()
|
||||||
|
|
||||||
self._application.globalContainerStackChanged.connect(self._updateJobName)
|
self._application.globalContainerStackChanged.connect(self._updateJobName)
|
||||||
|
@ -199,7 +199,7 @@ class PrintInformation(QObject):
|
||||||
self._current_print_time[build_plate_number].setDuration(total_estimated_time)
|
self._current_print_time[build_plate_number].setDuration(total_estimated_time)
|
||||||
|
|
||||||
def _calculateInformation(self, build_plate_number):
|
def _calculateInformation(self, build_plate_number):
|
||||||
global_stack = Application.getInstance().getGlobalContainerStack()
|
global_stack = self._application.getGlobalContainerStack()
|
||||||
if global_stack is None:
|
if global_stack is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -358,7 +358,7 @@ class PrintInformation(QObject):
|
||||||
## Created an acronymn-like abbreviated machine name from the currently active machine name
|
## Created an acronymn-like abbreviated machine name from the currently active machine name
|
||||||
# Called each time the global stack is switched
|
# Called each time the global stack is switched
|
||||||
def _setAbbreviatedMachineName(self):
|
def _setAbbreviatedMachineName(self):
|
||||||
global_container_stack = Application.getInstance().getGlobalContainerStack()
|
global_container_stack = self._application.getGlobalContainerStack()
|
||||||
if not global_container_stack:
|
if not global_container_stack:
|
||||||
self._abbr_machine = ""
|
self._abbr_machine = ""
|
||||||
return
|
return
|
||||||
|
|
|
@ -24,7 +24,7 @@ class ConvexHullNode(SceneNode):
|
||||||
self._original_parent = parent
|
self._original_parent = parent
|
||||||
|
|
||||||
# Color of the drawn convex hull
|
# Color of the drawn convex hull
|
||||||
if Application.getInstance().hasGui():
|
if not Application.getInstance().getIsHeadLess():
|
||||||
self._color = Color(*Application.getInstance().getTheme().getColor("convex_hull").getRgb())
|
self._color = Color(*Application.getInstance().getTheme().getColor("convex_hull").getRgb())
|
||||||
else:
|
else:
|
||||||
self._color = Color(0, 0, 0)
|
self._color = Color(0, 0, 0)
|
||||||
|
|
|
@ -1,32 +1,25 @@
|
||||||
# Copyright (c) 2018 Ultimaker B.V.
|
# Copyright (c) 2018 Ultimaker B.V.
|
||||||
# Cura is released under the terms of the LGPLv3 or higher.
|
# Cura is released under the terms of the LGPLv3 or higher.
|
||||||
|
|
||||||
import os.path
|
import os
|
||||||
import urllib.parse
|
import urllib.parse
|
||||||
import uuid
|
import uuid
|
||||||
from typing import Dict, Union
|
from typing import Dict, Union
|
||||||
|
|
||||||
from PyQt5.QtCore import QObject, QUrl, QVariant
|
from PyQt5.QtCore import QObject, QUrl, QVariant
|
||||||
from UM.FlameProfiler import pyqtSlot
|
|
||||||
from PyQt5.QtWidgets import QMessageBox
|
from PyQt5.QtWidgets import QMessageBox
|
||||||
|
|
||||||
from UM.PluginRegistry import PluginRegistry
|
from UM.i18n import i18nCatalog
|
||||||
from UM.SaveFile import SaveFile
|
from UM.FlameProfiler import pyqtSlot
|
||||||
from UM.Platform import Platform
|
|
||||||
from UM.MimeTypeDatabase import MimeTypeDatabase
|
|
||||||
|
|
||||||
from UM.Logger import Logger
|
from UM.Logger import Logger
|
||||||
from UM.Application import Application
|
from UM.MimeTypeDatabase import MimeTypeDatabase, MimeTypeNotFoundError
|
||||||
|
from UM.Platform import Platform
|
||||||
|
from UM.SaveFile import SaveFile
|
||||||
|
from UM.Settings.ContainerFormatError import ContainerFormatError
|
||||||
from UM.Settings.ContainerStack import ContainerStack
|
from UM.Settings.ContainerStack import ContainerStack
|
||||||
from UM.Settings.DefinitionContainer import DefinitionContainer
|
from UM.Settings.DefinitionContainer import DefinitionContainer
|
||||||
from UM.Settings.InstanceContainer import InstanceContainer
|
from UM.Settings.InstanceContainer import InstanceContainer
|
||||||
|
|
||||||
from UM.MimeTypeDatabase import MimeTypeNotFoundError
|
|
||||||
from UM.Settings.ContainerFormatError import ContainerFormatError
|
|
||||||
from UM.Settings.ContainerRegistry import ContainerRegistry
|
|
||||||
from cura.Settings.ExtruderManager import ExtruderManager
|
|
||||||
from UM.i18n import i18nCatalog
|
|
||||||
|
|
||||||
catalog = i18nCatalog("cura")
|
catalog = i18nCatalog("cura")
|
||||||
|
|
||||||
|
|
||||||
|
@ -36,11 +29,17 @@ catalog = i18nCatalog("cura")
|
||||||
# from within QML. We want to be able to trigger things like removing a container
|
# from within QML. We want to be able to trigger things like removing a container
|
||||||
# when a certain action happens. This can be done through this class.
|
# when a certain action happens. This can be done through this class.
|
||||||
class ContainerManager(QObject):
|
class ContainerManager(QObject):
|
||||||
def __init__(self, parent = None):
|
|
||||||
super().__init__(parent)
|
|
||||||
|
|
||||||
self._application = Application.getInstance()
|
def __init__(self, application):
|
||||||
self._container_registry = ContainerRegistry.getInstance()
|
if ContainerManager.__instance is not None:
|
||||||
|
raise RuntimeError("Try to create singleton '%s' more than once" % self.__class__.__name__)
|
||||||
|
ContainerManager.__instance = self
|
||||||
|
|
||||||
|
super().__init__(parent = application)
|
||||||
|
|
||||||
|
self._application = application
|
||||||
|
self._plugin_registry = self._application.getPluginRegistry()
|
||||||
|
self._container_registry = self._application.getContainerRegistry()
|
||||||
self._machine_manager = self._application.getMachineManager()
|
self._machine_manager = self._application.getMachineManager()
|
||||||
self._material_manager = self._application.getMaterialManager()
|
self._material_manager = self._application.getMaterialManager()
|
||||||
self._container_name_filters = {}
|
self._container_name_filters = {}
|
||||||
|
@ -129,7 +128,7 @@ class ContainerManager(QObject):
|
||||||
container.setProperty(setting_key, property_name, property_value)
|
container.setProperty(setting_key, property_name, property_value)
|
||||||
|
|
||||||
basefile = container.getMetaDataEntry("base_file", container_id)
|
basefile = container.getMetaDataEntry("base_file", container_id)
|
||||||
for sibbling_container in ContainerRegistry.getInstance().findInstanceContainers(base_file = basefile):
|
for sibbling_container in self._container_registry.findInstanceContainers(base_file = basefile):
|
||||||
if sibbling_container != container:
|
if sibbling_container != container:
|
||||||
sibbling_container.setProperty(setting_key, property_name, property_value)
|
sibbling_container.setProperty(setting_key, property_name, property_value)
|
||||||
|
|
||||||
|
@ -307,13 +306,15 @@ class ContainerManager(QObject):
|
||||||
# \return \type{bool} True if successful, False if not.
|
# \return \type{bool} True if successful, False if not.
|
||||||
@pyqtSlot(result = bool)
|
@pyqtSlot(result = bool)
|
||||||
def updateQualityChanges(self):
|
def updateQualityChanges(self):
|
||||||
global_stack = Application.getInstance().getGlobalContainerStack()
|
global_stack = self._machine_manager.activeMachine
|
||||||
if not global_stack:
|
if not global_stack:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
self._machine_manager.blurSettings.emit()
|
self._machine_manager.blurSettings.emit()
|
||||||
|
|
||||||
for stack in ExtruderManager.getInstance().getActiveGlobalAndExtruderStacks():
|
global_stack = self._machine_manager.activeMachine
|
||||||
|
extruder_stacks = list(global_stack.extruders.values())
|
||||||
|
for stack in [global_stack] + extruder_stacks:
|
||||||
# Find the quality_changes container for this stack and merge the contents of the top container into it.
|
# Find the quality_changes container for this stack and merge the contents of the top container into it.
|
||||||
quality_changes = stack.qualityChanges
|
quality_changes = stack.qualityChanges
|
||||||
if not quality_changes or self._container_registry.isReadOnly(quality_changes.getId()):
|
if not quality_changes or self._container_registry.isReadOnly(quality_changes.getId()):
|
||||||
|
@ -334,13 +335,15 @@ class ContainerManager(QObject):
|
||||||
send_emits_containers = []
|
send_emits_containers = []
|
||||||
|
|
||||||
# Go through global and extruder stacks and clear their topmost container (the user settings).
|
# Go through global and extruder stacks and clear their topmost container (the user settings).
|
||||||
for stack in ExtruderManager.getInstance().getActiveGlobalAndExtruderStacks():
|
global_stack = self._machine_manager.activeMachine
|
||||||
|
extruder_stacks = list(global_stack.extruders.values())
|
||||||
|
for stack in [global_stack] + extruder_stacks:
|
||||||
container = stack.userChanges
|
container = stack.userChanges
|
||||||
container.clear()
|
container.clear()
|
||||||
send_emits_containers.append(container)
|
send_emits_containers.append(container)
|
||||||
|
|
||||||
# user changes are possibly added to make the current setup match the current enabled extruders
|
# user changes are possibly added to make the current setup match the current enabled extruders
|
||||||
Application.getInstance().getMachineManager().correctExtruderSettings()
|
self._machine_manager.correctExtruderSettings()
|
||||||
|
|
||||||
for container in send_emits_containers:
|
for container in send_emits_containers:
|
||||||
container.sendPostponedEmits()
|
container.sendPostponedEmits()
|
||||||
|
@ -381,21 +384,6 @@ class ContainerManager(QObject):
|
||||||
if container is not None:
|
if container is not None:
|
||||||
container.setMetaDataEntry("GUID", new_guid)
|
container.setMetaDataEntry("GUID", new_guid)
|
||||||
|
|
||||||
## Get the singleton instance for this class.
|
|
||||||
@classmethod
|
|
||||||
def getInstance(cls) -> "ContainerManager":
|
|
||||||
# Note: Explicit use of class name to prevent issues with inheritance.
|
|
||||||
if ContainerManager.__instance is None:
|
|
||||||
ContainerManager.__instance = cls()
|
|
||||||
return ContainerManager.__instance
|
|
||||||
|
|
||||||
__instance = None # type: "ContainerManager"
|
|
||||||
|
|
||||||
# Factory function, used by QML
|
|
||||||
@staticmethod
|
|
||||||
def createContainerManager(engine, js_engine):
|
|
||||||
return ContainerManager.getInstance()
|
|
||||||
|
|
||||||
def _performMerge(self, merge_into, merge, clear_settings = True):
|
def _performMerge(self, merge_into, merge, clear_settings = True):
|
||||||
if merge == merge_into:
|
if merge == merge_into:
|
||||||
return
|
return
|
||||||
|
@ -415,7 +403,7 @@ class ContainerManager(QObject):
|
||||||
|
|
||||||
serialize_type = ""
|
serialize_type = ""
|
||||||
try:
|
try:
|
||||||
plugin_metadata = PluginRegistry.getInstance().getMetaData(plugin_id)
|
plugin_metadata = self._plugin_registry.getMetaData(plugin_id)
|
||||||
if plugin_metadata:
|
if plugin_metadata:
|
||||||
serialize_type = plugin_metadata["settings_container"]["type"]
|
serialize_type = plugin_metadata["settings_container"]["type"]
|
||||||
else:
|
else:
|
||||||
|
@ -470,3 +458,9 @@ class ContainerManager(QObject):
|
||||||
|
|
||||||
container_list = [n.getContainer() for n in quality_changes_group.getAllNodes() if n.getContainer() is not None]
|
container_list = [n.getContainer() for n in quality_changes_group.getAllNodes() if n.getContainer() is not None]
|
||||||
self._container_registry.exportQualityProfile(container_list, path, file_type)
|
self._container_registry.exportQualityProfile(container_list, path, file_type)
|
||||||
|
|
||||||
|
__instance = None
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def getInstance(cls, *args, **kwargs) -> "ContainerManager":
|
||||||
|
return cls.__instance
|
||||||
|
|
|
@ -7,7 +7,6 @@ from UM.ConfigurationErrorMessage import ConfigurationErrorMessage
|
||||||
from UM.Logger import Logger
|
from UM.Logger import Logger
|
||||||
from UM.Settings.Interfaces import DefinitionContainerInterface
|
from UM.Settings.Interfaces import DefinitionContainerInterface
|
||||||
from UM.Settings.InstanceContainer import InstanceContainer
|
from UM.Settings.InstanceContainer import InstanceContainer
|
||||||
from UM.Settings.ContainerRegistry import ContainerRegistry
|
|
||||||
|
|
||||||
from cura.Machines.VariantManager import VariantType
|
from cura.Machines.VariantManager import VariantType
|
||||||
from .GlobalStack import GlobalStack
|
from .GlobalStack import GlobalStack
|
||||||
|
@ -29,7 +28,7 @@ class CuraStackBuilder:
|
||||||
variant_manager = application.getVariantManager()
|
variant_manager = application.getVariantManager()
|
||||||
material_manager = application.getMaterialManager()
|
material_manager = application.getMaterialManager()
|
||||||
quality_manager = application.getQualityManager()
|
quality_manager = application.getQualityManager()
|
||||||
registry = ContainerRegistry.getInstance()
|
registry = application.getContainerRegistry()
|
||||||
|
|
||||||
definitions = registry.findDefinitionContainers(id = definition_id)
|
definitions = registry.findDefinitionContainers(id = definition_id)
|
||||||
if not definitions:
|
if not definitions:
|
||||||
|
@ -142,6 +141,7 @@ class CuraStackBuilder:
|
||||||
variant_container, material_container, quality_container, global_stack) -> ExtruderStack:
|
variant_container, material_container, quality_container, global_stack) -> ExtruderStack:
|
||||||
from cura.CuraApplication import CuraApplication
|
from cura.CuraApplication import CuraApplication
|
||||||
application = CuraApplication.getInstance()
|
application = CuraApplication.getInstance()
|
||||||
|
registry = application.getContainerRegistry()
|
||||||
|
|
||||||
stack = ExtruderStack(new_stack_id, parent = global_stack)
|
stack = ExtruderStack(new_stack_id, parent = global_stack)
|
||||||
stack.setName(extruder_definition.getName())
|
stack.setName(extruder_definition.getName())
|
||||||
|
@ -162,7 +162,7 @@ class CuraStackBuilder:
|
||||||
# Only add the created containers to the registry after we have set all the other
|
# Only add the created containers to the registry after we have set all the other
|
||||||
# properties. This makes the create operation more transactional, since any problems
|
# properties. This makes the create operation more transactional, since any problems
|
||||||
# setting properties will not result in incomplete containers being added.
|
# setting properties will not result in incomplete containers being added.
|
||||||
ContainerRegistry.getInstance().addContainer(user_container)
|
registry.addContainer(user_container)
|
||||||
|
|
||||||
return stack
|
return stack
|
||||||
|
|
||||||
|
@ -178,6 +178,7 @@ class CuraStackBuilder:
|
||||||
variant_container, material_container, quality_container) -> GlobalStack:
|
variant_container, material_container, quality_container) -> GlobalStack:
|
||||||
from cura.CuraApplication import CuraApplication
|
from cura.CuraApplication import CuraApplication
|
||||||
application = CuraApplication.getInstance()
|
application = CuraApplication.getInstance()
|
||||||
|
registry = application.getContainerRegistry()
|
||||||
|
|
||||||
stack = GlobalStack(new_stack_id)
|
stack = GlobalStack(new_stack_id)
|
||||||
stack.setDefinition(definition)
|
stack.setDefinition(definition)
|
||||||
|
@ -193,7 +194,7 @@ class CuraStackBuilder:
|
||||||
stack.qualityChanges = application.empty_quality_changes_container
|
stack.qualityChanges = application.empty_quality_changes_container
|
||||||
stack.userChanges = user_container
|
stack.userChanges = user_container
|
||||||
|
|
||||||
ContainerRegistry.getInstance().addContainer(user_container)
|
registry.addContainer(user_container)
|
||||||
|
|
||||||
return stack
|
return stack
|
||||||
|
|
||||||
|
@ -201,8 +202,10 @@ class CuraStackBuilder:
|
||||||
def createUserChangesContainer(cls, container_name: str, definition_id: str, stack_id: str,
|
def createUserChangesContainer(cls, container_name: str, definition_id: str, stack_id: str,
|
||||||
is_global_stack: bool) -> "InstanceContainer":
|
is_global_stack: bool) -> "InstanceContainer":
|
||||||
from cura.CuraApplication import CuraApplication
|
from cura.CuraApplication import CuraApplication
|
||||||
|
application = CuraApplication.getInstance()
|
||||||
|
registry = application.getContainerRegistry()
|
||||||
|
|
||||||
unique_container_name = ContainerRegistry.getInstance().uniqueName(container_name)
|
unique_container_name = registry.uniqueName(container_name)
|
||||||
|
|
||||||
container = InstanceContainer(unique_container_name)
|
container = InstanceContainer(unique_container_name)
|
||||||
container.setDefinition(definition_id)
|
container.setDefinition(definition_id)
|
||||||
|
@ -217,15 +220,17 @@ class CuraStackBuilder:
|
||||||
@classmethod
|
@classmethod
|
||||||
def createDefinitionChangesContainer(cls, container_stack, container_name):
|
def createDefinitionChangesContainer(cls, container_stack, container_name):
|
||||||
from cura.CuraApplication import CuraApplication
|
from cura.CuraApplication import CuraApplication
|
||||||
|
application = CuraApplication.getInstance()
|
||||||
|
registry = application.getContainerRegistry()
|
||||||
|
|
||||||
unique_container_name = ContainerRegistry.getInstance().uniqueName(container_name)
|
unique_container_name = registry.uniqueName(container_name)
|
||||||
|
|
||||||
definition_changes_container = InstanceContainer(unique_container_name)
|
definition_changes_container = InstanceContainer(unique_container_name)
|
||||||
definition_changes_container.setDefinition(container_stack.getBottom().getId())
|
definition_changes_container.setDefinition(container_stack.getBottom().getId())
|
||||||
definition_changes_container.addMetaDataEntry("type", "definition_changes")
|
definition_changes_container.addMetaDataEntry("type", "definition_changes")
|
||||||
definition_changes_container.addMetaDataEntry("setting_version", CuraApplication.SettingVersion)
|
definition_changes_container.addMetaDataEntry("setting_version", CuraApplication.SettingVersion)
|
||||||
|
|
||||||
ContainerRegistry.getInstance().addContainer(definition_changes_container)
|
registry.addContainer(definition_changes_container)
|
||||||
container_stack.definitionChanges = definition_changes_container
|
container_stack.definitionChanges = definition_changes_container
|
||||||
|
|
||||||
return definition_changes_container
|
return definition_changes_container
|
||||||
|
|
|
@ -15,6 +15,7 @@ from UM.Settings.SettingFunction import SettingFunction
|
||||||
from UM.Settings.SettingInstance import SettingInstance
|
from UM.Settings.SettingInstance import SettingInstance
|
||||||
from UM.Settings.ContainerStack import ContainerStack
|
from UM.Settings.ContainerStack import ContainerStack
|
||||||
from UM.Settings.PropertyEvaluationContext import PropertyEvaluationContext
|
from UM.Settings.PropertyEvaluationContext import PropertyEvaluationContext
|
||||||
|
|
||||||
from typing import Optional, List, TYPE_CHECKING, Union
|
from typing import Optional, List, TYPE_CHECKING, Union
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
|
@ -29,6 +30,10 @@ class ExtruderManager(QObject):
|
||||||
|
|
||||||
## Registers listeners and such to listen to changes to the extruders.
|
## Registers listeners and such to listen to changes to the extruders.
|
||||||
def __init__(self, parent = None):
|
def __init__(self, parent = None):
|
||||||
|
if ExtruderManager.__instance is not None:
|
||||||
|
raise RuntimeError("Try to create singleton '%s' more than once" % self.__class__.__name__)
|
||||||
|
ExtruderManager.__instance = self
|
||||||
|
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
|
|
||||||
self._application = Application.getInstance()
|
self._application = Application.getInstance()
|
||||||
|
@ -92,28 +97,6 @@ class ExtruderManager(QObject):
|
||||||
if extruder.getId() == extruder_stack_id:
|
if extruder.getId() == extruder_stack_id:
|
||||||
return extruder.qualityChanges.getId()
|
return extruder.qualityChanges.getId()
|
||||||
|
|
||||||
## The instance of the singleton pattern.
|
|
||||||
#
|
|
||||||
# It's None if the extruder manager hasn't been created yet.
|
|
||||||
__instance = None
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def createExtruderManager():
|
|
||||||
return ExtruderManager().getInstance()
|
|
||||||
|
|
||||||
## Gets an instance of the extruder manager, or creates one if no instance
|
|
||||||
# exists yet.
|
|
||||||
#
|
|
||||||
# This is an implementation of singleton. If an extruder manager already
|
|
||||||
# exists, it is re-used.
|
|
||||||
#
|
|
||||||
# \return The extruder manager.
|
|
||||||
@classmethod
|
|
||||||
def getInstance(cls) -> "ExtruderManager":
|
|
||||||
if not cls.__instance:
|
|
||||||
cls.__instance = ExtruderManager()
|
|
||||||
return cls.__instance
|
|
||||||
|
|
||||||
## Changes the active extruder by index.
|
## Changes the active extruder by index.
|
||||||
#
|
#
|
||||||
# \param index The index of the new active extruder.
|
# \param index The index of the new active extruder.
|
||||||
|
@ -746,3 +729,9 @@ class ExtruderManager(QObject):
|
||||||
resolved_value = global_stack.getProperty(key, "value", context = context)
|
resolved_value = global_stack.getProperty(key, "value", context = context)
|
||||||
|
|
||||||
return resolved_value
|
return resolved_value
|
||||||
|
|
||||||
|
__instance = None
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def getInstance(cls, *args, **kwargs) -> "ExtruderManager":
|
||||||
|
return cls.__instance
|
||||||
|
|
103
cura/SingleInstance.py
Normal file
103
cura/SingleInstance.py
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
# Copyright (c) 2018 Ultimaker B.V.
|
||||||
|
# Cura is released under the terms of the LGPLv3 or higher.
|
||||||
|
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
from typing import List, Optional
|
||||||
|
|
||||||
|
from PyQt5.QtNetwork import QLocalServer, QLocalSocket
|
||||||
|
|
||||||
|
from UM.Logger import Logger
|
||||||
|
|
||||||
|
|
||||||
|
class SingleInstance:
|
||||||
|
|
||||||
|
def __init__(self, application, files_to_open: Optional[List[str]]):
|
||||||
|
self._application = application
|
||||||
|
self._files_to_open = files_to_open
|
||||||
|
|
||||||
|
self._single_instance_server = None
|
||||||
|
|
||||||
|
# Starts a client that checks for a single instance server and sends the files that need to opened if the server
|
||||||
|
# exists. Returns True if the single instance server is found, otherwise False.
|
||||||
|
def startClient(self) -> bool:
|
||||||
|
Logger.log("i", "Checking for the presence of an ready running Cura instance.")
|
||||||
|
single_instance_socket = QLocalSocket(self._application)
|
||||||
|
Logger.log("d", "Full single instance server name: %s", single_instance_socket.fullServerName())
|
||||||
|
single_instance_socket.connectToServer("ultimaker-cura")
|
||||||
|
single_instance_socket.waitForConnected(msecs = 3000) # wait for 3 seconds
|
||||||
|
|
||||||
|
if single_instance_socket.state() != QLocalSocket.ConnectedState:
|
||||||
|
return False
|
||||||
|
|
||||||
|
# We only send the files that need to be opened.
|
||||||
|
if not self._files_to_open:
|
||||||
|
Logger.log("i", "No file need to be opened, do nothing.")
|
||||||
|
return True
|
||||||
|
|
||||||
|
if single_instance_socket.state() == QLocalSocket.ConnectedState:
|
||||||
|
Logger.log("i", "Connection has been made to the single-instance Cura socket.")
|
||||||
|
|
||||||
|
# Protocol is one line of JSON terminated with a carriage return.
|
||||||
|
# "command" field is required and holds the name of the command to execute.
|
||||||
|
# Other fields depend on the command.
|
||||||
|
|
||||||
|
payload = {"command": "clear-all"}
|
||||||
|
single_instance_socket.write(bytes(json.dumps(payload) + "\n", encoding = "ascii"))
|
||||||
|
|
||||||
|
payload = {"command": "focus"}
|
||||||
|
single_instance_socket.write(bytes(json.dumps(payload) + "\n", encoding = "ascii"))
|
||||||
|
|
||||||
|
for filename in self._files_to_open:
|
||||||
|
payload = {"command": "open", "filePath": os.path.abspath(filename)}
|
||||||
|
single_instance_socket.write(bytes(json.dumps(payload) + "\n", encoding = "ascii"))
|
||||||
|
|
||||||
|
payload = {"command": "close-connection"}
|
||||||
|
single_instance_socket.write(bytes(json.dumps(payload) + "\n", encoding = "ascii"))
|
||||||
|
|
||||||
|
single_instance_socket.flush()
|
||||||
|
single_instance_socket.waitForDisconnected()
|
||||||
|
return True
|
||||||
|
|
||||||
|
def startServer(self) -> None:
|
||||||
|
self._single_instance_server = QLocalServer()
|
||||||
|
self._single_instance_server.newConnection.connect(self._onClientConnected)
|
||||||
|
self._single_instance_server.listen("ultimaker-cura")
|
||||||
|
|
||||||
|
def _onClientConnected(self):
|
||||||
|
Logger.log("i", "New connection recevied on our single-instance server")
|
||||||
|
connection = self._single_instance_server.nextPendingConnection()
|
||||||
|
|
||||||
|
if connection is not None:
|
||||||
|
connection.readyRead.connect(lambda c = connection: self.__readCommands(c))
|
||||||
|
|
||||||
|
def __readCommands(self, connection):
|
||||||
|
line = connection.readLine()
|
||||||
|
while len(line) != 0: # There is also a .canReadLine()
|
||||||
|
try:
|
||||||
|
payload = json.loads(str(line, encoding = "ascii").strip())
|
||||||
|
command = payload["command"]
|
||||||
|
|
||||||
|
# Command: Remove all models from the build plate.
|
||||||
|
if command == "clear-all":
|
||||||
|
self._application.callLater(lambda: self._application.deleteAll())
|
||||||
|
|
||||||
|
# Command: Load a model file
|
||||||
|
elif command == "open":
|
||||||
|
self._application.callLater(lambda f = payload["filePath"]: self._application._openFile(f))
|
||||||
|
|
||||||
|
# Command: Activate the window and bring it to the top.
|
||||||
|
elif command == "focus":
|
||||||
|
# Operating systems these days prevent windows from moving around by themselves.
|
||||||
|
# 'alert' or flashing the icon in the taskbar is the best thing we do now.
|
||||||
|
self._application.callLater(lambda: self._application.getMainWindow().alert(0))
|
||||||
|
|
||||||
|
# Command: Close the socket connection. We're done.
|
||||||
|
elif command == "close-connection":
|
||||||
|
connection.close()
|
||||||
|
|
||||||
|
else:
|
||||||
|
Logger.log("w", "Received an unrecognized command " + str(command))
|
||||||
|
except json.decoder.JSONDecodeError as ex:
|
||||||
|
Logger.log("w", "Unable to parse JSON command '%s': %s", line, repr(ex))
|
||||||
|
line = connection.readLine()
|
64
cura_app.py
64
cura_app.py
|
@ -1,31 +1,17 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
# Copyright (c) 2015 Ultimaker B.V.
|
# Copyright (c) 2018 Ultimaker B.V.
|
||||||
# Cura is released under the terms of the LGPLv3 or higher.
|
# Cura is released under the terms of the LGPLv3 or higher.
|
||||||
|
|
||||||
import argparse
|
import faulthandler
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from UM.Platform import Platform
|
from UM.Platform import Platform
|
||||||
|
|
||||||
parser = argparse.ArgumentParser(prog = "cura",
|
|
||||||
add_help = False)
|
|
||||||
parser.add_argument('--debug',
|
|
||||||
action='store_true',
|
|
||||||
default = False,
|
|
||||||
help = "Turn on the debug mode by setting this option."
|
|
||||||
)
|
|
||||||
parser.add_argument('--trigger-early-crash',
|
|
||||||
dest = 'trigger_early_crash',
|
|
||||||
action = 'store_true',
|
|
||||||
default = False,
|
|
||||||
help = "FOR TESTING ONLY. Trigger an early crash to show the crash dialog."
|
|
||||||
)
|
|
||||||
known_args = vars(parser.parse_known_args()[0])
|
|
||||||
|
|
||||||
if not known_args["debug"]:
|
# Gets the directory for stdout and stderr
|
||||||
def get_cura_dir_path():
|
def get_cura_dir_for_stdoutputs() -> str:
|
||||||
if Platform.isWindows():
|
if Platform.isWindows():
|
||||||
return os.path.expanduser("~/AppData/Roaming/cura/")
|
return os.path.expanduser("~/AppData/Roaming/cura/")
|
||||||
elif Platform.isLinux():
|
elif Platform.isLinux():
|
||||||
|
@ -33,19 +19,18 @@ if not known_args["debug"]:
|
||||||
elif Platform.isOSX():
|
elif Platform.isOSX():
|
||||||
return os.path.expanduser("~/Library/Logs/cura")
|
return os.path.expanduser("~/Library/Logs/cura")
|
||||||
|
|
||||||
if hasattr(sys, "frozen"):
|
|
||||||
dirpath = get_cura_dir_path()
|
|
||||||
os.makedirs(dirpath, exist_ok = True)
|
|
||||||
sys.stdout = open(os.path.join(dirpath, "stdout.log"), "w", encoding = "utf-8")
|
|
||||||
sys.stderr = open(os.path.join(dirpath, "stderr.log"), "w", encoding = "utf-8")
|
|
||||||
|
|
||||||
import platform
|
# Change stdout and stderr to files if Cura is running as a packaged application.
|
||||||
import faulthandler
|
if hasattr(sys, "frozen"):
|
||||||
|
dir_path = get_cura_dir_for_stdoutputs()
|
||||||
|
os.makedirs(dir_path, exist_ok = True)
|
||||||
|
sys.stdout = open(os.path.join(dir_path, "stdout.log"), "w", encoding = "utf-8")
|
||||||
|
sys.stderr = open(os.path.join(dir_path, "stderr.log"), "w", encoding = "utf-8")
|
||||||
|
|
||||||
#WORKAROUND: GITHUB-88 GITHUB-385 GITHUB-612
|
|
||||||
|
# WORKAROUND: GITHUB-88 GITHUB-385 GITHUB-612
|
||||||
if Platform.isLinux(): # Needed for platform.linux_distribution, which is not available on Windows and OSX
|
if Platform.isLinux(): # Needed for platform.linux_distribution, which is not available on Windows and OSX
|
||||||
# For Ubuntu: https://bugs.launchpad.net/ubuntu/+source/python-qt4/+bug/941826
|
# For Ubuntu: https://bugs.launchpad.net/ubuntu/+source/python-qt4/+bug/941826
|
||||||
linux_distro_name = platform.linux_distribution()[0].lower()
|
|
||||||
# The workaround is only needed on Ubuntu+NVidia drivers. Other drivers are not affected, but fine with this fix.
|
# The workaround is only needed on Ubuntu+NVidia drivers. Other drivers are not affected, but fine with this fix.
|
||||||
try:
|
try:
|
||||||
import ctypes
|
import ctypes
|
||||||
|
@ -79,6 +64,7 @@ if "PYTHONPATH" in os.environ.keys(): # If PYTHONPATH is u
|
||||||
sys.path.remove(PATH_real)
|
sys.path.remove(PATH_real)
|
||||||
sys.path.insert(1, PATH_real) # Insert it at 1 after os.curdir, which is 0.
|
sys.path.insert(1, PATH_real) # Insert it at 1 after os.curdir, which is 0.
|
||||||
|
|
||||||
|
|
||||||
def exceptHook(hook_type, value, traceback):
|
def exceptHook(hook_type, value, traceback):
|
||||||
from cura.CrashHandler import CrashHandler
|
from cura.CrashHandler import CrashHandler
|
||||||
from cura.CuraApplication import CuraApplication
|
from cura.CuraApplication import CuraApplication
|
||||||
|
@ -121,25 +107,25 @@ def exceptHook(hook_type, value, traceback):
|
||||||
_crash_handler.early_crash_dialog.show()
|
_crash_handler.early_crash_dialog.show()
|
||||||
sys.exit(application.exec_())
|
sys.exit(application.exec_())
|
||||||
|
|
||||||
if not known_args["debug"]:
|
|
||||||
sys.excepthook = exceptHook
|
# Set exception hook to use the crash dialog handler
|
||||||
|
sys.excepthook = exceptHook
|
||||||
|
# Enable dumping traceback for all threads
|
||||||
|
faulthandler.enable(all_threads = True)
|
||||||
|
|
||||||
# Workaround for a race condition on certain systems where there
|
# Workaround for a race condition on certain systems where there
|
||||||
# is a race condition between Arcus and PyQt. Importing Arcus
|
# is a race condition between Arcus and PyQt. Importing Arcus
|
||||||
# first seems to prevent Sip from going into a state where it
|
# first seems to prevent Sip from going into a state where it
|
||||||
# tries to create PyQt objects on a non-main thread.
|
# tries to create PyQt objects on a non-main thread.
|
||||||
import Arcus #@UnusedImport
|
import Arcus #@UnusedImport
|
||||||
import cura.CuraApplication
|
from cura.CuraApplication import CuraApplication
|
||||||
import cura.Settings.CuraContainerRegistry
|
|
||||||
|
|
||||||
faulthandler.enable()
|
app = CuraApplication()
|
||||||
|
app.addCommandLineOptions()
|
||||||
|
app.parseCliOptions()
|
||||||
|
app.initialize()
|
||||||
|
|
||||||
# Force an instance of CuraContainerRegistry to be created and reused later.
|
app.startSlashWindowPhase()
|
||||||
cura.Settings.CuraContainerRegistry.CuraContainerRegistry.getInstance()
|
app.startPostSlashWindowPhase()
|
||||||
|
|
||||||
# This pre-start up check is needed to determine if we should start the application at all.
|
|
||||||
if not cura.CuraApplication.CuraApplication.preStartUp(parser = parser, parsed_command_line = known_args):
|
|
||||||
sys.exit(0)
|
|
||||||
|
|
||||||
app = cura.CuraApplication.CuraApplication.getInstance(parser = parser, parsed_command_line = known_args)
|
|
||||||
app.run()
|
app.run()
|
||||||
|
|
|
@ -275,7 +275,7 @@ class CuraEngineBackend(QObject, Backend):
|
||||||
self.processingProgress.emit(0)
|
self.processingProgress.emit(0)
|
||||||
Logger.log("d", "Attempting to kill the engine process")
|
Logger.log("d", "Attempting to kill the engine process")
|
||||||
|
|
||||||
if Application.getInstance().getCommandLineOption("external-backend", False):
|
if Application.getInstance().getUseExternalBackend():
|
||||||
return
|
return
|
||||||
|
|
||||||
if self._process is not None:
|
if self._process is not None:
|
||||||
|
|
|
@ -1,10 +1,16 @@
|
||||||
# Copyright (c) 2017 Ultimaker B.V.
|
# Copyright (c) 2018 Ultimaker B.V.
|
||||||
# Cura is released under the terms of the LGPLv3 or higher.
|
# Cura is released under the terms of the LGPLv3 or higher.
|
||||||
|
|
||||||
from UM.Signal import Signal, signalemitter
|
import threading
|
||||||
from UM.Application import Application
|
import platform
|
||||||
from UM.Resources import Resources
|
import time
|
||||||
|
import serial.tools.list_ports
|
||||||
|
|
||||||
|
from PyQt5.QtCore import QObject, pyqtSlot, pyqtProperty, pyqtSignal
|
||||||
|
|
||||||
from UM.Logger import Logger
|
from UM.Logger import Logger
|
||||||
|
from UM.Resources import Resources
|
||||||
|
from UM.Signal import Signal, signalemitter
|
||||||
from UM.OutputDevice.OutputDevicePlugin import OutputDevicePlugin
|
from UM.OutputDevice.OutputDevicePlugin import OutputDevicePlugin
|
||||||
from UM.i18n import i18nCatalog
|
from UM.i18n import i18nCatalog
|
||||||
|
|
||||||
|
@ -12,12 +18,6 @@ from cura.PrinterOutputDevice import ConnectionState
|
||||||
from cura.CuraApplication import CuraApplication
|
from cura.CuraApplication import CuraApplication
|
||||||
|
|
||||||
from . import USBPrinterOutputDevice
|
from . import USBPrinterOutputDevice
|
||||||
from PyQt5.QtCore import QObject, pyqtSlot, pyqtProperty, pyqtSignal
|
|
||||||
|
|
||||||
import threading
|
|
||||||
import platform
|
|
||||||
import time
|
|
||||||
import serial.tools.list_ports
|
|
||||||
|
|
||||||
i18n_catalog = i18nCatalog("cura")
|
i18n_catalog = i18nCatalog("cura")
|
||||||
|
|
||||||
|
@ -28,8 +28,14 @@ class USBPrinterOutputDeviceManager(QObject, OutputDevicePlugin):
|
||||||
addUSBOutputDeviceSignal = Signal()
|
addUSBOutputDeviceSignal = Signal()
|
||||||
progressChanged = pyqtSignal()
|
progressChanged = pyqtSignal()
|
||||||
|
|
||||||
def __init__(self, parent = None):
|
def __init__(self, application, parent = None):
|
||||||
|
if USBPrinterOutputDeviceManager.__instance is not None:
|
||||||
|
raise RuntimeError("Try to create singleton '%s' more than once" % self.__class__.__name__)
|
||||||
|
USBPrinterOutputDeviceManager.__instance = self
|
||||||
|
|
||||||
super().__init__(parent = parent)
|
super().__init__(parent = parent)
|
||||||
|
self._application = application
|
||||||
|
|
||||||
self._serial_port_list = []
|
self._serial_port_list = []
|
||||||
self._usb_output_devices = {}
|
self._usb_output_devices = {}
|
||||||
self._usb_output_devices_model = None
|
self._usb_output_devices_model = None
|
||||||
|
@ -38,11 +44,11 @@ class USBPrinterOutputDeviceManager(QObject, OutputDevicePlugin):
|
||||||
|
|
||||||
self._check_updates = True
|
self._check_updates = True
|
||||||
|
|
||||||
Application.getInstance().applicationShuttingDown.connect(self.stop)
|
self._application.applicationShuttingDown.connect(self.stop)
|
||||||
# Because the model needs to be created in the same thread as the QMLEngine, we use a signal.
|
# Because the model needs to be created in the same thread as the QMLEngine, we use a signal.
|
||||||
self.addUSBOutputDeviceSignal.connect(self.addOutputDevice)
|
self.addUSBOutputDeviceSignal.connect(self.addOutputDevice)
|
||||||
|
|
||||||
Application.getInstance().globalContainerStackChanged.connect(self.updateUSBPrinterOutputDevices)
|
self._application.globalContainerStackChanged.connect(self.updateUSBPrinterOutputDevices)
|
||||||
|
|
||||||
# The method updates/reset the USB settings for all connected USB devices
|
# The method updates/reset the USB settings for all connected USB devices
|
||||||
def updateUSBPrinterOutputDevices(self):
|
def updateUSBPrinterOutputDevices(self):
|
||||||
|
@ -69,7 +75,7 @@ class USBPrinterOutputDeviceManager(QObject, OutputDevicePlugin):
|
||||||
|
|
||||||
def _updateThread(self):
|
def _updateThread(self):
|
||||||
while self._check_updates:
|
while self._check_updates:
|
||||||
container_stack = Application.getInstance().getGlobalContainerStack()
|
container_stack = self._application.getGlobalContainerStack()
|
||||||
if container_stack is None:
|
if container_stack is None:
|
||||||
time.sleep(5)
|
time.sleep(5)
|
||||||
continue
|
continue
|
||||||
|
@ -80,19 +86,10 @@ class USBPrinterOutputDeviceManager(QObject, OutputDevicePlugin):
|
||||||
self._addRemovePorts(port_list)
|
self._addRemovePorts(port_list)
|
||||||
time.sleep(5)
|
time.sleep(5)
|
||||||
|
|
||||||
## Return the singleton instance of the USBPrinterManager
|
|
||||||
@classmethod
|
|
||||||
def getInstance(cls, engine = None, script_engine = None):
|
|
||||||
# Note: Explicit use of class name to prevent issues with inheritance.
|
|
||||||
if USBPrinterOutputDeviceManager._instance is None:
|
|
||||||
USBPrinterOutputDeviceManager._instance = cls()
|
|
||||||
|
|
||||||
return USBPrinterOutputDeviceManager._instance
|
|
||||||
|
|
||||||
@pyqtSlot(result = str)
|
@pyqtSlot(result = str)
|
||||||
def getDefaultFirmwareName(self):
|
def getDefaultFirmwareName(self):
|
||||||
# Check if there is a valid global container stack
|
# Check if there is a valid global container stack
|
||||||
global_container_stack = Application.getInstance().getGlobalContainerStack()
|
global_container_stack = self._application.getGlobalContainerStack()
|
||||||
if not global_container_stack:
|
if not global_container_stack:
|
||||||
Logger.log("e", "There is no global container stack. Can not update firmware.")
|
Logger.log("e", "There is no global container stack. Can not update firmware.")
|
||||||
self._firmware_view.close()
|
self._firmware_view.close()
|
||||||
|
@ -181,4 +178,8 @@ class USBPrinterOutputDeviceManager(QObject, OutputDevicePlugin):
|
||||||
|
|
||||||
return list(base_list)
|
return list(base_list)
|
||||||
|
|
||||||
_instance = None # type: "USBPrinterOutputDeviceManager"
|
__instance = None
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def getInstance(cls, *args, **kwargs) -> "USBPrinterOutputDeviceManager":
|
||||||
|
return cls.__instance
|
||||||
|
|
|
@ -15,4 +15,4 @@ def register(app):
|
||||||
# We are violating the QT API here (as we use a factory, which is technically not allowed).
|
# We are violating the QT API here (as we use a factory, which is technically not allowed).
|
||||||
# but we don't really have another means for doing this (and it seems to you know -work-)
|
# but we don't really have another means for doing this (and it seems to you know -work-)
|
||||||
qmlRegisterSingletonType(USBPrinterOutputDeviceManager.USBPrinterOutputDeviceManager, "Cura", 1, 0, "USBPrinterManager", USBPrinterOutputDeviceManager.USBPrinterOutputDeviceManager.getInstance)
|
qmlRegisterSingletonType(USBPrinterOutputDeviceManager.USBPrinterOutputDeviceManager, "Cura", 1, 0, "USBPrinterManager", USBPrinterOutputDeviceManager.USBPrinterOutputDeviceManager.getInstance)
|
||||||
return {"output_device": USBPrinterOutputDeviceManager.USBPrinterOutputDeviceManager.getInstance()}
|
return {"output_device": USBPrinterOutputDeviceManager.USBPrinterOutputDeviceManager(app)}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue