mirror of
https://github.com/Ultimaker/Cura.git
synced 2025-07-06 22:47:29 -06:00
CURA-5402 hopefully solved merge conflicts
This commit is contained in:
commit
96f2e6e1ed
96 changed files with 1612 additions and 838 deletions
|
@ -19,7 +19,10 @@ endif()
|
|||
|
||||
set(CURA_VERSION "master" CACHE STRING "Version name of Cura")
|
||||
set(CURA_BUILDTYPE "" CACHE STRING "Build type of Cura, eg. 'PPA'")
|
||||
set(CURA_PACKAGES_VERSION "" CACHE STRING "Packages version of Cura")
|
||||
set(CURA_SDK_VERSION "" CACHE STRING "SDK version of Cura")
|
||||
set(CURA_CLOUD_API_ROOT "" CACHE STRING "Alternative Cura cloud API root")
|
||||
set(CURA_CLOUD_API_VERSION "" CACHE STRING "Alternative Cura cloud API version")
|
||||
|
||||
configure_file(${CMAKE_SOURCE_DIR}/cura.desktop.in ${CMAKE_BINARY_DIR}/cura.desktop @ONLY)
|
||||
configure_file(cura/CuraVersion.py.in CuraVersion.py @ONLY)
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
|
||||
from UM.Logger import Logger
|
||||
from UM.Math.Polygon import Polygon
|
||||
from UM.Math.Vector import Vector
|
||||
from cura.Arranging.ShapeArray import ShapeArray
|
||||
from cura.Scene import ZOffsetDecorator
|
||||
|
@ -45,7 +46,7 @@ class Arrange:
|
|||
# \param scene_root Root for finding all scene nodes
|
||||
# \param fixed_nodes Scene nodes to be placed
|
||||
@classmethod
|
||||
def create(cls, scene_root = None, fixed_nodes = None, scale = 0.5, x = 350, y = 250):
|
||||
def create(cls, scene_root = None, fixed_nodes = None, scale = 0.5, x = 350, y = 250, min_offset = 8):
|
||||
arranger = Arrange(x, y, x // 2, y // 2, scale = scale)
|
||||
arranger.centerFirst()
|
||||
|
||||
|
@ -58,9 +59,10 @@ class Arrange:
|
|||
|
||||
# Place all objects fixed nodes
|
||||
for fixed_node in fixed_nodes:
|
||||
vertices = fixed_node.callDecoration("getConvexHull")
|
||||
vertices = fixed_node.callDecoration("getConvexHullHead") or fixed_node.callDecoration("getConvexHull")
|
||||
if not vertices:
|
||||
continue
|
||||
vertices = vertices.getMinkowskiHull(Polygon.approximatedCircle(min_offset))
|
||||
points = copy.deepcopy(vertices._points)
|
||||
shape_arr = ShapeArray.fromPolygon(points, scale = scale)
|
||||
arranger.place(0, 0, shape_arr)
|
||||
|
@ -81,12 +83,12 @@ class Arrange:
|
|||
## Find placement for a node (using offset shape) and place it (using hull shape)
|
||||
# return the nodes that should be placed
|
||||
# \param node
|
||||
# \param offset_shape_arr ShapeArray with offset, used to find location
|
||||
# \param hull_shape_arr ShapeArray without offset, for placing the shape
|
||||
# \param offset_shape_arr ShapeArray with offset, for placing the shape
|
||||
# \param hull_shape_arr ShapeArray without offset, used to find location
|
||||
def findNodePlacement(self, node, offset_shape_arr, hull_shape_arr, step = 1):
|
||||
new_node = copy.deepcopy(node)
|
||||
best_spot = self.bestSpot(
|
||||
offset_shape_arr, start_prio = self._last_priority, step = step)
|
||||
hull_shape_arr, start_prio = self._last_priority, step = step)
|
||||
x, y = best_spot.x, best_spot.y
|
||||
|
||||
# Save the last priority.
|
||||
|
@ -102,7 +104,7 @@ class Arrange:
|
|||
if x is not None: # We could find a place
|
||||
new_node.setPosition(Vector(x, center_y, y))
|
||||
found_spot = True
|
||||
self.place(x, y, hull_shape_arr) # place the object in arranger
|
||||
self.place(x, y, offset_shape_arr) # place the object in arranger
|
||||
else:
|
||||
Logger.log("d", "Could not find spot!"),
|
||||
found_spot = False
|
||||
|
|
|
@ -110,7 +110,7 @@ class ArrangeObjectsAllBuildPlatesJob(Job):
|
|||
arrange_array.add()
|
||||
arranger = arrange_array.get(current_build_plate_number)
|
||||
|
||||
best_spot = arranger.bestSpot(offset_shape_arr, start_prio=start_priority)
|
||||
best_spot = arranger.bestSpot(hull_shape_arr, start_prio=start_priority)
|
||||
x, y = best_spot.x, best_spot.y
|
||||
node.removeDecorator(ZOffsetDecorator)
|
||||
if node.getBoundingBox():
|
||||
|
@ -118,7 +118,7 @@ class ArrangeObjectsAllBuildPlatesJob(Job):
|
|||
else:
|
||||
center_y = 0
|
||||
if x is not None: # We could find a place
|
||||
arranger.place(x, y, hull_shape_arr) # place the object in the arranger
|
||||
arranger.place(x, y, offset_shape_arr) # place the object in the arranger
|
||||
|
||||
node.callDecoration("setBuildPlateNumber", current_build_plate_number)
|
||||
grouped_operation.addOperation(TranslateOperation(node, Vector(x, center_y, y), set_position = True))
|
||||
|
|
|
@ -37,12 +37,15 @@ class ArrangeObjectsJob(Job):
|
|||
machine_width = global_container_stack.getProperty("machine_width", "value")
|
||||
machine_depth = global_container_stack.getProperty("machine_depth", "value")
|
||||
|
||||
arranger = Arrange.create(x = machine_width, y = machine_depth, fixed_nodes = self._fixed_nodes)
|
||||
arranger = Arrange.create(x = machine_width, y = machine_depth, fixed_nodes = self._fixed_nodes, min_offset = self._min_offset)
|
||||
|
||||
# Collect nodes to be placed
|
||||
nodes_arr = [] # fill with (size, node, offset_shape_arr, hull_shape_arr)
|
||||
for node in self._nodes:
|
||||
offset_shape_arr, hull_shape_arr = ShapeArray.fromNode(node, min_offset = self._min_offset)
|
||||
if offset_shape_arr is None:
|
||||
Logger.log("w", "Node [%s] could not be converted to an array for arranging...", str(node))
|
||||
continue
|
||||
nodes_arr.append((offset_shape_arr.arr.shape[0] * offset_shape_arr.arr.shape[1], node, offset_shape_arr, hull_shape_arr))
|
||||
|
||||
# Sort the nodes with the biggest area first.
|
||||
|
@ -63,7 +66,7 @@ class ArrangeObjectsJob(Job):
|
|||
start_priority = last_priority
|
||||
else:
|
||||
start_priority = 0
|
||||
best_spot = arranger.bestSpot(offset_shape_arr, start_prio=start_priority)
|
||||
best_spot = arranger.bestSpot(hull_shape_arr, start_prio = start_priority)
|
||||
x, y = best_spot.x, best_spot.y
|
||||
node.removeDecorator(ZOffsetDecorator)
|
||||
if node.getBoundingBox():
|
||||
|
@ -74,7 +77,7 @@ class ArrangeObjectsJob(Job):
|
|||
last_size = size
|
||||
last_priority = best_spot.priority
|
||||
|
||||
arranger.place(x, y, hull_shape_arr) # take place before the next one
|
||||
arranger.place(x, y, offset_shape_arr) # take place before the next one
|
||||
grouped_operation.addOperation(TranslateOperation(node, Vector(x, center_y, y), set_position = True))
|
||||
else:
|
||||
Logger.log("d", "Arrange all: could not find spot!")
|
||||
|
|
|
@ -3,21 +3,20 @@
|
|||
|
||||
from PyQt5.QtCore import QTimer
|
||||
|
||||
from UM.Preferences import Preferences
|
||||
from UM.Logger import Logger
|
||||
|
||||
|
||||
class AutoSave:
|
||||
def __init__(self, application):
|
||||
self._application = application
|
||||
Preferences.getInstance().preferenceChanged.connect(self._triggerTimer)
|
||||
self._application.getPreferences().preferenceChanged.connect(self._triggerTimer)
|
||||
|
||||
self._global_stack = None
|
||||
|
||||
Preferences.getInstance().addPreference("cura/autosave_delay", 1000 * 10)
|
||||
self._application.getPreferences().addPreference("cura/autosave_delay", 1000 * 10)
|
||||
|
||||
self._change_timer = QTimer()
|
||||
self._change_timer.setInterval(Preferences.getInstance().getValue("cura/autosave_delay"))
|
||||
self._change_timer.setInterval(self._application.getPreferences().getValue("cura/autosave_delay"))
|
||||
self._change_timer.setSingleShot(True)
|
||||
|
||||
self._saving = False
|
||||
|
|
|
@ -3,12 +3,10 @@
|
|||
|
||||
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.Scene.Platform import Platform
|
||||
from UM.Scene.Iterator.BreadthFirstIterator import BreadthFirstIterator
|
||||
from UM.Scene.SceneNode import SceneNode
|
||||
from UM.Application import Application
|
||||
from UM.Resources import Resources
|
||||
from UM.Mesh.MeshBuilder import MeshBuilder
|
||||
from UM.Math.Vector import Vector
|
||||
|
@ -37,8 +35,10 @@ PRIME_CLEARANCE = 6.5
|
|||
class BuildVolume(SceneNode):
|
||||
raftThicknessChanged = Signal()
|
||||
|
||||
def __init__(self, parent = None):
|
||||
def __init__(self, application, parent = None):
|
||||
super().__init__(parent)
|
||||
self._application = application
|
||||
self._machine_manager = self._application.getMachineManager()
|
||||
|
||||
self._volume_outline_color = None
|
||||
self._x_axis_color = None
|
||||
|
@ -82,14 +82,14 @@ class BuildVolume(SceneNode):
|
|||
" with printed models."), title = catalog.i18nc("@info:title", "Build Volume"))
|
||||
|
||||
self._global_container_stack = None
|
||||
Application.getInstance().globalContainerStackChanged.connect(self._onStackChanged)
|
||||
self._application.globalContainerStackChanged.connect(self._onStackChanged)
|
||||
self._onStackChanged()
|
||||
|
||||
self._engine_ready = False
|
||||
Application.getInstance().engineCreatedSignal.connect(self._onEngineCreated)
|
||||
self._application.engineCreatedSignal.connect(self._onEngineCreated)
|
||||
|
||||
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.
|
||||
self._scene_objects = set()
|
||||
|
@ -107,14 +107,14 @@ class BuildVolume(SceneNode):
|
|||
# Must be after setting _build_volume_message, apparently that is used in getMachineManager.
|
||||
# activeQualityChanged is always emitted after setActiveVariant, setActiveMaterial and setActiveQuality.
|
||||
# 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,
|
||||
# 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
|
||||
Application.getInstance().getMachineManager().extruderChanged.connect(self.updateNodeBoundaryCheck)
|
||||
self._machine_manager.extruderChanged.connect(self.updateNodeBoundaryCheck)
|
||||
|
||||
# list of settings which were updated
|
||||
self._changed_settings_since_last_rebuild = []
|
||||
|
@ -124,7 +124,7 @@ class BuildVolume(SceneNode):
|
|||
self._scene_change_timer.start()
|
||||
|
||||
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"))
|
||||
if new_scene_objects != self._scene_objects:
|
||||
for node in new_scene_objects - self._scene_objects: #Nodes that were added to the scene.
|
||||
|
@ -186,7 +186,7 @@ class BuildVolume(SceneNode):
|
|||
if not self._shader:
|
||||
self._shader = OpenGL.getInstance().createShaderProgram(Resources.getPath(Resources.Shaders, "default.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_gridColor0", Color(*theme.getColor("buildplate_grid").getRgb()))
|
||||
self._grid_shader.setUniformValue("u_gridColor1", Color(*theme.getColor("buildplate_grid_minor").getRgb()))
|
||||
|
@ -206,7 +206,7 @@ class BuildVolume(SceneNode):
|
|||
## For every sliceable node, update node._outside_buildarea
|
||||
#
|
||||
def updateNodeBoundaryCheck(self):
|
||||
root = Application.getInstance().getController().getScene().getRoot()
|
||||
root = self._application.getController().getScene().getRoot()
|
||||
nodes = list(BreadthFirstIterator(root))
|
||||
group_nodes = []
|
||||
|
||||
|
@ -294,11 +294,11 @@ class BuildVolume(SceneNode):
|
|||
if not self._width or not self._height or not self._depth:
|
||||
return
|
||||
|
||||
if not Application.getInstance()._engine:
|
||||
if not self._application._qml_engine:
|
||||
return
|
||||
|
||||
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._x_axis_color = Color(*theme.getColor("x_axis").getRgb())
|
||||
self._y_axis_color = Color(*theme.getColor("y_axis").getRgb())
|
||||
|
@ -470,7 +470,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)
|
||||
)
|
||||
|
||||
Application.getInstance().getController().getScene()._maximum_bounds = scale_to_max_bounds
|
||||
self._application.getController().getScene()._maximum_bounds = scale_to_max_bounds
|
||||
|
||||
self.updateNodeBoundaryCheck()
|
||||
|
||||
|
@ -523,7 +523,7 @@ class BuildVolume(SceneNode):
|
|||
for extruder in extruders:
|
||||
extruder.propertyChanged.disconnect(self._onSettingPropertyChanged)
|
||||
|
||||
self._global_container_stack = Application.getInstance().getGlobalContainerStack()
|
||||
self._global_container_stack = self._application.getGlobalContainerStack()
|
||||
|
||||
if self._global_container_stack:
|
||||
self._global_container_stack.propertyChanged.connect(self._onSettingPropertyChanged)
|
||||
|
@ -566,7 +566,7 @@ class BuildVolume(SceneNode):
|
|||
|
||||
if setting_key == "print_sequence":
|
||||
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)
|
||||
if self._height < machine_height:
|
||||
self._build_volume_message.show()
|
||||
|
|
|
@ -12,7 +12,6 @@ from UM.Scene.Selection import Selection
|
|||
from UM.Scene.Iterator.BreadthFirstIterator import BreadthFirstIterator
|
||||
from UM.Operations.GroupedOperation import GroupedOperation
|
||||
from UM.Operations.RemoveSceneNodeOperation import RemoveSceneNodeOperation
|
||||
from UM.Operations.SetTransformOperation import SetTransformOperation
|
||||
from UM.Operations.TranslateOperation import TranslateOperation
|
||||
|
||||
from cura.Operations.SetParentOperation import SetParentOperation
|
||||
|
|
|
@ -1,9 +1,18 @@
|
|||
# Copyright (c) 2018 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
from PyQt5.QtCore import QObject, QTimer
|
||||
from PyQt5.QtNetwork import QLocalServer
|
||||
from PyQt5.QtNetwork import QLocalSocket
|
||||
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.QtGui import QColor, QIcon
|
||||
from PyQt5.QtWidgets import QMessageBox
|
||||
from PyQt5.QtQml import qmlRegisterUncreatableType, qmlRegisterSingletonType, qmlRegisterType
|
||||
|
||||
from UM.Qt.QtApplication import QtApplication
|
||||
from UM.Scene.SceneNode import SceneNode
|
||||
|
@ -74,6 +83,7 @@ from cura.Settings.SimpleModeSettingsManager import SimpleModeSettingsManager
|
|||
|
||||
from cura.Machines.VariantManager import VariantManager
|
||||
|
||||
from .SingleInstance import SingleInstance
|
||||
from .AutoSave import AutoSave
|
||||
from . import PlatformPhysics
|
||||
from . import BuildVolume
|
||||
|
@ -94,22 +104,10 @@ from cura.Settings.ContainerManager import ContainerManager
|
|||
|
||||
from cura.ObjectsModel import ObjectsModel
|
||||
|
||||
from PyQt5.QtCore import QUrl, pyqtSignal, pyqtProperty, QEvent, Q_ENUMS
|
||||
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
|
||||
if not MYPY:
|
||||
|
@ -144,20 +142,161 @@ class CuraApplication(QtApplication):
|
|||
|
||||
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()
|
||||
|
||||
# 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._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
|
||||
|
||||
# Backups
|
||||
self._auto_save = None
|
||||
self._save_data_enabled = 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:
|
||||
self.__addExpectedResourceDirsAndSearchPaths() # Must be added before init of super
|
||||
|
||||
super().initialize()
|
||||
|
||||
self.__sendCommandToSingleInstance()
|
||||
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
|
||||
for dir_name in ["extruders", "machine_instances", "materials", "plugins", "quality", "quality_changes", "user", "variants"]:
|
||||
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"):
|
||||
resource_path = os.path.join(os.path.abspath(os.path.dirname(__file__)), "..", "resources")
|
||||
Resources.addSearchPath(resource_path)
|
||||
|
||||
self._use_gui = True
|
||||
self._open_file_queue = [] # Files to open when plug-ins are loaded.
|
||||
|
||||
# Adds custom property types, settings types, and extra operators (functions) that need to be registered in
|
||||
# SettingDefinition and SettingFunction.
|
||||
def __initializeSettingDefinitionsAndFunctions(self):
|
||||
# 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_extruder", DefinitionPropertyType.Any, default = True, read_only = True)
|
||||
|
@ -181,8 +320,10 @@ class CuraApplication(QtApplication):
|
|||
SettingFunction.registerOperator("extruderValues", ExtruderManager.getExtruderValues)
|
||||
SettingFunction.registerOperator("extruderValue", ExtruderManager.getExtruderValue)
|
||||
SettingFunction.registerOperator("resolveOrValue", ExtruderManager.getResolveOrValue)
|
||||
SettingFunction.registerOperator("defaultExtruderPosition", ExtruderManager.getDefaultExtruderPosition)
|
||||
|
||||
## 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.QualityChangesInstanceContainer, "quality_changes")
|
||||
Resources.addStorageType(self.ResourceTypes.VariantInstanceContainer, "variants")
|
||||
|
@ -193,20 +334,64 @@ class CuraApplication(QtApplication):
|
|||
Resources.addStorageType(self.ResourceTypes.DefinitionChangesContainer, "definition_changes")
|
||||
Resources.addStorageType(self.ResourceTypes.SettingVisibilityPreset, "setting_visibility")
|
||||
|
||||
ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.QualityInstanceContainer, "quality")
|
||||
ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.QualityChangesInstanceContainer, "quality_changes")
|
||||
ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.VariantInstanceContainer, "variant")
|
||||
ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.MaterialInstanceContainer, "material")
|
||||
ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.UserInstanceContainer, "user")
|
||||
ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.ExtruderStack, "extruder_train")
|
||||
ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.MachineStack, "machine")
|
||||
ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.DefinitionChangesContainer, "definition_changes")
|
||||
self._container_registry.addResourceType(self.ResourceTypes.QualityInstanceContainer, "quality")
|
||||
self._container_registry.addResourceType(self.ResourceTypes.QualityChangesInstanceContainer, "quality_changes")
|
||||
self._container_registry.addResourceType(self.ResourceTypes.VariantInstanceContainer, "variant")
|
||||
self._container_registry.addResourceType(self.ResourceTypes.MaterialInstanceContainer, "material")
|
||||
self._container_registry.addResourceType(self.ResourceTypes.UserInstanceContainer, "user")
|
||||
self._container_registry.addResourceType(self.ResourceTypes.ExtruderStack, "extruder_train")
|
||||
self._container_registry.addResourceType(self.ResourceTypes.MachineStack, "machine")
|
||||
self._container_registry.addResourceType(self.ResourceTypes.DefinitionChangesContainer, "definition_changes")
|
||||
|
||||
## Initialise the version upgrade manager with Cura's storage paths.
|
||||
# Needs to be here to prevent circular dependencies.
|
||||
import UM.VersionUpgradeManager
|
||||
Resources.addType(self.ResourceTypes.QmlFiles, "qml")
|
||||
Resources.addType(self.ResourceTypes.Firmware, "firmware")
|
||||
|
||||
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", InstanceContainer.Version * 1000000 + self.SettingVersion): (self.ResourceTypes.QualityInstanceContainer, "application/x-uranium-instancecontainer"),
|
||||
("quality_changes", InstanceContainer.Version * 1000000 + self.SettingVersion): (self.ResourceTypes.QualityChangesInstanceContainer, "application/x-uranium-instancecontainer"),
|
||||
|
@ -219,6 +404,7 @@ class CuraApplication(QtApplication):
|
|||
}
|
||||
)
|
||||
|
||||
"""
|
||||
self._currently_loading_files = []
|
||||
self._non_sliceable_extensions = []
|
||||
|
||||
|
@ -254,6 +440,10 @@ class CuraApplication(QtApplication):
|
|||
self._variant_manager = None
|
||||
|
||||
self.default_theme = "cura-light"
|
||||
"""
|
||||
# Runs preparations that needs to be done before the starting process.
|
||||
def startSplashWindowPhase(self):
|
||||
super().startSplashWindowPhase()
|
||||
|
||||
self.setWindowIcon(QIcon(Resources.getPath(Resources.Images, "cura-icon.png")))
|
||||
|
||||
|
@ -287,23 +477,6 @@ class CuraApplication(QtApplication):
|
|||
"SelectionTool",
|
||||
"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._update_platform_activity_timer = QTimer()
|
||||
|
@ -316,56 +489,13 @@ class CuraApplication(QtApplication):
|
|||
self.getController().contextMenuRequested.connect(self._onContextMenuRequested)
|
||||
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..."))
|
||||
|
||||
# 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 = 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()
|
||||
with self._container_registry.lockFile():
|
||||
self._container_registry.loadAllMetadata()
|
||||
|
||||
# set the setting version for Preferences
|
||||
preferences = Preferences.getInstance()
|
||||
preferences = self.getPreferences()
|
||||
preferences.addPreference("metadata/setting_version", 0)
|
||||
preferences.setValue("metadata/setting_version", self.SettingVersion) #Don't make it equal to the default so that the setting version always gets written to the file.
|
||||
|
||||
|
@ -391,7 +521,7 @@ class CuraApplication(QtApplication):
|
|||
preferences.addPreference("view/filter_current_build_plate", False)
|
||||
preferences.addPreference("cura/sidebar_collapsed", False)
|
||||
|
||||
self._need_to_show_user_agreement = not Preferences.getInstance().getValue("general/accepted_user_agreement")
|
||||
self._need_to_show_user_agreement = not self.getPreferences().getValue("general/accepted_user_agreement")
|
||||
|
||||
for key in [
|
||||
"dialog_load_path", # dialog_save_path is in LocalFileOutputDevicePlugin
|
||||
|
@ -410,13 +540,10 @@ class CuraApplication(QtApplication):
|
|||
|
||||
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
|
||||
|
||||
def _onEngineCreated(self):
|
||||
self._engine.addImageProvider("camera", CameraImageProvider.CameraImageProvider())
|
||||
self._qml_engine.addImageProvider("camera", CameraImageProvider.CameraImageProvider())
|
||||
|
||||
@pyqtProperty(bool)
|
||||
def needToShowUserAgreement(self):
|
||||
|
@ -445,7 +572,7 @@ class CuraApplication(QtApplication):
|
|||
|
||||
## A reusable dialogbox
|
||||
#
|
||||
showMessageBox = pyqtSignal(str, str, str, str, str, int, int, arguments = ["title", "footer", "text", "informativeText", "detailedText", "buttons", "icon"])
|
||||
showMessageBox = pyqtSignal(str, str, str, str, int, int, arguments = ["title", "text", "informativeText", "detailedText", "buttons", "icon"])
|
||||
|
||||
def messageBox(self, title, text, informativeText = "", detailedText = "", buttons = QMessageBox.Ok, icon = QMessageBox.NoIcon, callback = None, callback_arguments = []):
|
||||
self._message_box_callback = callback
|
||||
|
@ -456,14 +583,14 @@ class CuraApplication(QtApplication):
|
|||
|
||||
def discardOrKeepProfileChanges(self):
|
||||
has_user_interaction = False
|
||||
choice = Preferences.getInstance().getValue("cura/choice_on_profile_override")
|
||||
choice = self.getPreferences().getValue("cura/choice_on_profile_override")
|
||||
if choice == "always_discard":
|
||||
# don't show dialog and DISCARD the profile
|
||||
self.discardOrKeepProfileChangesClosed("discard")
|
||||
elif choice == "always_keep":
|
||||
# don't show dialog and KEEP the profile
|
||||
self.discardOrKeepProfileChangesClosed("keep")
|
||||
elif self._use_gui:
|
||||
elif not self._is_headless:
|
||||
# ALWAYS ask whether to keep or discard the profile
|
||||
self.showDiscardOrKeepProfileChanges.emit()
|
||||
has_user_interaction = True
|
||||
|
@ -502,24 +629,19 @@ class CuraApplication(QtApplication):
|
|||
# Do not do saving during application start or when data should not be safed on quit.
|
||||
return
|
||||
ContainerRegistry.getInstance().saveDirtyContainers()
|
||||
Preferences.getInstance().writeToFile(Resources.getStoragePath(Resources.Preferences,
|
||||
self._application_name + ".cfg"))
|
||||
self.savePreferences()
|
||||
|
||||
def saveStack(self, stack):
|
||||
ContainerRegistry.getInstance().saveContainer(stack)
|
||||
|
||||
@pyqtSlot(str, result = QUrl)
|
||||
def getDefaultPath(self, key):
|
||||
default_path = Preferences.getInstance().getValue("local_file/%s" % key)
|
||||
default_path = self.getPreferences().getValue("local_file/%s" % key)
|
||||
return QUrl.fromLocalFile(default_path)
|
||||
|
||||
@pyqtSlot(str, str)
|
||||
def setDefaultPath(self, key, default_path):
|
||||
Preferences.getInstance().setValue("local_file/%s" % key, QUrl(default_path).toLocalFile())
|
||||
|
||||
@classmethod
|
||||
def getStaticVersion(cls):
|
||||
return CuraVersion
|
||||
self.getPreferences().setValue("local_file/%s" % key, QUrl(default_path).toLocalFile())
|
||||
|
||||
## Handle loading of all plugin types (and the backend explicitly)
|
||||
# \sa PluginRegistry
|
||||
|
@ -545,127 +667,8 @@ class CuraApplication(QtApplication):
|
|||
|
||||
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):
|
||||
self.preRun()
|
||||
|
||||
container_registry = ContainerRegistry.getInstance()
|
||||
container_registry = self._container_registry
|
||||
|
||||
Logger.log("i", "Initializing variant manager")
|
||||
self._variant_manager = VariantManager(container_registry)
|
||||
|
@ -684,29 +687,34 @@ class CuraApplication(QtApplication):
|
|||
Logger.log("i", "Initializing machine manager")
|
||||
self._machine_manager = MachineManager(self)
|
||||
|
||||
Logger.log("i", "Initializing container manager")
|
||||
self._container_manager = ContainerManager(self)
|
||||
|
||||
Logger.log("i", "Initializing machine error checker")
|
||||
self._machine_error_checker = MachineErrorChecker(self)
|
||||
self._machine_error_checker.initialize()
|
||||
|
||||
# Check if we should run as single instance or not
|
||||
self._setUpSingleInstanceServer()
|
||||
# Check if we should run as single instance or not. If so, set up a local socket server which listener which
|
||||
# coordinates multiple Cura instances and accepts commands.
|
||||
if self._use_single_instance:
|
||||
self.__setUpSingleInstanceServer()
|
||||
|
||||
# Setup scene and build volume
|
||||
root = self.getController().getScene().getRoot()
|
||||
self._volume = BuildVolume.BuildVolume(self.getController().getScene().getRoot())
|
||||
self._volume = BuildVolume.BuildVolume(self, root)
|
||||
Arrange.build_volume = self._volume
|
||||
|
||||
# initialize info objects
|
||||
self._print_information = PrintInformation.PrintInformation()
|
||||
self._print_information = PrintInformation.PrintInformation(self)
|
||||
self._cura_actions = CuraActions.CuraActions(self)
|
||||
|
||||
# Initialize setting visibility presets model
|
||||
self._setting_visibility_presets_model = SettingVisibilityPresetsModel(self)
|
||||
default_visibility_profile = self._setting_visibility_presets_model.getItem(0)
|
||||
Preferences.getInstance().setDefault("general/visible_settings", ";".join(default_visibility_profile["settings"]))
|
||||
self.getPreferences().setDefault("general/visible_settings", ";".join(default_visibility_profile["settings"]))
|
||||
|
||||
# Detect in which mode to run and execute that mode
|
||||
if self.getCommandLineOption("headless", False):
|
||||
if self._is_headless:
|
||||
self.runWithoutGUI()
|
||||
else:
|
||||
self.runWithGUI()
|
||||
|
@ -715,7 +723,6 @@ class CuraApplication(QtApplication):
|
|||
self.initializationFinished.emit()
|
||||
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
|
||||
# initialized, for example opening files because they may show dialogs which can be closed due to incomplete
|
||||
# GUI initialization.
|
||||
|
@ -730,8 +737,12 @@ class CuraApplication(QtApplication):
|
|||
|
||||
self.exec_()
|
||||
|
||||
def __setUpSingleInstanceServer(self):
|
||||
if self._use_single_instance:
|
||||
self._single_instance.startServer()
|
||||
|
||||
def _onPostStart(self):
|
||||
for file_name in self.getCommandLineOption("file", []):
|
||||
for file_name in self._files_to_open:
|
||||
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.
|
||||
self.callLater(self._openFile, file_name)
|
||||
|
@ -740,13 +751,10 @@ class CuraApplication(QtApplication):
|
|||
|
||||
## Run Cura without GUI elements and interaction (server mode).
|
||||
def runWithoutGUI(self):
|
||||
self._use_gui = False
|
||||
self.closeSplash()
|
||||
|
||||
## Run Cura with GUI (desktop mode).
|
||||
def runWithGUI(self):
|
||||
self._use_gui = True
|
||||
|
||||
self.showSplashMessage(self._i18n_catalog.i18nc("@info:progress", "Setting up scene..."))
|
||||
|
||||
controller = self.getController()
|
||||
|
@ -796,9 +804,6 @@ class CuraApplication(QtApplication):
|
|||
# Hide the splash screen
|
||||
self.closeSplash()
|
||||
|
||||
def hasGui(self):
|
||||
return self._use_gui
|
||||
|
||||
@pyqtSlot(result = QObject)
|
||||
def getSettingVisibilityPresetsModel(self, *args) -> SettingVisibilityPresetsModel:
|
||||
return self._setting_visibility_presets_model
|
||||
|
@ -813,7 +818,7 @@ class CuraApplication(QtApplication):
|
|||
|
||||
def getExtruderManager(self, *args):
|
||||
if self._extruder_manager is None:
|
||||
self._extruder_manager = ExtruderManager.createExtruderManager()
|
||||
self._extruder_manager = ExtruderManager()
|
||||
return self._extruder_manager
|
||||
|
||||
def getVariantManager(self, *args):
|
||||
|
@ -936,7 +941,7 @@ class CuraApplication(QtApplication):
|
|||
qmlRegisterType(QualitySettingsModel, "Cura", 1, 0, "QualitySettingsModel")
|
||||
qmlRegisterType(MachineNameValidator, "Cura", 1, 0, "MachineNameValidator")
|
||||
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.
|
||||
actions_url = QUrl.fromLocalFile(os.path.abspath(Resources.getPath(CuraApplication.ResourceTypes.QmlFiles, "Actions.qml")))
|
||||
|
@ -970,7 +975,7 @@ class CuraApplication(QtApplication):
|
|||
# Default
|
||||
self.getController().setActiveTool("TranslateTool")
|
||||
|
||||
if Preferences.getInstance().getValue("view/center_on_select"):
|
||||
if self.getPreferences().getValue("view/center_on_select"):
|
||||
self._center_after_select = True
|
||||
else:
|
||||
if self.getController().getActiveTool():
|
||||
|
@ -1317,15 +1322,15 @@ class CuraApplication(QtApplication):
|
|||
categories = list(set(categories))
|
||||
categories.sort()
|
||||
joined = ";".join(categories)
|
||||
if joined != Preferences.getInstance().getValue("cura/categories_expanded"):
|
||||
Preferences.getInstance().setValue("cura/categories_expanded", joined)
|
||||
if joined != self.getPreferences().getValue("cura/categories_expanded"):
|
||||
self.getPreferences().setValue("cura/categories_expanded", joined)
|
||||
self.expandedCategoriesChanged.emit()
|
||||
|
||||
expandedCategoriesChanged = pyqtSignal()
|
||||
|
||||
@pyqtProperty("QStringList", notify = expandedCategoriesChanged)
|
||||
def expandedCategories(self):
|
||||
return Preferences.getInstance().getValue("cura/categories_expanded").split(";")
|
||||
return self.getPreferences().getValue("cura/categories_expanded").split(";")
|
||||
|
||||
@pyqtSlot()
|
||||
def mergeSelected(self):
|
||||
|
@ -1476,8 +1481,7 @@ class CuraApplication(QtApplication):
|
|||
# see GroupDecorator._onChildrenChanged
|
||||
|
||||
def _createSplashScreen(self):
|
||||
run_headless = self.getCommandLineOption("headless", False)
|
||||
if run_headless:
|
||||
if self._is_headless:
|
||||
return None
|
||||
return CuraSplashScreen.CuraSplashScreen()
|
||||
|
||||
|
@ -1489,11 +1493,15 @@ class CuraApplication(QtApplication):
|
|||
|
||||
def _reloadMeshFinished(self, job):
|
||||
# TODO; This needs to be fixed properly. We now make the assumption that we only load a single mesh!
|
||||
mesh_data = job.getResult()[0].getMeshData()
|
||||
if mesh_data:
|
||||
job._node.setMeshData(mesh_data)
|
||||
else:
|
||||
job_result = job.getResult()
|
||||
if len(job_result) == 0:
|
||||
Logger.log("e", "Reloading the mesh failed.")
|
||||
return
|
||||
mesh_data = job_result[0].getMeshData()
|
||||
if not mesh_data:
|
||||
Logger.log("w", "Could not find a mesh in reloaded node.")
|
||||
return
|
||||
job._node.setMeshData(mesh_data)
|
||||
|
||||
def _openFile(self, filename):
|
||||
self.readLocalFile(QUrl.fromLocalFile(filename))
|
||||
|
@ -1557,10 +1565,11 @@ class CuraApplication(QtApplication):
|
|||
|
||||
f = file.toLocalFile()
|
||||
extension = os.path.splitext(f)[1]
|
||||
extension = extension.lower()
|
||||
filename = os.path.basename(f)
|
||||
if len(self._currently_loading_files) > 0:
|
||||
# If a non-slicable file is already being loaded, we prevent loading of any further non-slicable files
|
||||
if extension.lower() in self._non_sliceable_extensions:
|
||||
if extension in self._non_sliceable_extensions:
|
||||
message = Message(
|
||||
self._i18n_catalog.i18nc("@info:status",
|
||||
"Only one G-code file can be loaded at a time. Skipped importing {0}",
|
||||
|
@ -1569,7 +1578,8 @@ class CuraApplication(QtApplication):
|
|||
return
|
||||
# If file being loaded is non-slicable file, then prevent loading of any other files
|
||||
extension = os.path.splitext(self._currently_loading_files[0])[1]
|
||||
if extension.lower() in self._non_sliceable_extensions:
|
||||
extension = extension.lower()
|
||||
if extension in self._non_sliceable_extensions:
|
||||
message = Message(
|
||||
self._i18n_catalog.i18nc("@info:status",
|
||||
"Can't open any other file if G-code is loading. Skipped importing {0}",
|
||||
|
@ -1592,8 +1602,8 @@ class CuraApplication(QtApplication):
|
|||
|
||||
self.fileLoaded.emit(filename)
|
||||
arrange_objects_on_load = (
|
||||
not Preferences.getInstance().getValue("cura/use_multi_build_plate") or
|
||||
not Preferences.getInstance().getValue("cura/not_arrange_objects_on_load"))
|
||||
not self.getPreferences().getValue("cura/use_multi_build_plate") or
|
||||
not self.getPreferences().getValue("cura/not_arrange_objects_on_load"))
|
||||
target_build_plate = self.getMultiBuildPlateModel().activeBuildPlate if arrange_objects_on_load else -1
|
||||
|
||||
root = self.getController().getScene().getRoot()
|
||||
|
|
|
@ -4,4 +4,6 @@
|
|||
CuraVersion = "@CURA_VERSION@"
|
||||
CuraBuildType = "@CURA_BUILDTYPE@"
|
||||
CuraDebugMode = True if "@_cura_debugmode@" == "ON" else False
|
||||
CuraPackagesVersion = "@CURA_PACKAGES_VERSION@"
|
||||
CuraSDKVersion = "@CURA_SDK_VERSION@"
|
||||
CuraCloudAPIRoot = "@CURA_CLOUD_API_ROOT@"
|
||||
CuraCloudAPIVersion = "@CURA_CLOUD_API_VERSION@"
|
||||
|
|
|
@ -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.
|
||||
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 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
|
||||
class UnknownMachineActionError(Exception):
|
||||
|
@ -20,23 +20,27 @@ class NotUniqueMachineActionError(Exception):
|
|||
|
||||
|
||||
class MachineActionManager(QObject):
|
||||
def __init__(self, parent = None):
|
||||
def __init__(self, application, parent = None):
|
||||
super().__init__(parent)
|
||||
self._application = application
|
||||
|
||||
self._machine_actions = {} # Dict of all known machine actions
|
||||
self._required_actions = {} # Dict of all required 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
|
||||
|
||||
def initialize(self):
|
||||
container_registry = self._application.getContainerRegistry()
|
||||
|
||||
# Add machine_action as plugin type
|
||||
PluginRegistry.addType("machine_action", self.addMachineAction)
|
||||
|
||||
# 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.
|
||||
for container in ContainerRegistry.getInstance().findDefinitionContainers():
|
||||
for container in container_registry.findDefinitionContainers():
|
||||
self._onContainerAdded(container)
|
||||
|
||||
ContainerRegistry.getInstance().containerAdded.connect(self._onContainerAdded)
|
||||
container_registry.containerAdded.connect(self._onContainerAdded)
|
||||
|
||||
def _onContainerAdded(self, container):
|
||||
## Ensure that the actions are added to this manager
|
||||
|
|
|
@ -291,9 +291,10 @@ class MaterialManager(QObject):
|
|||
material_id_metadata_dict = dict()
|
||||
for node in nodes_to_check:
|
||||
if node is not None:
|
||||
# Only exclude the materials that are explicitly specified in the "exclude_materials" field.
|
||||
# Do not exclude other materials that are of the same type.
|
||||
for material_id, node in node.material_map.items():
|
||||
fallback_id = self.getFallbackMaterialIdByMaterialType(node.metadata["material"])
|
||||
if fallback_id in machine_exclude_materials:
|
||||
if material_id in machine_exclude_materials:
|
||||
Logger.log("d", "Exclude material [%s] for machine [%s]",
|
||||
material_id, machine_definition.getId())
|
||||
continue
|
||||
|
|
|
@ -39,6 +39,8 @@ class BaseMaterialsModel(ListModel):
|
|||
|
||||
self._extruder_position = 0
|
||||
self._extruder_stack = None
|
||||
# Update the stack and the model data when the machine changes
|
||||
self._machine_manager.globalContainerChanged.connect(self._updateExtruderStack)
|
||||
|
||||
def _updateExtruderStack(self):
|
||||
global_stack = self._machine_manager.activeMachine
|
||||
|
@ -50,9 +52,11 @@ class BaseMaterialsModel(ListModel):
|
|||
self._extruder_stack = global_stack.extruders.get(str(self._extruder_position))
|
||||
if self._extruder_stack is not None:
|
||||
self._extruder_stack.pyqtContainersChanged.connect(self._update)
|
||||
# Force update the model when the extruder stack changes
|
||||
self._update()
|
||||
|
||||
def setExtruderPosition(self, position: int):
|
||||
if self._extruder_position != position:
|
||||
if self._extruder_stack is None or self._extruder_position != position:
|
||||
self._extruder_position = position
|
||||
self._updateExtruderStack()
|
||||
self.extruderPositionChanged.emit()
|
||||
|
|
|
@ -8,9 +8,9 @@ from configparser import ConfigParser
|
|||
|
||||
from PyQt5.QtCore import pyqtProperty, Qt, pyqtSignal, pyqtSlot
|
||||
|
||||
from UM.Application import Application
|
||||
from UM.Logger import Logger
|
||||
from UM.Qt.ListModel import ListModel
|
||||
from UM.Preferences import Preferences
|
||||
from UM.Resources import Resources
|
||||
from UM.MimeTypeDatabase import MimeTypeDatabase, MimeTypeNotFoundError
|
||||
|
||||
|
@ -33,7 +33,7 @@ class SettingVisibilityPresetsModel(ListModel):
|
|||
basic_item = self.items[1]
|
||||
basic_visibile_settings = ";".join(basic_item["settings"])
|
||||
|
||||
self._preferences = Preferences.getInstance()
|
||||
self._preferences = Application.getInstance().getPreferences()
|
||||
# Preference to store which preset is currently selected
|
||||
self._preferences.addPreference("cura/active_setting_visibility_preset", "basic")
|
||||
# Preference that stores the "custom" set so it can always be restored (even after a restart)
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
# Copyright (c) 2017 Ultimaker B.V.
|
||||
# Copyright (c) 2018 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import copy
|
||||
|
||||
from UM.Job import Job
|
||||
from UM.Operations.GroupedOperation import GroupedOperation
|
||||
from UM.Message import Message
|
||||
|
@ -36,7 +38,7 @@ class MultiplyObjectsJob(Job):
|
|||
|
||||
root = scene.getRoot()
|
||||
scale = 0.5
|
||||
arranger = Arrange.create(x = machine_width, y = machine_depth, scene_root = root, scale = scale)
|
||||
arranger = Arrange.create(x = machine_width, y = machine_depth, scene_root = root, scale = scale, min_offset = self._min_offset)
|
||||
processed_nodes = []
|
||||
nodes = []
|
||||
|
||||
|
@ -64,6 +66,8 @@ class MultiplyObjectsJob(Job):
|
|||
# We do place the nodes one by one, as we want to yield in between.
|
||||
if not node_too_big:
|
||||
new_node, solution_found = arranger.findNodePlacement(current_node, offset_shape_arr, hull_shape_arr)
|
||||
else:
|
||||
new_node = copy.deepcopy(node)
|
||||
if node_too_big or not solution_found:
|
||||
found_solution_for_all = False
|
||||
new_location = new_node.getPosition()
|
||||
|
|
|
@ -8,7 +8,6 @@ from UM.Qt.ListModel import ListModel
|
|||
from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
|
||||
from UM.Scene.SceneNode import SceneNode
|
||||
from UM.Scene.Selection import Selection
|
||||
from UM.Preferences import Preferences
|
||||
from UM.i18n import i18nCatalog
|
||||
|
||||
catalog = i18nCatalog("cura")
|
||||
|
@ -20,7 +19,7 @@ class ObjectsModel(ListModel):
|
|||
super().__init__()
|
||||
|
||||
Application.getInstance().getController().getScene().sceneChanged.connect(self._updateDelayed)
|
||||
Preferences.getInstance().preferenceChanged.connect(self._updateDelayed)
|
||||
Application.getInstance().getPreferences().preferenceChanged.connect(self._updateDelayed)
|
||||
|
||||
self._update_timer = QTimer()
|
||||
self._update_timer.setInterval(100)
|
||||
|
@ -38,7 +37,7 @@ class ObjectsModel(ListModel):
|
|||
|
||||
def _update(self, *args):
|
||||
nodes = []
|
||||
filter_current_build_plate = Preferences.getInstance().getValue("view/filter_current_build_plate")
|
||||
filter_current_build_plate = Application.getInstance().getPreferences().getValue("view/filter_current_build_plate")
|
||||
active_build_plate_number = self._build_plate_number
|
||||
group_nr = 1
|
||||
for node in DepthFirstIterator(Application.getInstance().getController().getScene().getRoot()):
|
||||
|
|
|
@ -8,7 +8,6 @@ from UM.Scene.SceneNode import SceneNode
|
|||
from UM.Scene.Iterator.BreadthFirstIterator import BreadthFirstIterator
|
||||
from UM.Math.Vector import Vector
|
||||
from UM.Scene.Selection import Selection
|
||||
from UM.Preferences import Preferences
|
||||
|
||||
from cura.Scene.ConvexHullDecorator import ConvexHullDecorator
|
||||
|
||||
|
@ -36,8 +35,8 @@ class PlatformPhysics:
|
|||
self._max_overlap_checks = 10 # How many times should we try to find a new spot per tick?
|
||||
self._minimum_gap = 2 # It is a minimum distance (in mm) between two models, applicable for small models
|
||||
|
||||
Preferences.getInstance().addPreference("physics/automatic_push_free", False)
|
||||
Preferences.getInstance().addPreference("physics/automatic_drop_down", True)
|
||||
Application.getInstance().getPreferences().addPreference("physics/automatic_push_free", False)
|
||||
Application.getInstance().getPreferences().addPreference("physics/automatic_drop_down", True)
|
||||
|
||||
def _onSceneChanged(self, source):
|
||||
if not source.getMeshData():
|
||||
|
@ -71,7 +70,7 @@ class PlatformPhysics:
|
|||
# Move it downwards if bottom is above platform
|
||||
move_vector = Vector()
|
||||
|
||||
if Preferences.getInstance().getValue("physics/automatic_drop_down") and not (node.getParent() and node.getParent().callDecoration("isGroup") or node.getParent() != root) and node.isEnabled(): #If an object is grouped, don't move it down
|
||||
if Application.getInstance().getPreferences().getValue("physics/automatic_drop_down") and not (node.getParent() and node.getParent().callDecoration("isGroup") or node.getParent() != root) and node.isEnabled(): #If an object is grouped, don't move it down
|
||||
z_offset = node.callDecoration("getZOffset") if node.getDecorator(ZOffsetDecorator.ZOffsetDecorator) else 0
|
||||
move_vector = move_vector.set(y = -bbox.bottom + z_offset)
|
||||
|
||||
|
@ -80,7 +79,7 @@ class PlatformPhysics:
|
|||
node.addDecorator(ConvexHullDecorator())
|
||||
|
||||
# only push away objects if this node is a printing mesh
|
||||
if not node.callDecoration("isNonPrintingMesh") and Preferences.getInstance().getValue("physics/automatic_push_free"):
|
||||
if not node.callDecoration("isNonPrintingMesh") and Application.getInstance().getPreferences().getValue("physics/automatic_push_free"):
|
||||
# Check for collisions between convex hulls
|
||||
for other_node in BreadthFirstIterator(root):
|
||||
# Ignore root, ourselves and anything that is not a normal SceneNode.
|
||||
|
|
|
@ -1,25 +1,25 @@
|
|||
# Copyright (c) 2018 Ultimaker B.V.
|
||||
# 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 math
|
||||
import os
|
||||
import unicodedata
|
||||
import re # To create abbreviations for printer names.
|
||||
from typing import Dict
|
||||
|
||||
from PyQt5.QtCore import QObject, pyqtSignal, pyqtProperty, pyqtSlot
|
||||
|
||||
from UM.Application import Application
|
||||
from UM.i18n import i18nCatalog
|
||||
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.MimeTypeDatabase import MimeTypeDatabase
|
||||
|
||||
catalog = i18nCatalog("cura")
|
||||
|
||||
|
||||
## 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
|
||||
|
@ -48,8 +48,9 @@ class PrintInformation(QObject):
|
|||
ActiveMachineChanged = 3
|
||||
Other = 4
|
||||
|
||||
def __init__(self, parent = None):
|
||||
def __init__(self, application, parent = None):
|
||||
super().__init__(parent)
|
||||
self._application = application
|
||||
|
||||
self.initializeCuraMessagePrintTimeProperties()
|
||||
|
||||
|
@ -60,11 +61,12 @@ class PrintInformation(QObject):
|
|||
|
||||
self._pre_sliced = False
|
||||
|
||||
self._backend = Application.getInstance().getBackend()
|
||||
self._backend = self._application.getBackend()
|
||||
if self._backend:
|
||||
self._backend.printDurationMessage.connect(self._onPrintDurationMessage)
|
||||
Application.getInstance().getController().getScene().sceneChanged.connect(self._onSceneChanged)
|
||||
self._application.getController().getScene().sceneChanged.connect(self._onSceneChanged)
|
||||
|
||||
self._is_user_specified_job_name = False
|
||||
self._base_name = ""
|
||||
self._abbr_machine = ""
|
||||
self._job_name = ""
|
||||
|
@ -72,7 +74,6 @@ class PrintInformation(QObject):
|
|||
self._active_build_plate = 0
|
||||
self._initVariablesWithBuildPlate(self._active_build_plate)
|
||||
|
||||
self._application = Application.getInstance()
|
||||
self._multi_build_plate_model = self._application.getMultiBuildPlateModel()
|
||||
|
||||
self._application.globalContainerStackChanged.connect(self._updateJobName)
|
||||
|
@ -81,7 +82,7 @@ class PrintInformation(QObject):
|
|||
self._application.workspaceLoaded.connect(self.setProjectName)
|
||||
self._multi_build_plate_model.activeBuildPlateChanged.connect(self._onActiveBuildPlateChanged)
|
||||
|
||||
Preferences.getInstance().preferenceChanged.connect(self._onPreferencesChanged)
|
||||
self._application.getInstance().getPreferences().preferenceChanged.connect(self._onPreferencesChanged)
|
||||
|
||||
self._application.getMachineManager().rootMaterialChanged.connect(self._onActiveMaterialsChanged)
|
||||
self._onActiveMaterialsChanged()
|
||||
|
@ -200,7 +201,7 @@ class PrintInformation(QObject):
|
|||
self._current_print_time[build_plate_number].setDuration(total_estimated_time)
|
||||
|
||||
def _calculateInformation(self, build_plate_number):
|
||||
global_stack = Application.getInstance().getGlobalContainerStack()
|
||||
global_stack = self._application.getGlobalContainerStack()
|
||||
if global_stack is None:
|
||||
return
|
||||
|
||||
|
@ -209,7 +210,7 @@ class PrintInformation(QObject):
|
|||
self._material_costs[build_plate_number] = []
|
||||
self._material_names[build_plate_number] = []
|
||||
|
||||
material_preference_values = json.loads(Preferences.getInstance().getValue("cura/material_settings"))
|
||||
material_preference_values = json.loads(self._application.getInstance().getPreferences().getValue("cura/material_settings"))
|
||||
|
||||
extruder_stacks = global_stack.extruders
|
||||
for position, extruder_stack in extruder_stacks.items():
|
||||
|
@ -281,10 +282,13 @@ class PrintInformation(QObject):
|
|||
|
||||
# Manual override of job name should also set the base name so that when the printer prefix is updated, it the
|
||||
# prefix can be added to the manually added name, not the old base name
|
||||
@pyqtSlot(str)
|
||||
def setJobName(self, name):
|
||||
@pyqtSlot(str, bool)
|
||||
def setJobName(self, name, is_user_specified_job_name = False):
|
||||
self._is_user_specified_job_name = is_user_specified_job_name
|
||||
self._job_name = name
|
||||
self._base_name = name.replace(self._abbr_machine + "_", "")
|
||||
if name == "":
|
||||
self._is_user_specified_job_name = False
|
||||
self.jobNameChanged.emit()
|
||||
|
||||
jobNameChanged = pyqtSignal()
|
||||
|
@ -295,15 +299,19 @@ class PrintInformation(QObject):
|
|||
|
||||
def _updateJobName(self):
|
||||
if self._base_name == "":
|
||||
self._job_name = ""
|
||||
self._job_name = "unnamed"
|
||||
self._is_user_specified_job_name = False
|
||||
self.jobNameChanged.emit()
|
||||
return
|
||||
|
||||
base_name = self._stripAccents(self._base_name)
|
||||
self._setAbbreviatedMachineName()
|
||||
|
||||
# Only update the job name when it's not user-specified.
|
||||
if not self._is_user_specified_job_name:
|
||||
if self._pre_sliced:
|
||||
self._job_name = catalog.i18nc("@label", "Pre-sliced file {0}", base_name)
|
||||
elif Preferences.getInstance().getValue("cura/jobname_prefix"):
|
||||
elif self._application.getInstance().getPreferences().getValue("cura/jobname_prefix"):
|
||||
# Don't add abbreviation if it already has the exact same abbreviation.
|
||||
if base_name.startswith(self._abbr_machine + "_"):
|
||||
self._job_name = base_name
|
||||
|
@ -321,6 +329,8 @@ class PrintInformation(QObject):
|
|||
baseNameChanged = pyqtSignal()
|
||||
|
||||
def setBaseName(self, base_name: str, is_project_file: bool = False):
|
||||
self._is_user_specified_job_name = False
|
||||
|
||||
# Ensure that we don't use entire path but only filename
|
||||
name = os.path.basename(base_name)
|
||||
|
||||
|
@ -341,17 +351,17 @@ class PrintInformation(QObject):
|
|||
if is_gcode or is_project_file or (is_empty or (self._base_name == "" and self._base_name != check_name)):
|
||||
# Only take the file name part, Note : file name might have 'dot' in name as well
|
||||
|
||||
data = ''
|
||||
data = ""
|
||||
try:
|
||||
mime_type = MimeTypeDatabase.getMimeTypeForFile(name)
|
||||
data = mime_type.stripExtension(name)
|
||||
except:
|
||||
Logger.log("w", "Unsupported Mime Type Database file extension")
|
||||
Logger.log("w", "Unsupported Mime Type Database file extension %s", name)
|
||||
|
||||
if data is not None and check_name is not None:
|
||||
self._base_name = data
|
||||
else:
|
||||
self._base_name = ''
|
||||
self._base_name = ""
|
||||
|
||||
self._updateJobName()
|
||||
|
||||
|
@ -362,7 +372,7 @@ class PrintInformation(QObject):
|
|||
## Created an acronymn-like abbreviated machine name from the currently active machine name
|
||||
# Called each time the global stack is switched
|
||||
def _setAbbreviatedMachineName(self):
|
||||
global_container_stack = Application.getInstance().getGlobalContainerStack()
|
||||
global_container_stack = self._application.getGlobalContainerStack()
|
||||
if not global_container_stack:
|
||||
self._abbr_machine = ""
|
||||
return
|
||||
|
|
|
@ -24,7 +24,7 @@ class ConvexHullNode(SceneNode):
|
|||
self._original_parent = parent
|
||||
|
||||
# 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())
|
||||
else:
|
||||
self._color = Color(0, 0, 0)
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# Copyright (c) 2018 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
from copy import deepcopy
|
||||
from typing import List
|
||||
from typing import List, Optional
|
||||
|
||||
from UM.Application import Application
|
||||
from UM.Math.AxisAlignedBox import AxisAlignedBox
|
||||
|
@ -13,9 +13,9 @@ from cura.Settings.SettingOverrideDecorator import SettingOverrideDecorator
|
|||
## Scene nodes that are models are only seen when selecting the corresponding build plate
|
||||
# Note that many other nodes can just be UM SceneNode objects.
|
||||
class CuraSceneNode(SceneNode):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
if "no_setting_override" not in kwargs:
|
||||
def __init__(self, parent: Optional["SceneNode"] = None, visible: bool = True, name: str = "", no_setting_override: bool = False):
|
||||
super().__init__(parent = parent, visible = visible, name = name)
|
||||
if not no_setting_override:
|
||||
self.addDecorator(SettingOverrideDecorator()) # now we always have a getActiveExtruderPosition, unless explicitly disabled
|
||||
self._outside_buildarea = False
|
||||
|
||||
|
|
|
@ -1,32 +1,25 @@
|
|||
# Copyright (c) 2018 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import os.path
|
||||
import os
|
||||
import urllib.parse
|
||||
import uuid
|
||||
from typing import Dict, Union
|
||||
|
||||
from PyQt5.QtCore import QObject, QUrl, QVariant
|
||||
from UM.FlameProfiler import pyqtSlot
|
||||
from PyQt5.QtWidgets import QMessageBox
|
||||
|
||||
from UM.PluginRegistry import PluginRegistry
|
||||
from UM.SaveFile import SaveFile
|
||||
from UM.Platform import Platform
|
||||
from UM.MimeTypeDatabase import MimeTypeDatabase
|
||||
|
||||
from UM.i18n import i18nCatalog
|
||||
from UM.FlameProfiler import pyqtSlot
|
||||
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.DefinitionContainer import DefinitionContainer
|
||||
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")
|
||||
|
||||
|
||||
|
@ -36,11 +29,17 @@ catalog = i18nCatalog("cura")
|
|||
# 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.
|
||||
class ContainerManager(QObject):
|
||||
def __init__(self, parent = None):
|
||||
super().__init__(parent)
|
||||
|
||||
self._application = Application.getInstance()
|
||||
self._container_registry = ContainerRegistry.getInstance()
|
||||
def __init__(self, application):
|
||||
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._material_manager = self._application.getMaterialManager()
|
||||
self._container_name_filters = {}
|
||||
|
@ -129,7 +128,7 @@ class ContainerManager(QObject):
|
|||
container.setProperty(setting_key, property_name, property_value)
|
||||
|
||||
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:
|
||||
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.
|
||||
@pyqtSlot(result = bool)
|
||||
def updateQualityChanges(self):
|
||||
global_stack = Application.getInstance().getGlobalContainerStack()
|
||||
global_stack = self._machine_manager.activeMachine
|
||||
if not global_stack:
|
||||
return False
|
||||
|
||||
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.
|
||||
quality_changes = stack.qualityChanges
|
||||
if not quality_changes or self._container_registry.isReadOnly(quality_changes.getId()):
|
||||
|
@ -334,13 +335,15 @@ class ContainerManager(QObject):
|
|||
send_emits_containers = []
|
||||
|
||||
# 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.clear()
|
||||
send_emits_containers.append(container)
|
||||
|
||||
# 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:
|
||||
container.sendPostponedEmits()
|
||||
|
@ -381,21 +384,6 @@ class ContainerManager(QObject):
|
|||
if container is not None:
|
||||
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):
|
||||
if merge == merge_into:
|
||||
return
|
||||
|
@ -415,7 +403,7 @@ class ContainerManager(QObject):
|
|||
|
||||
serialize_type = ""
|
||||
try:
|
||||
plugin_metadata = PluginRegistry.getInstance().getMetaData(plugin_id)
|
||||
plugin_metadata = self._plugin_registry.getMetaData(plugin_id)
|
||||
if plugin_metadata:
|
||||
serialize_type = plugin_metadata["settings_container"]["type"]
|
||||
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]
|
||||
self._container_registry.exportQualityProfile(container_list, path, file_type)
|
||||
|
||||
__instance = None
|
||||
|
||||
@classmethod
|
||||
def getInstance(cls, *args, **kwargs) -> "ContainerManager":
|
||||
return cls.__instance
|
||||
|
|
|
@ -475,7 +475,7 @@ class CuraContainerRegistry(ContainerRegistry):
|
|||
extruder_definition = extruder_definitions[0]
|
||||
unique_name = self.uniqueName(machine.getName() + " " + new_extruder_id) if create_new_ids else machine.getName() + " " + new_extruder_id
|
||||
|
||||
extruder_stack = ExtruderStack.ExtruderStack(unique_name, parent = machine)
|
||||
extruder_stack = ExtruderStack.ExtruderStack(unique_name)
|
||||
extruder_stack.setName(extruder_definition.getName())
|
||||
extruder_stack.setDefinition(extruder_definition)
|
||||
extruder_stack.addMetaDataEntry("position", extruder_definition.getMetaDataEntry("position"))
|
||||
|
|
|
@ -39,8 +39,8 @@ from . import Exceptions
|
|||
# This also means that operations on the stack that modifies the container ordering is prohibited and
|
||||
# will raise an exception.
|
||||
class CuraContainerStack(ContainerStack):
|
||||
def __init__(self, container_id: str, *args, **kwargs):
|
||||
super().__init__(container_id, *args, **kwargs)
|
||||
def __init__(self, container_id: str):
|
||||
super().__init__(container_id)
|
||||
|
||||
self._container_registry = ContainerRegistry.getInstance()
|
||||
|
||||
|
|
|
@ -7,7 +7,6 @@ from UM.ConfigurationErrorMessage import ConfigurationErrorMessage
|
|||
from UM.Logger import Logger
|
||||
from UM.Settings.Interfaces import DefinitionContainerInterface
|
||||
from UM.Settings.InstanceContainer import InstanceContainer
|
||||
from UM.Settings.ContainerRegistry import ContainerRegistry
|
||||
|
||||
from cura.Machines.VariantManager import VariantType
|
||||
from .GlobalStack import GlobalStack
|
||||
|
@ -29,7 +28,7 @@ class CuraStackBuilder:
|
|||
variant_manager = application.getVariantManager()
|
||||
material_manager = application.getMaterialManager()
|
||||
quality_manager = application.getQualityManager()
|
||||
registry = ContainerRegistry.getInstance()
|
||||
registry = application.getContainerRegistry()
|
||||
|
||||
definitions = registry.findDefinitionContainers(id = definition_id)
|
||||
if not definitions:
|
||||
|
@ -99,8 +98,7 @@ class CuraStackBuilder:
|
|||
position = position,
|
||||
variant_container = extruder_variant_container,
|
||||
material_container = material_container,
|
||||
quality_container = application.empty_quality_container,
|
||||
global_stack = new_global_stack,
|
||||
quality_container = application.empty_quality_container
|
||||
)
|
||||
new_extruder.setNextStack(new_global_stack)
|
||||
new_global_stack.addExtruder(new_extruder)
|
||||
|
@ -139,11 +137,12 @@ class CuraStackBuilder:
|
|||
@classmethod
|
||||
def createExtruderStack(cls, new_stack_id: str, extruder_definition: DefinitionContainerInterface, machine_definition_id: str,
|
||||
position: int,
|
||||
variant_container, material_container, quality_container, global_stack) -> ExtruderStack:
|
||||
variant_container, material_container, quality_container) -> ExtruderStack:
|
||||
from cura.CuraApplication import CuraApplication
|
||||
application = CuraApplication.getInstance()
|
||||
registry = application.getContainerRegistry()
|
||||
|
||||
stack = ExtruderStack(new_stack_id, parent = global_stack)
|
||||
stack = ExtruderStack(new_stack_id)
|
||||
stack.setName(extruder_definition.getName())
|
||||
stack.setDefinition(extruder_definition)
|
||||
|
||||
|
@ -162,7 +161,7 @@ class CuraStackBuilder:
|
|||
# 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
|
||||
# setting properties will not result in incomplete containers being added.
|
||||
ContainerRegistry.getInstance().addContainer(user_container)
|
||||
registry.addContainer(user_container)
|
||||
|
||||
return stack
|
||||
|
||||
|
@ -178,6 +177,7 @@ class CuraStackBuilder:
|
|||
variant_container, material_container, quality_container) -> GlobalStack:
|
||||
from cura.CuraApplication import CuraApplication
|
||||
application = CuraApplication.getInstance()
|
||||
registry = application.getContainerRegistry()
|
||||
|
||||
stack = GlobalStack(new_stack_id)
|
||||
stack.setDefinition(definition)
|
||||
|
@ -193,7 +193,7 @@ class CuraStackBuilder:
|
|||
stack.qualityChanges = application.empty_quality_changes_container
|
||||
stack.userChanges = user_container
|
||||
|
||||
ContainerRegistry.getInstance().addContainer(user_container)
|
||||
registry.addContainer(user_container)
|
||||
|
||||
return stack
|
||||
|
||||
|
@ -201,8 +201,10 @@ class CuraStackBuilder:
|
|||
def createUserChangesContainer(cls, container_name: str, definition_id: str, stack_id: str,
|
||||
is_global_stack: bool) -> "InstanceContainer":
|
||||
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.setDefinition(definition_id)
|
||||
|
@ -217,15 +219,17 @@ class CuraStackBuilder:
|
|||
@classmethod
|
||||
def createDefinitionChangesContainer(cls, container_stack, container_name):
|
||||
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.setDefinition(container_stack.getBottom().getId())
|
||||
definition_changes_container.addMetaDataEntry("type", "definition_changes")
|
||||
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
|
||||
|
||||
return definition_changes_container
|
||||
|
|
|
@ -15,6 +15,7 @@ from UM.Settings.SettingFunction import SettingFunction
|
|||
from UM.Settings.SettingInstance import SettingInstance
|
||||
from UM.Settings.ContainerStack import ContainerStack
|
||||
from UM.Settings.PropertyEvaluationContext import PropertyEvaluationContext
|
||||
|
||||
from typing import Optional, List, TYPE_CHECKING, Union
|
||||
|
||||
if TYPE_CHECKING:
|
||||
|
@ -29,6 +30,10 @@ class ExtruderManager(QObject):
|
|||
|
||||
## Registers listeners and such to listen to changes to the extruders.
|
||||
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)
|
||||
|
||||
self._application = Application.getInstance()
|
||||
|
@ -92,28 +97,6 @@ class ExtruderManager(QObject):
|
|||
if extruder.getId() == extruder_stack_id:
|
||||
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.
|
||||
#
|
||||
# \param index The index of the new active extruder.
|
||||
|
@ -557,6 +540,11 @@ class ExtruderManager(QObject):
|
|||
|
||||
return result
|
||||
|
||||
## Return the default extruder position from the machine manager
|
||||
@staticmethod
|
||||
def getDefaultExtruderPosition() -> str:
|
||||
return Application.getInstance().getMachineManager().defaultExtruderPosition
|
||||
|
||||
## Get all extruder values for a certain setting.
|
||||
#
|
||||
# This is exposed to qml for display purposes
|
||||
|
@ -747,3 +735,9 @@ class ExtruderManager(QObject):
|
|||
resolved_value = global_stack.getProperty(key, "value", context = context)
|
||||
|
||||
return resolved_value
|
||||
|
||||
__instance = None
|
||||
|
||||
@classmethod
|
||||
def getInstance(cls, *args, **kwargs) -> "ExtruderManager":
|
||||
return cls.__instance
|
||||
|
|
|
@ -25,8 +25,8 @@ if TYPE_CHECKING:
|
|||
#
|
||||
#
|
||||
class ExtruderStack(CuraContainerStack):
|
||||
def __init__(self, container_id: str, *args, **kwargs):
|
||||
super().__init__(container_id, *args, **kwargs)
|
||||
def __init__(self, container_id: str):
|
||||
super().__init__(container_id)
|
||||
|
||||
self.addMetaDataEntry("type", "extruder_train") # For backward compatibility
|
||||
|
||||
|
|
|
@ -23,8 +23,8 @@ from .CuraContainerStack import CuraContainerStack
|
|||
## Represents the Global or Machine stack and its related containers.
|
||||
#
|
||||
class GlobalStack(CuraContainerStack):
|
||||
def __init__(self, container_id: str, *args, **kwargs):
|
||||
super().__init__(container_id, *args, **kwargs)
|
||||
def __init__(self, container_id: str):
|
||||
super().__init__(container_id)
|
||||
|
||||
self.addMetaDataEntry("type", "machine") # For backward compatibility
|
||||
|
||||
|
|
|
@ -17,7 +17,6 @@ from UM.FlameProfiler import pyqtSlot
|
|||
from UM import Util
|
||||
|
||||
from UM.Application import Application
|
||||
from UM.Preferences import Preferences
|
||||
from UM.Logger import Logger
|
||||
from UM.Message import Message
|
||||
|
||||
|
@ -98,12 +97,12 @@ class MachineManager(QObject):
|
|||
ExtruderManager.getInstance().activeExtruderChanged.connect(self.activeStackChanged)
|
||||
self.activeStackChanged.connect(self.activeStackValueChanged)
|
||||
|
||||
Preferences.getInstance().addPreference("cura/active_machine", "")
|
||||
self._application.getPreferences().addPreference("cura/active_machine", "")
|
||||
|
||||
self._global_event_keys = set()
|
||||
|
||||
self._printer_output_devices = [] # type: List[PrinterOutputDevice]
|
||||
Application.getInstance().getOutputDeviceManager().outputDevicesChanged.connect(self._onOutputDevicesChanged)
|
||||
self._application.getOutputDeviceManager().outputDevicesChanged.connect(self._onOutputDevicesChanged)
|
||||
# There might already be some output devices by the time the signal is connected
|
||||
self._onOutputDevicesChanged()
|
||||
|
||||
|
@ -164,14 +163,14 @@ class MachineManager(QObject):
|
|||
rootMaterialChanged = pyqtSignal()
|
||||
|
||||
def setInitialActiveMachine(self) -> None:
|
||||
active_machine_id = Preferences.getInstance().getValue("cura/active_machine")
|
||||
active_machine_id = self._application.getPreferences().getValue("cura/active_machine")
|
||||
if active_machine_id != "" and ContainerRegistry.getInstance().findContainerStacksMetadata(id = active_machine_id):
|
||||
# An active machine was saved, so restore it.
|
||||
self.setActiveMachine(active_machine_id)
|
||||
|
||||
def _onOutputDevicesChanged(self) -> None:
|
||||
self._printer_output_devices = []
|
||||
for printer_output_device in Application.getInstance().getOutputDeviceManager().getOutputDevices():
|
||||
for printer_output_device in self._application.getOutputDeviceManager().getOutputDevices():
|
||||
if isinstance(printer_output_device, PrinterOutputDevice):
|
||||
self._printer_output_devices.append(printer_output_device)
|
||||
|
||||
|
@ -238,7 +237,7 @@ class MachineManager(QObject):
|
|||
extruder_stack.containersChanged.disconnect(self._onContainersChanged)
|
||||
|
||||
# Update the local global container stack reference
|
||||
self._global_container_stack = Application.getInstance().getGlobalContainerStack()
|
||||
self._global_container_stack = self._application.getGlobalContainerStack()
|
||||
if self._global_container_stack:
|
||||
self.updateDefaultExtruder()
|
||||
self.updateNumberExtrudersEnabled()
|
||||
|
@ -246,7 +245,7 @@ class MachineManager(QObject):
|
|||
|
||||
# after switching the global stack we reconnect all the signals and set the variant and material references
|
||||
if self._global_container_stack:
|
||||
Preferences.getInstance().setValue("cura/active_machine", self._global_container_stack.getId())
|
||||
self._application.getPreferences().setValue("cura/active_machine", self._global_container_stack.getId())
|
||||
|
||||
self._global_container_stack.nameChanged.connect(self._onMachineNameChanged)
|
||||
self._global_container_stack.containersChanged.connect(self._onContainersChanged)
|
||||
|
@ -270,7 +269,7 @@ class MachineManager(QObject):
|
|||
|
||||
if self._global_container_stack.getId() in self.machine_extruder_material_update_dict:
|
||||
for func in self.machine_extruder_material_update_dict[self._global_container_stack.getId()]:
|
||||
Application.getInstance().callLater(func)
|
||||
self._application.callLater(func)
|
||||
del self.machine_extruder_material_update_dict[self._global_container_stack.getId()]
|
||||
|
||||
self.activeQualityGroupChanged.emit()
|
||||
|
@ -364,7 +363,7 @@ class MachineManager(QObject):
|
|||
return # We're done here
|
||||
ExtruderManager.getInstance().setActiveExtruderIndex(0) # Switch to first extruder
|
||||
self._global_container_stack = global_stack
|
||||
Application.getInstance().setGlobalContainerStack(global_stack)
|
||||
self._application.setGlobalContainerStack(global_stack)
|
||||
ExtruderManager.getInstance()._globalContainerStackChanged()
|
||||
self._initMachineState(containers[0])
|
||||
self._onGlobalContainerChanged()
|
||||
|
@ -655,6 +654,15 @@ class MachineManager(QObject):
|
|||
|
||||
return ""
|
||||
|
||||
@pyqtProperty(str, notify = activeVariantChanged)
|
||||
def activeVariantId(self) -> str:
|
||||
if self._active_container_stack:
|
||||
variant = self._active_container_stack.variant
|
||||
if variant:
|
||||
return variant.getId()
|
||||
|
||||
return ""
|
||||
|
||||
@pyqtProperty(str, notify = activeVariantChanged)
|
||||
def activeVariantBuildplateName(self) -> str:
|
||||
if self._global_container_stack:
|
||||
|
@ -838,7 +846,7 @@ class MachineManager(QObject):
|
|||
## Set the amount of extruders on the active machine (global stack)
|
||||
# \param extruder_count int the number of extruders to set
|
||||
def setActiveMachineExtruderCount(self, extruder_count: int) -> None:
|
||||
extruder_manager = Application.getInstance().getExtruderManager()
|
||||
extruder_manager = self._application.getExtruderManager()
|
||||
|
||||
definition_changes_container = self._global_container_stack.definitionChanges
|
||||
if not self._global_container_stack or definition_changes_container == self._empty_definition_changes_container:
|
||||
|
@ -855,7 +863,7 @@ class MachineManager(QObject):
|
|||
self.correctExtruderSettings()
|
||||
|
||||
# Check to see if any objects are set to print with an extruder that will no longer exist
|
||||
root_node = Application.getInstance().getController().getScene().getRoot()
|
||||
root_node = self._application.getController().getScene().getRoot()
|
||||
for node in DepthFirstIterator(root_node):
|
||||
if node.getMeshData():
|
||||
extruder_nr = node.callDecoration("getActiveExtruderPosition")
|
||||
|
@ -888,7 +896,7 @@ class MachineManager(QObject):
|
|||
global_user_container.removeInstance(setting_key)
|
||||
|
||||
# Signal that the global stack has changed
|
||||
Application.getInstance().globalContainerStackChanged.emit()
|
||||
self._application.globalContainerStackChanged.emit()
|
||||
self.forceUpdateAllSettings()
|
||||
|
||||
@pyqtSlot(int, result = QObject)
|
||||
|
@ -982,6 +990,14 @@ class MachineManager(QObject):
|
|||
container = extruder.userChanges
|
||||
container.setProperty(setting_name, property_name, property_value)
|
||||
|
||||
## Reset all setting properties of a setting for all extruders.
|
||||
# \param setting_name The ID of the setting to reset.
|
||||
@pyqtSlot(str)
|
||||
def resetSettingForAllExtruders(self, setting_name: str) -> None:
|
||||
for key, extruder in self._global_container_stack.extruders.items():
|
||||
container = extruder.userChanges
|
||||
container.removeInstance(setting_name)
|
||||
|
||||
@pyqtProperty("QVariantList", notify = globalContainerChanged)
|
||||
def currentExtruderPositions(self) -> List[str]:
|
||||
if self._global_container_stack is None:
|
||||
|
@ -1032,6 +1048,10 @@ class MachineManager(QObject):
|
|||
self.activeQualityChangesGroupChanged.emit()
|
||||
|
||||
def _setQualityGroup(self, quality_group, empty_quality_changes: bool = True) -> None:
|
||||
if quality_group is None:
|
||||
self._setEmptyQuality()
|
||||
return
|
||||
|
||||
if quality_group.node_for_global.getContainer() is None:
|
||||
return
|
||||
for node in quality_group.nodes_for_extruders.values():
|
||||
|
@ -1042,10 +1062,6 @@ class MachineManager(QObject):
|
|||
if empty_quality_changes:
|
||||
self._current_quality_changes_group = None
|
||||
|
||||
if quality_group is None:
|
||||
self._setEmptyQuality()
|
||||
return
|
||||
|
||||
# Set quality and quality_changes for the GlobalStack
|
||||
self._global_container_stack.quality = quality_group.node_for_global.getContainer()
|
||||
if empty_quality_changes:
|
||||
|
@ -1120,7 +1136,7 @@ class MachineManager(QObject):
|
|||
def _setGlobalVariant(self, container_node):
|
||||
self._global_container_stack.variant = container_node.getContainer()
|
||||
if not self._global_container_stack.variant:
|
||||
self._global_container_stack.variant = Application.getInstance().empty_variant_container
|
||||
self._global_container_stack.variant = self._application.empty_variant_container
|
||||
|
||||
def _setMaterial(self, position, container_node = None):
|
||||
if container_node and container_node.getContainer():
|
||||
|
@ -1280,6 +1296,10 @@ class MachineManager(QObject):
|
|||
self._global_container_stack.variant = self._empty_variant_container
|
||||
self._updateQualityWithMaterial()
|
||||
|
||||
# See if we need to show the Discard or Keep changes screen
|
||||
if self.hasUserSettings and self._application.getPreferences().getValue("cura/active_mode") == 1:
|
||||
self._application.discardOrKeepProfileChanges()
|
||||
|
||||
## Find all container stacks that has the pair 'key = value' in its metadata and replaces the value with 'new_value'
|
||||
def replaceContainersMetadata(self, key: str, value: str, new_value: str) -> None:
|
||||
machines = ContainerRegistry.getInstance().findContainerStacks(type = "machine")
|
||||
|
@ -1331,6 +1351,10 @@ class MachineManager(QObject):
|
|||
self._setMaterial(position, container_node)
|
||||
self._updateQualityWithMaterial()
|
||||
|
||||
# See if we need to show the Discard or Keep changes screen
|
||||
if self.hasUserSettings and self._application.getPreferences().getValue("cura/active_mode") == 1:
|
||||
self._application.discardOrKeepProfileChanges()
|
||||
|
||||
@pyqtSlot(str, str)
|
||||
def setVariantByName(self, position: str, variant_name: str) -> None:
|
||||
machine_definition_id = self._global_container_stack.definition.id
|
||||
|
@ -1346,6 +1370,10 @@ class MachineManager(QObject):
|
|||
self._updateMaterialWithVariant(position)
|
||||
self._updateQualityWithMaterial()
|
||||
|
||||
# See if we need to show the Discard or Keep changes screen
|
||||
if self.hasUserSettings and self._application.getPreferences().getValue("cura/active_mode") == 1:
|
||||
self._application.discardOrKeepProfileChanges()
|
||||
|
||||
@pyqtSlot(str)
|
||||
def setQualityGroupByQualityType(self, quality_type: str) -> None:
|
||||
if self._global_container_stack is None:
|
||||
|
@ -1362,7 +1390,7 @@ class MachineManager(QObject):
|
|||
self._setQualityGroup(quality_group)
|
||||
|
||||
# See if we need to show the Discard or Keep changes screen
|
||||
if not no_dialog and self.hasUserSettings and Preferences.getInstance().getValue("cura/active_mode") == 1:
|
||||
if not no_dialog and self.hasUserSettings and self._application.getPreferences().getValue("cura/active_mode") == 1:
|
||||
self._application.discardOrKeepProfileChanges()
|
||||
|
||||
@pyqtProperty(QObject, fset = setQualityGroup, notify = activeQualityGroupChanged)
|
||||
|
@ -1376,7 +1404,7 @@ class MachineManager(QObject):
|
|||
self._setQualityChangesGroup(quality_changes_group)
|
||||
|
||||
# See if we need to show the Discard or Keep changes screen
|
||||
if not no_dialog and self.hasUserSettings and Preferences.getInstance().getValue("cura/active_mode") == 1:
|
||||
if not no_dialog and self.hasUserSettings and self._application.getPreferences().getValue("cura/active_mode") == 1:
|
||||
self._application.discardOrKeepProfileChanges()
|
||||
|
||||
@pyqtSlot()
|
||||
|
|
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()
|
35
cura_app.py
35
cura_app.py
|
@ -1,9 +1,10 @@
|
|||
#!/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.
|
||||
|
||||
import argparse
|
||||
import faulthandler
|
||||
import os
|
||||
import sys
|
||||
|
||||
|
@ -27,7 +28,7 @@ known_args = vars(parser.parse_known_args()[0])
|
|||
if not known_args["debug"]:
|
||||
def get_cura_dir_path():
|
||||
if Platform.isWindows():
|
||||
return os.path.expanduser("~/AppData/Roaming/cura/")
|
||||
return os.path.expanduser("~/AppData/Roaming/cura")
|
||||
elif Platform.isLinux():
|
||||
return os.path.expanduser("~/.local/share/cura")
|
||||
elif Platform.isOSX():
|
||||
|
@ -39,13 +40,10 @@ if not known_args["debug"]:
|
|||
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
|
||||
import faulthandler
|
||||
|
||||
#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
|
||||
# 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.
|
||||
try:
|
||||
import ctypes
|
||||
|
@ -79,6 +77,7 @@ if "PYTHONPATH" in os.environ.keys(): # If PYTHONPATH is u
|
|||
sys.path.remove(PATH_real)
|
||||
sys.path.insert(1, PATH_real) # Insert it at 1 after os.curdir, which is 0.
|
||||
|
||||
|
||||
def exceptHook(hook_type, value, traceback):
|
||||
from cura.CrashHandler import CrashHandler
|
||||
from cura.CuraApplication import CuraApplication
|
||||
|
@ -121,25 +120,25 @@ def exceptHook(hook_type, value, traceback):
|
|||
_crash_handler.early_crash_dialog.show()
|
||||
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
|
||||
# is a race condition between Arcus and PyQt. Importing Arcus
|
||||
# first seems to prevent Sip from going into a state where it
|
||||
# tries to create PyQt objects on a non-main thread.
|
||||
import Arcus #@UnusedImport
|
||||
import cura.CuraApplication
|
||||
import cura.Settings.CuraContainerRegistry
|
||||
from cura.CuraApplication import CuraApplication
|
||||
|
||||
faulthandler.enable()
|
||||
app = CuraApplication()
|
||||
app.addCommandLineOptions()
|
||||
app.parseCliOptions()
|
||||
app.initialize()
|
||||
|
||||
# Force an instance of CuraContainerRegistry to be created and reused later.
|
||||
cura.Settings.CuraContainerRegistry.CuraContainerRegistry.getInstance()
|
||||
app.startSplashWindowPhase()
|
||||
app.startPostSplashWindowPhase()
|
||||
|
||||
# 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()
|
||||
|
|
|
@ -27,14 +27,6 @@ from cura.Machines.QualityManager import getMachineDefinitionIDForQualitySearch
|
|||
MYPY = False
|
||||
|
||||
|
||||
MimeTypeDatabase.addMimeType(
|
||||
MimeType(
|
||||
name = "application/x-cura-project-file",
|
||||
comment = "Cura Project File",
|
||||
suffixes = ["curaproject.3mf"]
|
||||
)
|
||||
)
|
||||
|
||||
try:
|
||||
if not MYPY:
|
||||
import xml.etree.cElementTree as ET
|
||||
|
@ -42,10 +34,20 @@ except ImportError:
|
|||
Logger.log("w", "Unable to load cElementTree, switching to slower version")
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
|
||||
## Base implementation for reading 3MF files. Has no support for textures. Only loads meshes!
|
||||
class ThreeMFReader(MeshReader):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
def __init__(self, application):
|
||||
super().__init__(application)
|
||||
|
||||
MimeTypeDatabase.addMimeType(
|
||||
MimeType(
|
||||
name = "application/vnd.ms-package.3dmanufacturing-3dmodel+xml",
|
||||
comment="3MF",
|
||||
suffixes=["3mf"]
|
||||
)
|
||||
)
|
||||
|
||||
self._supported_extensions = [".3mf"]
|
||||
self._root = None
|
||||
self._base_name = ""
|
||||
|
@ -158,7 +160,7 @@ class ThreeMFReader(MeshReader):
|
|||
um_node.addDecorator(sliceable_decorator)
|
||||
return um_node
|
||||
|
||||
def read(self, file_name):
|
||||
def _read(self, file_name):
|
||||
result = []
|
||||
self._object_count = 0 # Used to name objects as there is no node name yet.
|
||||
# The base object of 3mf is a zipped archive.
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
from configparser import ConfigParser
|
||||
import zipfile
|
||||
import os
|
||||
import threading
|
||||
from typing import List, Tuple
|
||||
|
||||
|
||||
|
@ -21,7 +20,7 @@ from UM.Settings.ContainerStack import ContainerStack
|
|||
from UM.Settings.DefinitionContainer import DefinitionContainer
|
||||
from UM.Settings.InstanceContainer import InstanceContainer
|
||||
from UM.Settings.ContainerRegistry import ContainerRegistry
|
||||
from UM.MimeTypeDatabase import MimeTypeDatabase
|
||||
from UM.MimeTypeDatabase import MimeTypeDatabase, MimeType
|
||||
from UM.Job import Job
|
||||
from UM.Preferences import Preferences
|
||||
|
||||
|
@ -84,6 +83,15 @@ class ExtruderInfo:
|
|||
class ThreeMFWorkspaceReader(WorkspaceReader):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
MimeTypeDatabase.addMimeType(
|
||||
MimeType(
|
||||
name="application/x-cura-project-file",
|
||||
comment="Cura Project File",
|
||||
suffixes=["curaproject.3mf"]
|
||||
)
|
||||
)
|
||||
|
||||
self._supported_extensions = [".3mf"]
|
||||
self._dialog = WorkspaceDialog()
|
||||
self._3mf_mesh_reader = None
|
||||
|
@ -456,7 +464,7 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
|
|||
num_visible_settings = len(visible_settings_string.split(";"))
|
||||
active_mode = temp_preferences.getValue("cura/active_mode")
|
||||
if not active_mode:
|
||||
active_mode = Preferences.getInstance().getValue("cura/active_mode")
|
||||
active_mode = Application.getInstance().getPreferences().getValue("cura/active_mode")
|
||||
except KeyError:
|
||||
# If there is no preferences file, it's not a workspace, so notify user of failure.
|
||||
Logger.log("w", "File %s is not a valid workspace.", file_name)
|
||||
|
@ -575,7 +583,7 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
|
|||
temp_preferences.deserialize(serialized)
|
||||
|
||||
# Copy a number of settings from the temp preferences to the global
|
||||
global_preferences = Preferences.getInstance()
|
||||
global_preferences = application.getInstance().getPreferences()
|
||||
|
||||
visible_settings = temp_preferences.getValue("general/visible_settings")
|
||||
if visible_settings is None:
|
||||
|
|
|
@ -13,8 +13,10 @@ from . import ThreeMFWorkspaceReader
|
|||
|
||||
from UM.i18n import i18nCatalog
|
||||
from UM.Platform import Platform
|
||||
|
||||
catalog = i18nCatalog("cura")
|
||||
|
||||
|
||||
def getMetaData() -> Dict:
|
||||
# Workarround for osx not supporting double file extensions correctly.
|
||||
if Platform.isOSX():
|
||||
|
@ -42,7 +44,7 @@ def getMetaData() -> Dict:
|
|||
|
||||
def register(app):
|
||||
if "3MFReader.ThreeMFReader" in sys.modules:
|
||||
return {"mesh_reader": ThreeMFReader.ThreeMFReader(),
|
||||
return {"mesh_reader": ThreeMFReader.ThreeMFReader(app),
|
||||
"workspace_reader": ThreeMFWorkspaceReader.ThreeMFWorkspaceReader()}
|
||||
else:
|
||||
return {}
|
||||
|
|
|
@ -51,7 +51,7 @@ class ThreeMFWorkspaceWriter(WorkspaceWriter):
|
|||
self._writeContainerToArchive(container, archive)
|
||||
|
||||
# Write preferences to archive
|
||||
original_preferences = Preferences.getInstance() #Copy only the preferences that we use to the workspace.
|
||||
original_preferences = Application.getInstance().getPreferences() #Copy only the preferences that we use to the workspace.
|
||||
temp_preferences = Preferences()
|
||||
for preference in {"general/visible_settings", "cura/active_mode", "cura/categories_expanded"}:
|
||||
temp_preferences.addPreference(preference, None)
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
|
||||
from UM.i18n import i18nCatalog
|
||||
from UM.Extension import Extension
|
||||
from UM.Preferences import Preferences
|
||||
from UM.Application import Application
|
||||
from UM.PluginRegistry import PluginRegistry
|
||||
from UM.Version import Version
|
||||
|
@ -29,7 +28,7 @@ class ChangeLog(Extension, QObject,):
|
|||
|
||||
self._change_logs = None
|
||||
Application.getInstance().engineCreatedSignal.connect(self._onEngineCreated)
|
||||
Preferences.getInstance().addPreference("general/latest_version_changelog_shown", "2.0.0") #First version of CURA with uranium
|
||||
Application.getInstance().getPreferences().addPreference("general/latest_version_changelog_shown", "2.0.0") #First version of CURA with uranium
|
||||
self.addMenuItem(catalog.i18nc("@item:inmenu", "Show Changelog"), self.showChangelog)
|
||||
|
||||
def getChangeLogs(self):
|
||||
|
@ -79,12 +78,12 @@ class ChangeLog(Extension, QObject,):
|
|||
if not self._current_app_version:
|
||||
return #We're on dev branch.
|
||||
|
||||
if Preferences.getInstance().getValue("general/latest_version_changelog_shown") == "master":
|
||||
if Application.getInstance().getPreferences().getValue("general/latest_version_changelog_shown") == "master":
|
||||
latest_version_shown = Version("0.0.0")
|
||||
else:
|
||||
latest_version_shown = Version(Preferences.getInstance().getValue("general/latest_version_changelog_shown"))
|
||||
latest_version_shown = Version(Application.getInstance().getPreferences().getValue("general/latest_version_changelog_shown"))
|
||||
|
||||
Preferences.getInstance().setValue("general/latest_version_changelog_shown", Application.getInstance().getVersion())
|
||||
Application.getInstance().getPreferences().setValue("general/latest_version_changelog_shown", Application.getInstance().getVersion())
|
||||
|
||||
# Do not show the changelog when there is no global container stack
|
||||
# This implies we are running Cura for the first time.
|
||||
|
|
|
@ -1,3 +1,127 @@
|
|||
|
||||
|
||||
[3.4.0]
|
||||
|
||||
*Toolbox
|
||||
The plugin browser has been remodeled into the Toolbox. Navigation now involves graphical elements such as tiles, which can be clicked for further details.
|
||||
|
||||
*Upgradable bundled resources
|
||||
It is now possible to have multiple versions of bundled resources installed: the bundled version and the downloaded upgrade. If an upgrade in the form of a package is present, the bundled version will not be loaded. If it's not present, Ultimaker Cura will revert to the bundled version.
|
||||
|
||||
*Package manager recognizes bundled resources
|
||||
Bundled packages are now made visible to the CuraPackageMangager. This means the resources are included by default, as well as the "wrapping" of a package, (e.g. package.json) so that the CuraPackageManger and Toolbox recognize them as being installed.
|
||||
|
||||
*Retraction combing max distance
|
||||
New setting for maximum combing travel distance. Combing travel moves longer than this value will use retraction. Contributed by smartavionics.
|
||||
|
||||
*Infill support
|
||||
When enabled, infill will be generated only where it is needed using a specialized support generation algorithm for the internal support structures of a part. Contributed by BagelOrb.
|
||||
|
||||
*Print outside perimeter before holes
|
||||
This prioritizes outside perimeters before printing holes. By printing holes as late as possible, there is a reduced risk of travel moves dislodging them from the build plate. This setting should only have an effect if printing outer before inner walls. Contributed by smartavionics.
|
||||
|
||||
*Disable omitting retractions in support
|
||||
Previous versions had no option to disable omitting retraction moves when printing supports, which could cause issues with third-party machines or materials. An option has been added to disable this. Contributed by BagelOrb.
|
||||
|
||||
*Support wall line count
|
||||
Added setting to configure how many walls to print around supports. Contributed by BagelOrb.
|
||||
|
||||
*Maximum combing resolution
|
||||
Combing travel moves are kept at least 1.5 mm long to prevent buffer underruns.
|
||||
|
||||
*Avoid supports when traveling
|
||||
Added setting to avoid supports when performing travel moves. This minimizes the risk of the print head hitting support material.
|
||||
|
||||
*Rewrite cross infill
|
||||
Experimental setting that allows you to input a path to an image to manipulate the cross infill density. This will overlay that image on your model. Contributed by BagelOrb.
|
||||
|
||||
*Backup and restore
|
||||
Added functionality to backup and restore settings and profiles to cloud using the Cura Backups plugin.
|
||||
|
||||
*Auto-select model after import
|
||||
User can now set preferences for the behavior of selecting a newly imported model or not.
|
||||
|
||||
*Settings filter timeout
|
||||
The settings filter is triggered on enter or after a 500ms timeout when typing a setting to filter.
|
||||
|
||||
*Event measurements
|
||||
Added time measurement to logs for occurrences, including startup time, file load time, number of items on the build plate when slicing, slicing time, and time and performance when moving items on the build plate, for benchmarking purposes.
|
||||
|
||||
*Send anonymous data
|
||||
Disable button on the ‘Send anonymous data’ popup has changed to a ‘more info’ button, with further options to enable/disable anonymous data messages.
|
||||
|
||||
*Configuration error assistant
|
||||
Detect and show potential configuration file errors to users, e.g. incorrect files and duplicate files in material or quality profiles, there are several places to check. Information is stored and communicated to the user to prevent crashing in future.
|
||||
|
||||
*Disable ensure models are kept apart
|
||||
Disable "Ensure models are kept apart" by default due to to a change in preference files.
|
||||
|
||||
*Prepare and monitor QML files
|
||||
Created two separate QML files for the Prepare and Monitor stages.
|
||||
|
||||
*Hide bed temperature
|
||||
Option to hide bed temperature when no heated bed is present. Contributed by ngraziano.
|
||||
|
||||
*Reprap/Marlin GCODE flavor
|
||||
RepRap firmware now lists values for all extruders in the "Filament used" GCODE comment. Contributed by smartavionics.
|
||||
|
||||
*AutoDesk Inventor integration
|
||||
Open AutoDesk inventor files (parts, assemblies, drawings) directly into Ultimaker Cura. Contributed by thopiekar.
|
||||
|
||||
*Blender integration
|
||||
Open Blender files directly into Ultimaker Cura. Contributed by thopiekar.
|
||||
|
||||
*OpenSCAD integration
|
||||
Open OpenSCAD files directly into Ultimaker Cura. Contributed by thopiekar.
|
||||
|
||||
*FreeCAD integration
|
||||
Open FreeCAD files directly into Ultimaker Cura. Contributed by thopiekar.
|
||||
|
||||
*OctoPrint plugin
|
||||
New version of the OctoPrint plugin for Ultimaker Cura. Contributed by fieldOfView.
|
||||
|
||||
*Cura Backups
|
||||
Backup and restore your configuration, including settings, materials and plugins, for use across different systems.
|
||||
|
||||
*MakePrintable
|
||||
New version of the MakePrintable plugin.
|
||||
|
||||
*Compact Prepare sidebar
|
||||
Plugin that replaces the sidebar with a more compact variation of the original sidebar. Nozzle and material dropdowns are combined into a single line, the “Check compatibility” link is removed, extruder selection buttons are downsized, recommended and custom mode selection buttons are moved to a combobox at the top, and margins are tweaked. Contributed by fieldOfView.
|
||||
|
||||
*PauseAtHeight plugin
|
||||
Bug fixes and improvements for PauseAtHeight plugin. Plugin now accounts for raft layers when choosing “Pause of layer no.” Now positions the nozzle at x and y values of the next layer when resuming. Contributed by JPFrancoia.
|
||||
|
||||
*Bug fixes
|
||||
- Prime tower purge fix. Prime tower purge now starts away from the center, minimizing the chance of overextrusion and nozzle obstructions. Contributed by BagelOrb.
|
||||
- Extruder 2 temp via USB. Fixed a bug where temperatures can’t be read for a second extruder via USB. Contributed by kirilledelman.
|
||||
- Move to next object position before bed heat. Print one at a time mode caused waiting for the bed temperature to reach the first layer temperature while the nozzle was still positioned on the top of the last part. This has been fixed so that the nozzle moves to the location of the next part before waiting for heat up. Contributed by smartavionics.
|
||||
- Non-GCODE USB. Fixed a bug where the USB port doesn’t open if printer doesn't support GCODE. Contributed by ohrn.
|
||||
- Improved wall overlap compensation. Minimizes unexpected behavior on overlap lines, providing smoother results. Contributed by BagelOrb.
|
||||
- Configuration/sync. Fixes minor issues with the configuration/sync menu, such as text rendering on some OSX systems and untranslatable text. Contributed by fieldOfView.
|
||||
- Print job name reslice. Fixed behavior where print job name changes back to origin when reslicing.
|
||||
- Discard/keep. Customized settings don't give an 'discard or keep' dialog when changing material.
|
||||
- Message box styling. Fixed bugs related to message box styling, such as the progress bar overlapping the button in the ‘Sending Data’ popup.
|
||||
- Curaproject naming. Fixed bug related to two "curaprojects" in the file name when saving a project.
|
||||
- No support on first layers. Fixed a bug related to no support generated causing failed prints when model is floating above build plate.
|
||||
- False incompatible configuration. Fixed a bug where PrintCore and materials were flagged even though the configurations are compatible.
|
||||
- Spiralize contour overlaps. Fixed a bug related to spiralize contour overlaps.
|
||||
- Model saved outside build volume. Fixed a bug that would saved a model to file (GCODE) outside the build volume.
|
||||
- Filament diameter line width. Adjust filament diameter to calculate line width in the GCODE parser.
|
||||
- Holes in model surfaces. Fixed a bug where illogical travel moves leave holes in the model surface.
|
||||
- Nozzle legacy file variant. Fixed crashes caused by loading legacy nozzle variant files.
|
||||
- Brim wall order. Fixed a bug related to brim wall order. Contributed by smartavionics.
|
||||
- GCODE reader gaps. Fixed a GCODE reader bug that can create a gap at the start of a spiralized layer.
|
||||
- Korean translation. Fixed some typos in Korean translation.
|
||||
- ARM/Mali systems. Graphics pipeline for ARM/Mali fixed. Contributed by jwalt.
|
||||
- NGC Writer. Fixed missing author for NGC Writer plugin.
|
||||
- Support blocker legacy GPU. Fixes depth picking on older GPUs that do not support the 4.1 shading model which caused the support blocker to put cubes in unexpected locations. Contributed by fieldOfView.
|
||||
|
||||
*Third-party printers
|
||||
- Felix Tec4 printer. Updated definitions for Felix Tec4. Contributed by kerog777.
|
||||
- Deltacomb. Updated definitions for Deltacomb. Contributed by kaleidoscopeit.
|
||||
- Rigid3D Mucit. Added definitions for Rigid3D Mucit. Contributed by Rigid3D.
|
||||
|
||||
[3.3.0]
|
||||
|
||||
*Profile for the Ultimaker S5
|
||||
|
@ -66,7 +190,7 @@ Generate a cube mesh to prevent support material generation in specific areas of
|
|||
*Real bridging - smartavionics
|
||||
New experimental feature that detects bridges, adjusting the print speed, slow and fan speed to enhance print quality on bridging parts.
|
||||
|
||||
*Updated CuraEngine executable - thopiekar & Ultimaker B.V. ❤️
|
||||
*Updated CuraEngine executable - thopiekar & Ultimaker B.V.
|
||||
The CuraEngine executable contains a dedicated icon, author and license info on Windows now. The icon has been designed by Ultimaker B.V.
|
||||
|
||||
*Use RapidJSON and ClipperLib from system libraries
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
from UM.Backend.Backend import Backend, BackendState
|
||||
from UM.Application import Application
|
||||
from UM.Scene.SceneNode import SceneNode
|
||||
from UM.Preferences import Preferences
|
||||
from UM.Signal import Signal
|
||||
from UM.Logger import Logger
|
||||
from UM.Message import Message
|
||||
|
@ -72,7 +71,7 @@ class CuraEngineBackend(QObject, Backend):
|
|||
Logger.log("i", "Found CuraEngine at: %s", default_engine_location)
|
||||
|
||||
default_engine_location = os.path.abspath(default_engine_location)
|
||||
Preferences.getInstance().addPreference("backend/location", default_engine_location)
|
||||
Application.getInstance().getPreferences().addPreference("backend/location", default_engine_location)
|
||||
|
||||
# Workaround to disable layer view processing if layer view is not active.
|
||||
self._layer_view_active = False
|
||||
|
@ -121,7 +120,7 @@ class CuraEngineBackend(QObject, Backend):
|
|||
self._slice_start_time = None
|
||||
self._is_disabled = False
|
||||
|
||||
Preferences.getInstance().addPreference("general/auto_slice", False)
|
||||
Application.getInstance().getPreferences().addPreference("general/auto_slice", False)
|
||||
|
||||
self._use_timer = False
|
||||
# When you update a setting and other settings get changed through inheritance, many propertyChanged signals are fired.
|
||||
|
@ -131,7 +130,7 @@ class CuraEngineBackend(QObject, Backend):
|
|||
self._change_timer.setSingleShot(True)
|
||||
self._change_timer.setInterval(500)
|
||||
self.determineAutoSlicing()
|
||||
Preferences.getInstance().preferenceChanged.connect(self._onPreferencesChanged)
|
||||
Application.getInstance().getPreferences().preferenceChanged.connect(self._onPreferencesChanged)
|
||||
|
||||
self._application.initializationFinished.connect(self.initialize)
|
||||
|
||||
|
@ -170,7 +169,7 @@ class CuraEngineBackend(QObject, Backend):
|
|||
# \return list of commands and args / parameters.
|
||||
def getEngineCommand(self):
|
||||
json_path = Resources.getPath(Resources.DefinitionContainers, "fdmprinter.def.json")
|
||||
return [Preferences.getInstance().getValue("backend/location"), "connect", "127.0.0.1:{0}".format(self._port), "-j", json_path, ""]
|
||||
return [Application.getInstance().getPreferences().getValue("backend/location"), "connect", "127.0.0.1:{0}".format(self._port), "-j", json_path, ""]
|
||||
|
||||
## Emitted when we get a message containing print duration and material amount.
|
||||
# This also implies the slicing has finished.
|
||||
|
@ -275,7 +274,7 @@ class CuraEngineBackend(QObject, Backend):
|
|||
self.processingProgress.emit(0)
|
||||
Logger.log("d", "Attempting to kill the engine process")
|
||||
|
||||
if Application.getInstance().getCommandLineOption("external-backend", False):
|
||||
if Application.getInstance().getUseExternalBackend():
|
||||
return
|
||||
|
||||
if self._process is not None:
|
||||
|
@ -408,7 +407,7 @@ class CuraEngineBackend(QObject, Backend):
|
|||
enable_timer = True
|
||||
self._is_disabled = False
|
||||
|
||||
if not Preferences.getInstance().getValue("general/auto_slice"):
|
||||
if not Application.getInstance().getPreferences().getValue("general/auto_slice"):
|
||||
enable_timer = False
|
||||
for node in DepthFirstIterator(self._scene.getRoot()):
|
||||
if node.callDecoration("isBlockSlicing"):
|
||||
|
|
|
@ -6,7 +6,6 @@ import gc
|
|||
from UM.Job import Job
|
||||
from UM.Application import Application
|
||||
from UM.Mesh.MeshData import MeshData
|
||||
from UM.Preferences import Preferences
|
||||
from UM.View.GL.OpenGLContext import OpenGLContext
|
||||
|
||||
from UM.Message import Message
|
||||
|
@ -199,7 +198,7 @@ class ProcessSlicedLayersJob(Job):
|
|||
material_color_map[0, :] = color
|
||||
|
||||
# We have to scale the colors for compatibility mode
|
||||
if OpenGLContext.isLegacyOpenGL() or bool(Preferences.getInstance().getValue("view/force_layer_view_compatibility_mode")):
|
||||
if OpenGLContext.isLegacyOpenGL() or bool(Application.getInstance().getPreferences().getValue("view/force_layer_view_compatibility_mode")):
|
||||
line_type_brightness = 0.5 # for compatibility mode
|
||||
else:
|
||||
line_type_brightness = 1.0
|
||||
|
|
|
@ -287,14 +287,9 @@ class StartSliceJob(Job):
|
|||
# \return A dictionary of replacement tokens to the values they should be
|
||||
# replaced with.
|
||||
def _buildReplacementTokens(self, stack) -> dict:
|
||||
default_extruder_position = int(Application.getInstance().getMachineManager().defaultExtruderPosition)
|
||||
result = {}
|
||||
for key in stack.getAllKeys():
|
||||
setting_type = stack.definition.getProperty(key, "type")
|
||||
value = stack.getProperty(key, "value")
|
||||
if setting_type == "extruder" and value == -1:
|
||||
# replace with the default value
|
||||
value = default_extruder_position
|
||||
result[key] = value
|
||||
Job.yieldThread()
|
||||
|
||||
|
|
|
@ -5,8 +5,7 @@ from PyQt5.QtCore import QUrl
|
|||
from PyQt5.QtGui import QDesktopServices
|
||||
|
||||
from UM.Extension import Extension
|
||||
from UM.Preferences import Preferences
|
||||
from UM.Logger import Logger
|
||||
from UM.Application import Application
|
||||
from UM.i18n import i18nCatalog
|
||||
from cura.Settings.GlobalStack import GlobalStack
|
||||
|
||||
|
@ -27,12 +26,12 @@ class FirmwareUpdateChecker(Extension):
|
|||
|
||||
# Initialize the Preference called `latest_checked_firmware` that stores the last version
|
||||
# checked for the UM3. In the future if we need to check other printers' firmware
|
||||
Preferences.getInstance().addPreference("info/latest_checked_firmware", "")
|
||||
Application.getInstance().getPreferences().addPreference("info/latest_checked_firmware", "")
|
||||
|
||||
# Listen to a Signal that indicates a change in the list of printers, just if the user has enabled the
|
||||
# 'check for updates' option
|
||||
Preferences.getInstance().addPreference("info/automatic_update_check", True)
|
||||
if Preferences.getInstance().getValue("info/automatic_update_check"):
|
||||
Application.getInstance().getPreferences().addPreference("info/automatic_update_check", True)
|
||||
if Application.getInstance().getPreferences().getValue("info/automatic_update_check"):
|
||||
ContainerRegistry.getInstance().containerAdded.connect(self._onContainerAdded)
|
||||
|
||||
self._download_url = None
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
# Copyright (c) 2017 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
from UM.Preferences import Preferences
|
||||
from UM.Application import Application
|
||||
from UM.Message import Message
|
||||
from UM.Logger import Logger
|
||||
|
@ -51,11 +50,11 @@ class FirmwareUpdateCheckerJob(Job):
|
|||
current_version = reader(current_version_file).readline().rstrip()
|
||||
|
||||
# If it is the first time the version is checked, the checked_version is ''
|
||||
checked_version = Preferences.getInstance().getValue("info/latest_checked_firmware")
|
||||
checked_version = Application.getInstance().getPreferences().getValue("info/latest_checked_firmware")
|
||||
|
||||
# If the checked_version is '', it's because is the first time we check firmware and in this case
|
||||
# we will not show the notification, but we will store it for the next time
|
||||
Preferences.getInstance().setValue("info/latest_checked_firmware", current_version)
|
||||
Application.getInstance().getPreferences().setValue("info/latest_checked_firmware", current_version)
|
||||
Logger.log("i", "Reading firmware version of %s: checked = %s - latest = %s", machine_name, checked_version, current_version)
|
||||
|
||||
# The first time we want to store the current version, the notification will not be shown,
|
||||
|
@ -63,13 +62,26 @@ class FirmwareUpdateCheckerJob(Job):
|
|||
# notify the user when no new firmware version is available.
|
||||
if (checked_version != "") and (checked_version != current_version):
|
||||
Logger.log("i", "SHOWING FIRMWARE UPDATE MESSAGE")
|
||||
message = Message(i18n_catalog.i18nc("@info Don't translate {machine_name}, since it gets replaced by a printer name!", "New features are available for your {machine_name}! It is recommended to update the firmware on your printer.").format(machine_name = machine_name),
|
||||
title = i18n_catalog.i18nc("@info:title The %s gets replaced with the printer name.", "New %s firmware available") % machine_name)
|
||||
message.addAction("download", i18n_catalog.i18nc("@action:button", "How to update"), "[no_icon]", "[no_description]")
|
||||
|
||||
message = Message(i18n_catalog.i18nc(
|
||||
"@info Don't translate {machine_name}, since it gets replaced by a printer name!",
|
||||
"New features are available for your {machine_name}! It is recommended to update the firmware on your printer.").format(
|
||||
machine_name=machine_name),
|
||||
title=i18n_catalog.i18nc(
|
||||
"@info:title The %s gets replaced with the printer name.",
|
||||
"New %s firmware available") % machine_name)
|
||||
|
||||
message.addAction("download",
|
||||
i18n_catalog.i18nc("@action:button", "How to update"),
|
||||
"[no_icon]",
|
||||
"[no_description]",
|
||||
button_style=Message.ActionButtonStyle.LINK,
|
||||
button_align=Message.ActionButtonStyle.BUTTON_ALIGN_LEFT)
|
||||
|
||||
|
||||
# If we do this in a cool way, the download url should be available in the JSON file
|
||||
if self._set_download_url_callback:
|
||||
self._set_download_url_callback("https://ultimaker.com/en/resources/23129-updating-the-firmware?utm_source=cura&utm_medium=software&utm_campaign=hw-update")
|
||||
self._set_download_url_callback("https://ultimaker.com/en/resources/20500-upgrade-firmware")
|
||||
message.actionTriggered.connect(self._callback)
|
||||
message.show()
|
||||
|
||||
|
|
|
@ -12,11 +12,11 @@ from UM.PluginRegistry import PluginRegistry
|
|||
# If you're zipping g-code, you might as well use gzip!
|
||||
class GCodeGzReader(MeshReader):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
def __init__(self, application):
|
||||
super().__init__(application)
|
||||
self._supported_extensions = [".gcode.gz"]
|
||||
|
||||
def read(self, file_name):
|
||||
def _read(self, file_name):
|
||||
with open(file_name, "rb") as file:
|
||||
file_data = file.read()
|
||||
uncompressed_gcode = gzip.decompress(file_data).decode("utf-8")
|
||||
|
|
|
@ -21,4 +21,4 @@ def getMetaData():
|
|||
|
||||
def register(app):
|
||||
app.addNonSliceableExtension(".gz")
|
||||
return { "mesh_reader": GCodeGzReader.GCodeGzReader() }
|
||||
return { "mesh_reader": GCodeGzReader.GCodeGzReader(app) }
|
||||
|
|
|
@ -10,7 +10,6 @@ from UM.Math.Vector import Vector
|
|||
from UM.Message import Message
|
||||
from cura.Scene.CuraSceneNode import CuraSceneNode
|
||||
from UM.i18n import i18nCatalog
|
||||
from UM.Preferences import Preferences
|
||||
|
||||
catalog = i18nCatalog("cura")
|
||||
|
||||
|
@ -47,7 +46,7 @@ class FlavorParser:
|
|||
self._current_layer_thickness = 0.2 # default
|
||||
self._filament_diameter = 2.85 # default
|
||||
|
||||
Preferences.getInstance().addPreference("gcodereader/show_caution", True)
|
||||
Application.getInstance().getPreferences().addPreference("gcodereader/show_caution", True)
|
||||
|
||||
def _clearValues(self) -> None:
|
||||
self._extruder_number = 0
|
||||
|
@ -462,7 +461,7 @@ class FlavorParser:
|
|||
|
||||
Logger.log("d", "GCode loading finished")
|
||||
|
||||
if Preferences.getInstance().getValue("gcodereader/show_caution"):
|
||||
if Application.getInstance().getPreferences().getValue("gcodereader/show_caution"):
|
||||
caution_message = Message(catalog.i18nc(
|
||||
"@info:generic",
|
||||
"Make sure the g-code is suitable for your printer and printer configuration before sending the file to it. The g-code representation may not be accurate."),
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
from UM.FileHandler.FileReader import FileReader
|
||||
from UM.Mesh.MeshReader import MeshReader
|
||||
from UM.i18n import i18nCatalog
|
||||
from UM.Preferences import Preferences
|
||||
from UM.Application import Application
|
||||
from UM.MimeTypeDatabase import MimeTypeDatabase, MimeType
|
||||
|
||||
catalog = i18nCatalog("cura")
|
||||
|
@ -27,12 +27,12 @@ class GCodeReader(MeshReader):
|
|||
_flavor_readers_dict = {"RepRap" : RepRapFlavorParser.RepRapFlavorParser(),
|
||||
"Marlin" : MarlinFlavorParser.MarlinFlavorParser()}
|
||||
|
||||
def __init__(self):
|
||||
super(GCodeReader, self).__init__()
|
||||
def __init__(self, application):
|
||||
super(GCodeReader, self).__init__(application)
|
||||
self._supported_extensions = [".gcode", ".g"]
|
||||
self._flavor_reader = None
|
||||
|
||||
Preferences.getInstance().addPreference("gcodereader/show_caution", True)
|
||||
Application.getInstance().getPreferences().addPreference("gcodereader/show_caution", True)
|
||||
|
||||
def preReadFromStream(self, stream, *args, **kwargs):
|
||||
for line in stream.split("\n"):
|
||||
|
@ -57,7 +57,7 @@ class GCodeReader(MeshReader):
|
|||
def readFromStream(self, stream):
|
||||
return self._flavor_reader.processGCodeStream(stream)
|
||||
|
||||
def read(self, file_name):
|
||||
def _read(self, file_name):
|
||||
with open(file_name, "r", encoding = "utf-8") as file:
|
||||
file_data = file.read()
|
||||
return self.readFromStream(file_data)
|
||||
|
|
|
@ -23,4 +23,4 @@ def getMetaData():
|
|||
def register(app):
|
||||
app.addNonSliceableExtension(".gcode")
|
||||
app.addNonSliceableExtension(".g")
|
||||
return { "mesh_reader": GCodeReader.GCodeReader() }
|
||||
return { "mesh_reader": GCodeReader.GCodeReader(app) }
|
||||
|
|
|
@ -17,8 +17,8 @@ from cura.Scene.CuraSceneNode import CuraSceneNode as SceneNode
|
|||
|
||||
|
||||
class ImageReader(MeshReader):
|
||||
def __init__(self):
|
||||
super(ImageReader, self).__init__()
|
||||
def __init__(self, application):
|
||||
super(ImageReader, self).__init__(application)
|
||||
self._supported_extensions = [".jpg", ".jpeg", ".bmp", ".gif", ".png"]
|
||||
self._ui = ImageReaderUI(self)
|
||||
|
||||
|
@ -44,7 +44,7 @@ class ImageReader(MeshReader):
|
|||
return MeshReader.PreReadResult.cancelled
|
||||
return MeshReader.PreReadResult.accepted
|
||||
|
||||
def read(self, file_name):
|
||||
def _read(self, file_name):
|
||||
size = max(self._ui.getWidth(), self._ui.getDepth())
|
||||
return self._generateSceneNode(file_name, size, self._ui.peak_height, self._ui.base_height, self._ui.smoothing, 512, self._ui.image_color_invert)
|
||||
|
||||
|
|
|
@ -33,4 +33,4 @@ def getMetaData():
|
|||
}
|
||||
|
||||
def register(app):
|
||||
return { "mesh_reader": ImageReader.ImageReader() }
|
||||
return { "mesh_reader": ImageReader.ImageReader(app) }
|
||||
|
|
|
@ -27,7 +27,7 @@ class ModelChecker(QObject, Extension):
|
|||
|
||||
self._caution_message = Message("", #Message text gets set when the message gets shown, to display the models in question.
|
||||
lifetime = 0,
|
||||
title = catalog.i18nc("@info:title", "Model Checker Warning"))
|
||||
title = catalog.i18nc("@info:title", "3D Model Assistant"))
|
||||
|
||||
Application.getInstance().initializationFinished.connect(self._pluginsInitialized)
|
||||
Application.getInstance().getController().getScene().sceneChanged.connect(self._onChanged)
|
||||
|
|
|
@ -5,7 +5,6 @@ from UM.Tool import Tool
|
|||
from UM.Scene.Selection import Selection
|
||||
from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
|
||||
from UM.Application import Application
|
||||
from UM.Preferences import Preferences
|
||||
from cura.Settings.SettingOverrideDecorator import SettingOverrideDecorator
|
||||
from cura.Settings.ExtruderManager import ExtruderManager
|
||||
from UM.Settings.SettingInstance import SettingInstance
|
||||
|
@ -27,7 +26,7 @@ class PerObjectSettingsTool(Tool):
|
|||
|
||||
Selection.selectionChanged.connect(self.propertyChanged)
|
||||
|
||||
Preferences.getInstance().preferenceChanged.connect(self._onPreferenceChanged)
|
||||
Application.getInstance().getPreferences().preferenceChanged.connect(self._onPreferenceChanged)
|
||||
self._onPreferenceChanged("cura/active_mode")
|
||||
|
||||
Application.getInstance().globalContainerStackChanged.connect(self._onGlobalContainerChanged)
|
||||
|
@ -106,7 +105,7 @@ class PerObjectSettingsTool(Tool):
|
|||
|
||||
def _onPreferenceChanged(self, preference):
|
||||
if preference == "cura/active_mode":
|
||||
self._advanced_mode = Preferences.getInstance().getValue(preference) == 1
|
||||
self._advanced_mode = Application.getInstance().getPreferences().getValue(preference) == 1
|
||||
self._updateEnabled()
|
||||
|
||||
def _onGlobalContainerChanged(self):
|
||||
|
|
|
@ -16,7 +16,6 @@ from UM.Mesh.MeshBuilder import MeshBuilder
|
|||
from UM.Message import Message
|
||||
from UM.Platform import Platform
|
||||
from UM.PluginRegistry import PluginRegistry
|
||||
from UM.Preferences import Preferences
|
||||
from UM.Resources import Resources
|
||||
from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
|
||||
from UM.Scene.Selection import Selection
|
||||
|
@ -81,30 +80,30 @@ class SimulationView(View):
|
|||
self._show_travel_moves = False
|
||||
self._nozzle_node = None
|
||||
|
||||
Preferences.getInstance().addPreference("view/top_layer_count", 5)
|
||||
Preferences.getInstance().addPreference("view/only_show_top_layers", False)
|
||||
Preferences.getInstance().addPreference("view/force_layer_view_compatibility_mode", False)
|
||||
Application.getInstance().getPreferences().addPreference("view/top_layer_count", 5)
|
||||
Application.getInstance().getPreferences().addPreference("view/only_show_top_layers", False)
|
||||
Application.getInstance().getPreferences().addPreference("view/force_layer_view_compatibility_mode", False)
|
||||
|
||||
Preferences.getInstance().addPreference("layerview/layer_view_type", 0)
|
||||
Preferences.getInstance().addPreference("layerview/extruder_opacities", "")
|
||||
Application.getInstance().getPreferences().addPreference("layerview/layer_view_type", 0)
|
||||
Application.getInstance().getPreferences().addPreference("layerview/extruder_opacities", "")
|
||||
|
||||
Preferences.getInstance().addPreference("layerview/show_travel_moves", False)
|
||||
Preferences.getInstance().addPreference("layerview/show_helpers", True)
|
||||
Preferences.getInstance().addPreference("layerview/show_skin", True)
|
||||
Preferences.getInstance().addPreference("layerview/show_infill", True)
|
||||
Application.getInstance().getPreferences().addPreference("layerview/show_travel_moves", False)
|
||||
Application.getInstance().getPreferences().addPreference("layerview/show_helpers", True)
|
||||
Application.getInstance().getPreferences().addPreference("layerview/show_skin", True)
|
||||
Application.getInstance().getPreferences().addPreference("layerview/show_infill", True)
|
||||
|
||||
Preferences.getInstance().preferenceChanged.connect(self._onPreferencesChanged)
|
||||
Application.getInstance().getPreferences().preferenceChanged.connect(self._onPreferencesChanged)
|
||||
self._updateWithPreferences()
|
||||
|
||||
self._solid_layers = int(Preferences.getInstance().getValue("view/top_layer_count"))
|
||||
self._only_show_top_layers = bool(Preferences.getInstance().getValue("view/only_show_top_layers"))
|
||||
self._solid_layers = int(Application.getInstance().getPreferences().getValue("view/top_layer_count"))
|
||||
self._only_show_top_layers = bool(Application.getInstance().getPreferences().getValue("view/only_show_top_layers"))
|
||||
self._compatibility_mode = self._evaluateCompatibilityMode()
|
||||
|
||||
self._wireprint_warning_message = Message(catalog.i18nc("@info:status", "Cura does not accurately display layers when Wire Printing is enabled"),
|
||||
title = catalog.i18nc("@info:title", "Simulation View"))
|
||||
|
||||
def _evaluateCompatibilityMode(self):
|
||||
return OpenGLContext.isLegacyOpenGL() or bool(Preferences.getInstance().getValue("view/force_layer_view_compatibility_mode"))
|
||||
return OpenGLContext.isLegacyOpenGL() or bool(Application.getInstance().getPreferences().getValue("view/force_layer_view_compatibility_mode"))
|
||||
|
||||
def _resetSettings(self):
|
||||
self._layer_view_type = 0 # 0 is material color, 1 is color by linetype, 2 is speed, 3 is layer thickness
|
||||
|
@ -543,23 +542,23 @@ class SimulationView(View):
|
|||
self._top_layers_job = None
|
||||
|
||||
def _updateWithPreferences(self):
|
||||
self._solid_layers = int(Preferences.getInstance().getValue("view/top_layer_count"))
|
||||
self._only_show_top_layers = bool(Preferences.getInstance().getValue("view/only_show_top_layers"))
|
||||
self._solid_layers = int(Application.getInstance().getPreferences().getValue("view/top_layer_count"))
|
||||
self._only_show_top_layers = bool(Application.getInstance().getPreferences().getValue("view/only_show_top_layers"))
|
||||
self._compatibility_mode = self._evaluateCompatibilityMode()
|
||||
|
||||
self.setSimulationViewType(int(float(Preferences.getInstance().getValue("layerview/layer_view_type"))));
|
||||
self.setSimulationViewType(int(float(Application.getInstance().getPreferences().getValue("layerview/layer_view_type"))));
|
||||
|
||||
for extruder_nr, extruder_opacity in enumerate(Preferences.getInstance().getValue("layerview/extruder_opacities").split("|")):
|
||||
for extruder_nr, extruder_opacity in enumerate(Application.getInstance().getPreferences().getValue("layerview/extruder_opacities").split("|")):
|
||||
try:
|
||||
opacity = float(extruder_opacity)
|
||||
except ValueError:
|
||||
opacity = 1.0
|
||||
self.setExtruderOpacity(extruder_nr, opacity)
|
||||
|
||||
self.setShowTravelMoves(bool(Preferences.getInstance().getValue("layerview/show_travel_moves")))
|
||||
self.setShowHelpers(bool(Preferences.getInstance().getValue("layerview/show_helpers")))
|
||||
self.setShowSkin(bool(Preferences.getInstance().getValue("layerview/show_skin")))
|
||||
self.setShowInfill(bool(Preferences.getInstance().getValue("layerview/show_infill")))
|
||||
self.setShowTravelMoves(bool(Application.getInstance().getPreferences().getValue("layerview/show_travel_moves")))
|
||||
self.setShowHelpers(bool(Application.getInstance().getPreferences().getValue("layerview/show_helpers")))
|
||||
self.setShowSkin(bool(Application.getInstance().getPreferences().getValue("layerview/show_skin")))
|
||||
self.setShowInfill(bool(Application.getInstance().getPreferences().getValue("layerview/show_infill")))
|
||||
|
||||
self._startUpdateTopLayers()
|
||||
self.preferencesChanged.emit()
|
||||
|
|
|
@ -10,7 +10,6 @@ from PyQt5.QtCore import pyqtSlot, QObject
|
|||
|
||||
from UM.Extension import Extension
|
||||
from UM.Application import Application
|
||||
from UM.Preferences import Preferences
|
||||
from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
|
||||
from UM.Message import Message
|
||||
from UM.i18n import i18nCatalog
|
||||
|
@ -34,22 +33,23 @@ class SliceInfo(QObject, Extension):
|
|||
QObject.__init__(self, parent)
|
||||
Extension.__init__(self)
|
||||
Application.getInstance().getOutputDeviceManager().writeStarted.connect(self._onWriteStarted)
|
||||
Preferences.getInstance().addPreference("info/send_slice_info", True)
|
||||
Preferences.getInstance().addPreference("info/asked_send_slice_info", False)
|
||||
Application.getInstance().getPreferences().addPreference("info/send_slice_info", True)
|
||||
Application.getInstance().getPreferences().addPreference("info/asked_send_slice_info", False)
|
||||
|
||||
self._more_info_dialog = None
|
||||
self._example_data_content = None
|
||||
|
||||
if not Preferences.getInstance().getValue("info/asked_send_slice_info"):
|
||||
if not Application.getInstance().getPreferences().getValue("info/asked_send_slice_info"):
|
||||
self.send_slice_info_message = Message(catalog.i18nc("@info", "Cura collects anonymized usage statistics."),
|
||||
lifetime = 0,
|
||||
dismissable = False,
|
||||
title = catalog.i18nc("@info:title", "Collecting Data"))
|
||||
|
||||
self.send_slice_info_message.addAction("Dismiss", name = catalog.i18nc("@action:button", "Allow"), icon = None,
|
||||
description = catalog.i18nc("@action:tooltip", "Allow Cura to send anonymized usage statistics to help prioritize future improvements to Cura. Some of your preferences and settings are sent, the Cura version and a hash of the models you're slicing."))
|
||||
self.send_slice_info_message.addAction("MoreInfo", name = catalog.i18nc("@action:button", "More info"), icon = None,
|
||||
description = catalog.i18nc("@action:tooltip", "See more information on what data Cura sends."), button_style = Message.ActionButtonStyle.LINK)
|
||||
|
||||
self.send_slice_info_message.addAction("Dismiss", name = catalog.i18nc("@action:button", "Allow"), icon = None,
|
||||
description = catalog.i18nc("@action:tooltip", "Allow Cura to send anonymized usage statistics to help prioritize future improvements to Cura. Some of your preferences and settings are sent, the Cura version and a hash of the models you're slicing."))
|
||||
self.send_slice_info_message.actionTriggered.connect(self.messageActionTriggered)
|
||||
self.send_slice_info_message.show()
|
||||
|
||||
|
@ -62,7 +62,7 @@ class SliceInfo(QObject, Extension):
|
|||
## Perform action based on user input.
|
||||
# Note that clicking "Disable" won't actually disable the data sending, but rather take the user to preferences where they can disable it.
|
||||
def messageActionTriggered(self, message_id, action_id):
|
||||
Preferences.getInstance().setValue("info/asked_send_slice_info", True)
|
||||
Application.getInstance().getPreferences().setValue("info/asked_send_slice_info", True)
|
||||
if action_id == "MoreInfo":
|
||||
self.showMoreInfoDialog()
|
||||
self.send_slice_info_message.hide()
|
||||
|
@ -88,11 +88,11 @@ class SliceInfo(QObject, Extension):
|
|||
|
||||
@pyqtSlot(bool)
|
||||
def setSendSliceInfo(self, enabled: bool):
|
||||
Preferences.getInstance().setValue("info/send_slice_info", enabled)
|
||||
Application.getInstance().getPreferences().setValue("info/send_slice_info", enabled)
|
||||
|
||||
def _onWriteStarted(self, output_device):
|
||||
try:
|
||||
if not Preferences.getInstance().getValue("info/send_slice_info"):
|
||||
if not Application.getInstance().getPreferences().getValue("info/send_slice_info"):
|
||||
Logger.log("d", "'info/send_slice_info' is turned off.")
|
||||
return # Do nothing, user does not want to send data
|
||||
|
||||
|
@ -107,7 +107,7 @@ class SliceInfo(QObject, Extension):
|
|||
data["schema_version"] = 0
|
||||
data["cura_version"] = application.getVersion()
|
||||
|
||||
active_mode = Preferences.getInstance().getValue("cura/active_mode")
|
||||
active_mode = Application.getInstance().getPreferences().getValue("cura/active_mode")
|
||||
if active_mode == 0:
|
||||
data["active_mode"] = "recommended"
|
||||
else:
|
||||
|
@ -122,7 +122,7 @@ class SliceInfo(QObject, Extension):
|
|||
machine_settings_changed_by_user = True
|
||||
|
||||
data["machine_settings_changed_by_user"] = machine_settings_changed_by_user
|
||||
data["language"] = Preferences.getInstance().getValue("general/language")
|
||||
data["language"] = Application.getInstance().getPreferences().getValue("general/language")
|
||||
data["os"] = {"type": platform.system(), "version": platform.version()}
|
||||
|
||||
data["active_machine"] = {"definition_id": global_stack.definition.getId(),
|
||||
|
|
|
@ -6,7 +6,6 @@ from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
|
|||
from UM.Scene.Selection import Selection
|
||||
from UM.Resources import Resources
|
||||
from UM.Application import Application
|
||||
from UM.Preferences import Preferences
|
||||
from UM.View.RenderBatch import RenderBatch
|
||||
from UM.Settings.Validator import ValidatorState
|
||||
from UM.Math.Color import Color
|
||||
|
@ -23,7 +22,7 @@ class SolidView(View):
|
|||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
Preferences.getInstance().addPreference("view/show_overhang", True)
|
||||
Application.getInstance().getPreferences().addPreference("view/show_overhang", True)
|
||||
|
||||
self._enabled_shader = None
|
||||
self._disabled_shader = None
|
||||
|
@ -65,7 +64,7 @@ class SolidView(View):
|
|||
support_extruder_nr = global_container_stack.getExtruderPositionValueWithDefault("support_extruder_nr")
|
||||
support_angle_stack = Application.getInstance().getExtruderManager().getExtruderStack(support_extruder_nr)
|
||||
|
||||
if support_angle_stack is not None and Preferences.getInstance().getValue("view/show_overhang"):
|
||||
if support_angle_stack is not None and Application.getInstance().getPreferences().getValue("view/show_overhang"):
|
||||
angle = support_angle_stack.getProperty("support_angle", "value")
|
||||
# Make sure the overhang angle is valid before passing it to the shader
|
||||
# Note: if the overhang angle is set to its default value, it does not need to get validated (validationState = None)
|
||||
|
|
|
@ -31,8 +31,9 @@ Item
|
|||
frameVisible: false
|
||||
selectionMode: 0
|
||||
model: packageData.supported_configs
|
||||
headerDelegate: Item
|
||||
headerDelegate: Rectangle
|
||||
{
|
||||
color: UM.Theme.getColor("sidebar")
|
||||
height: UM.Theme.getSize("toolbox_chart_row").height
|
||||
Label
|
||||
{
|
||||
|
|
|
@ -34,6 +34,7 @@ Column
|
|||
// Don't allow installing while another download is running
|
||||
enabled: installed || !(toolbox.isDownloading && toolbox.activePackage != model)
|
||||
opacity: enabled ? 1.0 : 0.5
|
||||
visible: !updateButton.visible // Don't show when the update button is visible
|
||||
}
|
||||
|
||||
ToolboxProgressButton
|
||||
|
@ -55,7 +56,7 @@ Column
|
|||
// Don't allow installing while another download is running
|
||||
enabled: !(toolbox.isDownloading && toolbox.activePackage != model)
|
||||
opacity: enabled ? 1.0 : 0.5
|
||||
visible: installed && canUpdate
|
||||
visible: canUpdate
|
||||
}
|
||||
Connections
|
||||
{
|
||||
|
|
|
@ -12,6 +12,7 @@ Column
|
|||
height: childrenRect.height
|
||||
width: parent.width
|
||||
spacing: UM.Theme.getSize("default_margin").height
|
||||
/* Hidden for 3.4
|
||||
Label
|
||||
{
|
||||
id: heading
|
||||
|
@ -20,6 +21,7 @@ Column
|
|||
color: UM.Theme.getColor("text_medium")
|
||||
font: UM.Theme.getFont("medium")
|
||||
}
|
||||
*/
|
||||
GridLayout
|
||||
{
|
||||
id: grid
|
||||
|
|
|
@ -18,6 +18,7 @@ ScrollView
|
|||
spacing: UM.Theme.getSize("default_margin").height
|
||||
padding: UM.Theme.getSize("wide_margin").height
|
||||
height: childrenRect.height + 2 * padding
|
||||
|
||||
ToolboxDownloadsShowcase
|
||||
{
|
||||
id: showcase
|
||||
|
@ -29,6 +30,7 @@ ScrollView
|
|||
width: parent.width
|
||||
height: UM.Theme.getSize("default_lining").height
|
||||
}
|
||||
|
||||
ToolboxDownloadsGrid
|
||||
{
|
||||
id: allPlugins
|
||||
|
|
|
@ -19,10 +19,11 @@ Column
|
|||
color: UM.Theme.getColor("text_medium")
|
||||
font: UM.Theme.getFont("medium")
|
||||
}
|
||||
Row
|
||||
Grid
|
||||
{
|
||||
height: childrenRect.height
|
||||
spacing: UM.Theme.getSize("wide_margin").width
|
||||
columns: 3
|
||||
anchors
|
||||
{
|
||||
horizontalCenter: parent.horizontalCenter
|
||||
|
|
|
@ -33,6 +33,7 @@ Item
|
|||
toolbox.viewPage = "overview"
|
||||
}
|
||||
}
|
||||
|
||||
ToolboxTabButton
|
||||
{
|
||||
text: catalog.i18nc("@title:tab", "Materials")
|
||||
|
|
|
@ -16,7 +16,7 @@ Item
|
|||
{
|
||||
color: UM.Theme.getColor("lining")
|
||||
width: parent.width
|
||||
height: UM.Theme.getSize("default_lining").height
|
||||
height: Math.floor(UM.Theme.getSize("default_lining").height)
|
||||
anchors.bottom: parent.bottom
|
||||
}
|
||||
Row
|
||||
|
@ -40,14 +40,14 @@ Item
|
|||
Column
|
||||
{
|
||||
id: pluginInfo
|
||||
topPadding: UM.Theme.getSize("default_margin").height / 2
|
||||
topPadding: Math.floor(UM.Theme.getSize("default_margin").height / 2)
|
||||
property var color: model.type === "plugin" && !isEnabled ? UM.Theme.getColor("lining") : UM.Theme.getColor("text")
|
||||
width: tileRow.width - (authorInfo.width + pluginActions.width + 2 * tileRow.spacing + ((disableButton.visible) ? disableButton.width + tileRow.spacing : 0))
|
||||
width: Math.floor(tileRow.width - (authorInfo.width + pluginActions.width + 2 * tileRow.spacing + ((disableButton.visible) ? disableButton.width + tileRow.spacing : 0)))
|
||||
Label
|
||||
{
|
||||
text: model.name
|
||||
width: parent.width
|
||||
height: UM.Theme.getSize("toolbox_property_label").height
|
||||
height: Math.floor(UM.Theme.getSize("toolbox_property_label").height)
|
||||
wrapMode: Text.WordWrap
|
||||
font: UM.Theme.getFont("default_bold")
|
||||
color: pluginInfo.color
|
||||
|
@ -81,7 +81,7 @@ Item
|
|||
}
|
||||
}
|
||||
width: parent.width
|
||||
height: UM.Theme.getSize("toolbox_property_label").height
|
||||
height: Math.floor(UM.Theme.getSize("toolbox_property_label").height)
|
||||
wrapMode: Text.WordWrap
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
|
|
|
@ -13,6 +13,16 @@ Column
|
|||
width: UM.Theme.getSize("toolbox_action_button").width
|
||||
spacing: UM.Theme.getSize("narrow_margin").height
|
||||
|
||||
Label
|
||||
{
|
||||
visible: !model.is_installed
|
||||
text: catalog.i18nc("@label", "Will install upon restarting")
|
||||
color: UM.Theme.getColor("lining")
|
||||
font: UM.Theme.getFont("default")
|
||||
wrapMode: Text.WordWrap
|
||||
width: parent.width
|
||||
}
|
||||
|
||||
ToolboxProgressButton
|
||||
{
|
||||
id: updateButton
|
||||
|
@ -39,7 +49,7 @@ Column
|
|||
{
|
||||
id: removeButton
|
||||
text: canDowngrade ? catalog.i18nc("@action:button", "Downgrade") : catalog.i18nc("@action:button", "Uninstall")
|
||||
visible: !model.is_bundled
|
||||
visible: !model.is_bundled && model.is_installed
|
||||
enabled: !toolbox.isDownloading
|
||||
style: ButtonStyle
|
||||
{
|
||||
|
|
|
@ -150,7 +150,7 @@ Item
|
|||
{
|
||||
id: loader
|
||||
visible: active
|
||||
source: "../images/loading.gif"
|
||||
source: visible ? "../images/loading.gif" : ""
|
||||
width: UM.Theme.getSize("toolbox_loader").width
|
||||
height: UM.Theme.getSize("toolbox_loader").height
|
||||
anchors.right: button.left
|
||||
|
|
|
@ -29,8 +29,9 @@ class PackagesModel(ListModel):
|
|||
self.addRoleName(Qt.UserRole + 12, "last_updated")
|
||||
self.addRoleName(Qt.UserRole + 13, "is_bundled")
|
||||
self.addRoleName(Qt.UserRole + 14, "is_enabled")
|
||||
self.addRoleName(Qt.UserRole + 15, "has_configs")
|
||||
self.addRoleName(Qt.UserRole + 16, "supported_configs")
|
||||
self.addRoleName(Qt.UserRole + 15, "is_installed") # Scheduled pkgs are included in the model but should not be marked as actually installed
|
||||
self.addRoleName(Qt.UserRole + 16, "has_configs")
|
||||
self.addRoleName(Qt.UserRole + 17, "supported_configs")
|
||||
|
||||
# List of filters for queries. The result is the union of the each list of results.
|
||||
self._filter = {} # type: Dict[str, str]
|
||||
|
@ -73,6 +74,7 @@ class PackagesModel(ListModel):
|
|||
"last_updated": package["last_updated"] if "last_updated" in package else None,
|
||||
"is_bundled": package["is_bundled"] if "is_bundled" in package else False,
|
||||
"is_enabled": package["is_enabled"] if "is_enabled" in package else False,
|
||||
"is_installed": package["is_installed"] if "is_installed" in package else False,
|
||||
"has_configs": has_configs,
|
||||
"supported_configs": configs_model
|
||||
})
|
||||
|
|
|
@ -28,7 +28,8 @@ i18n_catalog = i18nCatalog("cura")
|
|||
## The Toolbox class is responsible of communicating with the server through the API
|
||||
class Toolbox(QObject, Extension):
|
||||
|
||||
DEFAULT_PACKAGES_API_ROOT = "https://api.ultimaker.com"
|
||||
DEFAULT_CLOUD_API_ROOT = "https://api.ultimaker.com"
|
||||
DEFAULT_CLOUD_API_VERSION = 1
|
||||
|
||||
def __init__(self, parent=None) -> None:
|
||||
super().__init__(parent)
|
||||
|
@ -36,14 +37,11 @@ class Toolbox(QObject, Extension):
|
|||
self._application = Application.getInstance()
|
||||
self._package_manager = None
|
||||
self._plugin_registry = Application.getInstance().getPluginRegistry()
|
||||
self._packages_api_root = self._getPackagesApiRoot()
|
||||
self._packages_version = self._getPackagesVersion()
|
||||
self._api_version = 1
|
||||
self._api_url = "{api_root}/cura-packages/v{api_version}/cura/v{package_version}".format(
|
||||
api_root = self._packages_api_root,
|
||||
api_version = self._api_version,
|
||||
package_version = self._packages_version
|
||||
)
|
||||
|
||||
self._sdk_version = None
|
||||
self._cloud_api_version = None
|
||||
self._cloud_api_root = None
|
||||
self._api_url = None
|
||||
|
||||
# Network:
|
||||
self._get_packages_request = None
|
||||
|
@ -64,21 +62,19 @@ class Toolbox(QObject, Extension):
|
|||
)
|
||||
)
|
||||
]
|
||||
self._request_urls = {
|
||||
"authors": QUrl("{base_url}/authors".format(base_url = self._api_url)),
|
||||
"packages": QUrl("{base_url}/packages".format(base_url = self._api_url)),
|
||||
"plugins_showcase": QUrl("{base_url}/showcase".format(base_url = self._api_url)),
|
||||
"materials_showcase": QUrl("{base_url}/showcase".format(base_url = self._api_url))
|
||||
}
|
||||
self._request_urls = {}
|
||||
self._to_update = [] # Package_ids that are waiting to be updated
|
||||
self._old_plugin_ids = []
|
||||
|
||||
# Data:
|
||||
self._metadata = {
|
||||
"authors": [],
|
||||
"packages": [],
|
||||
"plugins_showcase": [],
|
||||
"plugins_available": [],
|
||||
"plugins_installed": [],
|
||||
"materials_showcase": [],
|
||||
"materials_available": [],
|
||||
"materials_installed": []
|
||||
}
|
||||
|
||||
|
@ -161,22 +157,52 @@ class Toolbox(QObject, Extension):
|
|||
# this is initialized. Therefore, we wait until the application is ready.
|
||||
def _onAppInitialized(self) -> None:
|
||||
self._package_manager = Application.getInstance().getPackageManager()
|
||||
self._sdk_version = self._getSDKVersion()
|
||||
self._cloud_api_version = self._getCloudAPIVersion()
|
||||
self._cloud_api_root = self._getCloudAPIRoot()
|
||||
self._api_url = "{cloud_api_root}/cura-packages/v{cloud_api_version}/cura/v{sdk_version}".format(
|
||||
cloud_api_root=self._cloud_api_root,
|
||||
cloud_api_version=self._cloud_api_version,
|
||||
sdk_version=self._sdk_version
|
||||
)
|
||||
self._request_urls = {
|
||||
"authors": QUrl("{base_url}/authors".format(base_url=self._api_url)),
|
||||
"packages": QUrl("{base_url}/packages".format(base_url=self._api_url)),
|
||||
"plugins_showcase": QUrl("{base_url}/showcase".format(base_url=self._api_url)),
|
||||
"plugins_available": QUrl("{base_url}/packages?package_type=plugin".format(base_url=self._api_url)),
|
||||
"materials_showcase": QUrl("{base_url}/showcase".format(base_url=self._api_url)),
|
||||
"materials_available": QUrl("{base_url}/packages?package_type=material".format(base_url=self._api_url))
|
||||
}
|
||||
|
||||
# Get the API root for the packages API depending on Cura version settings.
|
||||
def _getPackagesApiRoot(self) -> str:
|
||||
def _getCloudAPIRoot(self) -> str:
|
||||
if not hasattr(cura, "CuraVersion"):
|
||||
return self.DEFAULT_PACKAGES_API_ROOT
|
||||
if not hasattr(cura.CuraVersion, "CuraPackagesApiRoot"):
|
||||
return self.DEFAULT_PACKAGES_API_ROOT
|
||||
return cura.CuraVersion.CuraPackagesApiRoot
|
||||
return self.DEFAULT_CLOUD_API_ROOT
|
||||
if not hasattr(cura.CuraVersion, "CuraCloudAPIRoot"):
|
||||
return self.DEFAULT_CLOUD_API_ROOT
|
||||
if not cura.CuraVersion.CuraCloudAPIRoot:
|
||||
return self.DEFAULT_CLOUD_API_ROOT
|
||||
return cura.CuraVersion.CuraCloudAPIRoot
|
||||
|
||||
# Get the cloud API version from CuraVersion
|
||||
def _getCloudAPIVersion(self) -> int:
|
||||
if not hasattr(cura, "CuraVersion"):
|
||||
return self.DEFAULT_CLOUD_API_VERSION
|
||||
if not hasattr(cura.CuraVersion, "CuraCloudAPIVersion"):
|
||||
return self.DEFAULT_CLOUD_API_VERSION
|
||||
if not cura.CuraVersion.CuraCloudAPIVersion:
|
||||
return self.DEFAULT_CLOUD_API_VERSION
|
||||
return cura.CuraVersion.CuraCloudAPIVersion
|
||||
|
||||
# Get the packages version depending on Cura version settings.
|
||||
def _getPackagesVersion(self) -> int:
|
||||
def _getSDKVersion(self) -> int:
|
||||
if not hasattr(cura, "CuraVersion"):
|
||||
return self._plugin_registry.APIVersion
|
||||
if not hasattr(cura.CuraVersion, "CuraPackagesVersion"):
|
||||
if not hasattr(cura.CuraVersion, "CuraSDKVersion"):
|
||||
return self._plugin_registry.APIVersion
|
||||
return cura.CuraVersion.CuraPackagesVersion
|
||||
if not cura.CuraVersion.CuraSDKVersion:
|
||||
return self._plugin_registry.APIVersion
|
||||
return cura.CuraVersion.CuraSDKVersion
|
||||
|
||||
@pyqtSlot()
|
||||
def browsePackages(self) -> None:
|
||||
|
@ -212,15 +238,52 @@ class Toolbox(QObject, Extension):
|
|||
dialog = Application.getInstance().createQmlComponent(path, {"toolbox": self})
|
||||
return dialog
|
||||
|
||||
|
||||
def _convertPluginMetadata(self, plugin: dict) -> dict:
|
||||
formatted = {
|
||||
"package_id": plugin["id"],
|
||||
"package_type": "plugin",
|
||||
"display_name": plugin["plugin"]["name"],
|
||||
"package_version": plugin["plugin"]["version"],
|
||||
"sdk_version": plugin["plugin"]["api"],
|
||||
"author": {
|
||||
"author_id": plugin["plugin"]["author"],
|
||||
"display_name": plugin["plugin"]["author"]
|
||||
},
|
||||
"is_installed": True,
|
||||
"description": plugin["plugin"]["description"]
|
||||
}
|
||||
return formatted
|
||||
|
||||
@pyqtSlot()
|
||||
def _updateInstalledModels(self) -> None:
|
||||
|
||||
# This is moved here to avoid code duplication and so that after installing plugins they get removed from the
|
||||
# list of old plugins
|
||||
old_plugin_ids = self._plugin_registry.getInstalledPlugins()
|
||||
installed_package_ids = self._package_manager.getAllInstalledPackageIDs()
|
||||
|
||||
self._old_plugin_ids = []
|
||||
self._old_plugin_metadata = []
|
||||
|
||||
for plugin_id in old_plugin_ids:
|
||||
if plugin_id not in installed_package_ids:
|
||||
Logger.log('i', 'Found a plugin that was installed with the old plugin browser: %s', plugin_id)
|
||||
|
||||
old_metadata = self._plugin_registry.getMetaData(plugin_id)
|
||||
new_metadata = self._convertPluginMetadata(old_metadata)
|
||||
|
||||
self._old_plugin_ids.append(plugin_id)
|
||||
self._old_plugin_metadata.append(new_metadata)
|
||||
|
||||
all_packages = self._package_manager.getAllInstalledPackagesInfo()
|
||||
if "plugin" in all_packages:
|
||||
self._metadata["plugins_installed"] = all_packages["plugin"]
|
||||
self._metadata["plugins_installed"] = all_packages["plugin"] + self._old_plugin_metadata
|
||||
self._models["plugins_installed"].setMetadata(self._metadata["plugins_installed"])
|
||||
self.metadataChanged.emit()
|
||||
if "material" in all_packages:
|
||||
self._metadata["materials_installed"] = all_packages["material"]
|
||||
# TODO: ADD MATERIALS HERE ONCE MATERIALS PORTION OF TOOLBOX IS LIVE
|
||||
self._models["materials_installed"].setMetadata(self._metadata["materials_installed"])
|
||||
self.metadataChanged.emit()
|
||||
|
||||
|
@ -250,8 +313,6 @@ class Toolbox(QObject, Extension):
|
|||
if remote_package:
|
||||
download_url = remote_package["download_url"]
|
||||
Logger.log("d", "Updating package [%s]..." % plugin_id)
|
||||
if self._package_manager.isUserInstalledPackage(plugin_id):
|
||||
self.uninstall(plugin_id)
|
||||
self.startDownload(download_url)
|
||||
else:
|
||||
Logger.log("e", "Could not update package [%s] because there is no remote package info available.", plugin_id)
|
||||
|
@ -306,6 +367,9 @@ class Toolbox(QObject, Extension):
|
|||
# --------------------------------------------------------------------------
|
||||
@pyqtSlot(str, result = bool)
|
||||
def canUpdate(self, package_id: str) -> bool:
|
||||
if self.isOldPlugin(package_id):
|
||||
return True
|
||||
|
||||
local_package = self._package_manager.getInstalledPackageInfo(package_id)
|
||||
if local_package is None:
|
||||
return False
|
||||
|
@ -318,19 +382,21 @@ class Toolbox(QObject, Extension):
|
|||
remote_version = Version(remote_package["package_version"])
|
||||
return remote_version > local_version
|
||||
|
||||
@pyqtSlot(str, result=bool)
|
||||
@pyqtSlot(str, result = bool)
|
||||
def canDowngrade(self, package_id: str) -> bool:
|
||||
# If the currently installed version is higher than the bundled version (if present), the we can downgrade
|
||||
# this package.
|
||||
local_package = self._package_manager.getInstalledPackageInfo(package_id)
|
||||
if local_package is None:
|
||||
return False
|
||||
|
||||
remote_package = self.getRemotePackage(package_id)
|
||||
if remote_package is None:
|
||||
bundled_package = self._package_manager.getBundledPackageInfo(package_id)
|
||||
if bundled_package is None:
|
||||
return False
|
||||
|
||||
local_version = Version(local_package["package_version"])
|
||||
remote_version = Version(remote_package["package_version"])
|
||||
return remote_version < local_version
|
||||
bundled_version = Version(bundled_package["package_version"])
|
||||
return bundled_version < local_version
|
||||
|
||||
@pyqtSlot(str, result = bool)
|
||||
def isInstalled(self, package_id: str) -> bool:
|
||||
|
@ -342,6 +408,13 @@ class Toolbox(QObject, Extension):
|
|||
return True
|
||||
return False
|
||||
|
||||
# Check for plugins that were installed with the old plugin browser
|
||||
@pyqtSlot(str, result = bool)
|
||||
def isOldPlugin(self, plugin_id: str) -> bool:
|
||||
if plugin_id in self._old_plugin_ids:
|
||||
return True
|
||||
return False
|
||||
|
||||
def loadingComplete(self) -> bool:
|
||||
populated = 0
|
||||
for list in self._metadata.items():
|
||||
|
@ -383,7 +456,10 @@ class Toolbox(QObject, Extension):
|
|||
|
||||
def resetDownload(self) -> None:
|
||||
if self._download_reply:
|
||||
try:
|
||||
self._download_reply.downloadProgress.disconnect(self._onDownloadProgress)
|
||||
except TypeError: #Raised when the method is not connected to the signal yet.
|
||||
pass #Don't need to disconnect.
|
||||
self._download_reply.abort()
|
||||
self._download_reply = None
|
||||
self._download_request = None
|
||||
|
|
|
@ -148,6 +148,10 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
|
|||
def selectPrinter(self, target_printer: str = "") -> None:
|
||||
self._sending_job.send(target_printer)
|
||||
|
||||
@pyqtSlot()
|
||||
def cancelPrintSelection(self) -> None:
|
||||
self._sending_gcode = False
|
||||
|
||||
## Greenlet to send a job to the printer over the network.
|
||||
#
|
||||
# This greenlet gets called asynchronously in requestWrite. It is a
|
||||
|
@ -388,7 +392,7 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
|
|||
self._updatePrintJob(print_job, print_job_data)
|
||||
|
||||
if print_job.state != "queued": # Print job should be assigned to a printer.
|
||||
if print_job.state in ["failed", "finished", "aborted"]:
|
||||
if print_job.state in ["failed", "finished", "aborted", "none"]:
|
||||
# Print job was already completed, so don't attach it to a printer.
|
||||
printer = None
|
||||
else:
|
||||
|
|
|
@ -90,6 +90,7 @@ UM.Dialog
|
|||
onClicked: {
|
||||
base.visible = false;
|
||||
printerSelectionCombobox.currentIndex = 0
|
||||
OutputDevice.cancelPrintSelection()
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
|
@ -5,7 +5,6 @@ from UM.OutputDevice.OutputDevicePlugin import OutputDevicePlugin
|
|||
from UM.Logger import Logger
|
||||
from UM.Application import Application
|
||||
from UM.Signal import Signal, signalemitter
|
||||
from UM.Preferences import Preferences
|
||||
from UM.Version import Version
|
||||
|
||||
from . import ClusterUM3OutputDevice, LegacyUM3OutputDevice
|
||||
|
@ -54,7 +53,7 @@ class UM3OutputDevicePlugin(OutputDevicePlugin):
|
|||
self._cluster_api_prefix = "/cluster-api/v" + self._cluster_api_version + "/"
|
||||
|
||||
# Get list of manual instances from preferences
|
||||
self._preferences = Preferences.getInstance()
|
||||
self._preferences = Application.getInstance().getPreferences()
|
||||
self._preferences.addPreference("um3networkprinting/manual_instances",
|
||||
"") # A comma-separated list of ip adresses or hostnames
|
||||
|
||||
|
|
|
@ -379,13 +379,14 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
|
|||
|
||||
def resumePrint(self):
|
||||
self._paused = False
|
||||
self._sendNextGcodeLine() #Send one line of g-code next so that we'll trigger an "ok" response loop even if we're not polling temperatures.
|
||||
|
||||
def cancelPrint(self):
|
||||
self._gcode_position = 0
|
||||
self._gcode.clear()
|
||||
self._printers[0].updateActivePrintJob(None)
|
||||
self._is_printing = False
|
||||
self._is_paused = False
|
||||
self._paused = False
|
||||
|
||||
# Turn off temperatures, fan and steppers
|
||||
self._sendCommand("M140 S0")
|
||||
|
|
|
@ -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.
|
||||
|
||||
from UM.Signal import Signal, signalemitter
|
||||
from UM.Application import Application
|
||||
from UM.Resources import Resources
|
||||
import threading
|
||||
import platform
|
||||
import time
|
||||
import serial.tools.list_ports
|
||||
|
||||
from PyQt5.QtCore import QObject, pyqtSlot, pyqtProperty, pyqtSignal
|
||||
|
||||
from UM.Logger import Logger
|
||||
from UM.Resources import Resources
|
||||
from UM.Signal import Signal, signalemitter
|
||||
from UM.OutputDevice.OutputDevicePlugin import OutputDevicePlugin
|
||||
from UM.i18n import i18nCatalog
|
||||
|
||||
|
@ -12,12 +18,6 @@ from cura.PrinterOutputDevice import ConnectionState
|
|||
from cura.CuraApplication import CuraApplication
|
||||
|
||||
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")
|
||||
|
||||
|
@ -28,8 +28,14 @@ class USBPrinterOutputDeviceManager(QObject, OutputDevicePlugin):
|
|||
addUSBOutputDeviceSignal = Signal()
|
||||
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)
|
||||
self._application = application
|
||||
|
||||
self._serial_port_list = []
|
||||
self._usb_output_devices = {}
|
||||
self._usb_output_devices_model = None
|
||||
|
@ -38,11 +44,11 @@ class USBPrinterOutputDeviceManager(QObject, OutputDevicePlugin):
|
|||
|
||||
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.
|
||||
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
|
||||
def updateUSBPrinterOutputDevices(self):
|
||||
|
@ -69,7 +75,7 @@ class USBPrinterOutputDeviceManager(QObject, OutputDevicePlugin):
|
|||
|
||||
def _updateThread(self):
|
||||
while self._check_updates:
|
||||
container_stack = Application.getInstance().getGlobalContainerStack()
|
||||
container_stack = self._application.getGlobalContainerStack()
|
||||
if container_stack is None:
|
||||
time.sleep(5)
|
||||
continue
|
||||
|
@ -81,19 +87,10 @@ class USBPrinterOutputDeviceManager(QObject, OutputDevicePlugin):
|
|||
self._addRemovePorts(port_list)
|
||||
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)
|
||||
def getDefaultFirmwareName(self):
|
||||
# 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:
|
||||
Logger.log("e", "There is no global container stack. Can not update firmware.")
|
||||
self._firmware_view.close()
|
||||
|
@ -182,4 +179,8 @@ class USBPrinterOutputDeviceManager(QObject, OutputDevicePlugin):
|
|||
|
||||
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).
|
||||
# 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)
|
||||
return {"output_device": USBPrinterOutputDeviceManager.USBPrinterOutputDeviceManager.getInstance()}
|
||||
return {"output_device": USBPrinterOutputDeviceManager.USBPrinterOutputDeviceManager(app)}
|
||||
|
|
|
@ -1,28 +1,26 @@
|
|||
# Copyright (c) 2017 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
from UM.Extension import Extension
|
||||
from UM.Preferences import Preferences
|
||||
from UM.Application import Application
|
||||
from UM.PluginRegistry import PluginRegistry
|
||||
from UM.Logger import Logger
|
||||
|
||||
from cura.CuraApplication import CuraApplication
|
||||
import os
|
||||
|
||||
from PyQt5.QtCore import QObject, pyqtSlot
|
||||
|
||||
import os.path
|
||||
from UM.Extension import Extension
|
||||
from UM.Logger import Logger
|
||||
|
||||
|
||||
class UserAgreement(QObject, Extension):
|
||||
def __init__(self):
|
||||
def __init__(self, application):
|
||||
super(UserAgreement, self).__init__()
|
||||
self._application = application
|
||||
self._user_agreement_window = None
|
||||
self._user_agreement_context = None
|
||||
Application.getInstance().engineCreatedSignal.connect(self._onEngineCreated)
|
||||
Preferences.getInstance().addPreference("general/accepted_user_agreement", False)
|
||||
self._application.engineCreatedSignal.connect(self._onEngineCreated)
|
||||
|
||||
self._application.getPreferences().addPreference("general/accepted_user_agreement", False)
|
||||
|
||||
def _onEngineCreated(self):
|
||||
if not Preferences.getInstance().getValue("general/accepted_user_agreement"):
|
||||
if not self._application.getPreferences().getValue("general/accepted_user_agreement"):
|
||||
self.showUserAgreement()
|
||||
|
||||
def showUserAgreement(self):
|
||||
|
@ -35,14 +33,14 @@ class UserAgreement(QObject, Extension):
|
|||
def didAgree(self, user_choice):
|
||||
if user_choice:
|
||||
Logger.log("i", "User agreed to the user agreement")
|
||||
Preferences.getInstance().setValue("general/accepted_user_agreement", True)
|
||||
self._application.getPreferences().setValue("general/accepted_user_agreement", True)
|
||||
self._user_agreement_window.hide()
|
||||
else:
|
||||
Logger.log("i", "User did NOT agree to the user agreement")
|
||||
Preferences.getInstance().setValue("general/accepted_user_agreement", False)
|
||||
CuraApplication.getInstance().quit()
|
||||
CuraApplication.getInstance().setNeedToShowUserAgreement(False)
|
||||
self._application.getPreferences().setValue("general/accepted_user_agreement", False)
|
||||
self._application.quit()
|
||||
self._application.setNeedToShowUserAgreement(False)
|
||||
|
||||
def createUserAgreementWindow(self):
|
||||
path = os.path.join(PluginRegistry.getInstance().getPluginPath(self.getPluginId()), "UserAgreement.qml")
|
||||
self._user_agreement_window = Application.getInstance().createQmlComponent(path, {"manager": self})
|
||||
path = os.path.join(self._application.getPluginRegistry().getPluginPath(self.getPluginId()), "UserAgreement.qml")
|
||||
self._user_agreement_window = self._application.createQmlComponent(path, {"manager": self})
|
||||
|
|
|
@ -7,4 +7,4 @@ def getMetaData():
|
|||
return {}
|
||||
|
||||
def register(app):
|
||||
return {"extension": UserAgreement.UserAgreement()}
|
||||
return {"extension": UserAgreement.UserAgreement(app)}
|
||||
|
|
|
@ -107,7 +107,12 @@ class MachineInstance:
|
|||
user_profile["values"] = {}
|
||||
|
||||
version_upgrade_manager = UM.VersionUpgradeManager.VersionUpgradeManager.getInstance()
|
||||
user_storage = os.path.join(Resources.getDataStoragePath(), next(iter(version_upgrade_manager.getStoragePaths("user"))))
|
||||
user_version_to_paths_dict = version_upgrade_manager.getStoragePaths("user")
|
||||
paths_set = set()
|
||||
for paths in user_version_to_paths_dict.values():
|
||||
paths_set |= paths
|
||||
|
||||
user_storage = os.path.join(Resources.getDataStoragePath(), next(iter(paths_set)))
|
||||
user_profile_file = os.path.join(user_storage, urllib.parse.quote_plus(self._name) + "_current_settings.inst.cfg")
|
||||
if not os.path.exists(user_storage):
|
||||
os.makedirs(user_storage)
|
||||
|
|
|
@ -6,6 +6,9 @@ import io #To serialise the preference files afterwards.
|
|||
|
||||
from UM.VersionUpgrade import VersionUpgrade #We're inheriting from this.
|
||||
|
||||
_renamed_settings = {
|
||||
"infill_hollow": "infill_support_enabled"
|
||||
}
|
||||
|
||||
## Upgrades configurations from the state they were in at version 3.3 to the
|
||||
# state they should be in at version 3.4.
|
||||
|
@ -38,6 +41,17 @@ class VersionUpgrade33to34(VersionUpgrade):
|
|||
# Update version number.
|
||||
parser["general"]["version"] = "4"
|
||||
|
||||
if "values" in parser:
|
||||
#If infill_hollow was enabled and the overhang angle was adjusted, copy that overhang angle to the new infill support angle.
|
||||
if "infill_hollow" in parser["values"] and parser["values"]["infill_hollow"] and "support_angle" in parser["values"]:
|
||||
parser["values"]["infill_support_angle"] = parser["values"]["support_angle"]
|
||||
|
||||
#Renamed settings.
|
||||
for original, replacement in _renamed_settings.items():
|
||||
if original in parser["values"]:
|
||||
parser["values"][replacement] = parser["values"][original]
|
||||
del parser["values"][original]
|
||||
|
||||
result = io.StringIO()
|
||||
parser.write(result)
|
||||
return [filename], [result.getvalue()]
|
|
@ -21,7 +21,7 @@ def getMetaData():
|
|||
},
|
||||
"quality_changes": {
|
||||
"get_version": upgrade.getCfgVersion,
|
||||
"location": {"./quality"}
|
||||
"location": {"./quality_changes"}
|
||||
},
|
||||
"user": {
|
||||
"get_version": upgrade.getCfgVersion,
|
||||
|
|
|
@ -38,14 +38,14 @@ class Shape:
|
|||
self.name = name
|
||||
|
||||
class X3DReader(MeshReader):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
def __init__(self, application):
|
||||
super().__init__(application)
|
||||
self._supported_extensions = [".x3d"]
|
||||
self._namespaces = {}
|
||||
|
||||
# Main entry point
|
||||
# Reads the file, returns a SceneNode (possibly with nested ones), or None
|
||||
def read(self, file_name):
|
||||
def _read(self, file_name):
|
||||
try:
|
||||
self.defs = {}
|
||||
self.shapes = []
|
||||
|
|
|
@ -16,4 +16,4 @@ def getMetaData():
|
|||
}
|
||||
|
||||
def register(app):
|
||||
return { "mesh_reader": X3DReader.X3DReader() }
|
||||
return { "mesh_reader": X3DReader.X3DReader(app) }
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# Copyright (c) 2017 Ultimaker B.V.
|
||||
# Copyright (c) 2018 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import copy
|
||||
|
@ -12,10 +12,10 @@ import xml.etree.ElementTree as ET
|
|||
from UM.Resources import Resources
|
||||
from UM.Logger import Logger
|
||||
from cura.CuraApplication import CuraApplication
|
||||
|
||||
import UM.Dictionary
|
||||
from UM.Settings.InstanceContainer import InstanceContainer
|
||||
from UM.Settings.ContainerRegistry import ContainerRegistry
|
||||
from UM.ConfigurationErrorMessage import ConfigurationErrorMessage
|
||||
|
||||
from .XmlMaterialValidator import XmlMaterialValidator
|
||||
|
||||
|
@ -540,7 +540,9 @@ class XmlMaterialProfile(InstanceContainer):
|
|||
|
||||
validation_message = XmlMaterialValidator.validateMaterialMetaData(meta_data)
|
||||
if validation_message is not None:
|
||||
raise Exception("Not valid material profile: %s" % (validation_message))
|
||||
ConfigurationErrorMessage.getInstance().addFaultyContainers(self.getId())
|
||||
Logger.log("e", "Not a valid material profile: {message}".format(message = validation_message))
|
||||
return
|
||||
|
||||
property_values = {}
|
||||
properties = data.iterfind("./um:properties/*", self.__namespaces)
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
"display_name": "3MF Reader",
|
||||
"description": "Provides support for reading 3MF files.",
|
||||
"package_version": "1.0.0",
|
||||
"cura_version": 4,
|
||||
"sdk_version": 4,
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "Ultimaker",
|
||||
|
@ -23,7 +23,7 @@
|
|||
"display_name": "3MF Writer",
|
||||
"description": "Provides support for writing 3MF files.",
|
||||
"package_version": "1.0.0",
|
||||
"cura_version": 4,
|
||||
"sdk_version": 4,
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "Ultimaker",
|
||||
|
@ -40,7 +40,7 @@
|
|||
"display_name": "Change Log",
|
||||
"description": "Shows changes since latest checked version.",
|
||||
"package_version": "1.0.0",
|
||||
"cura_version": 4,
|
||||
"sdk_version": 4,
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "Ultimaker",
|
||||
|
@ -57,7 +57,7 @@
|
|||
"display_name": "CuraEngine Backend",
|
||||
"description": "Provides the link to the CuraEngine slicing backend.",
|
||||
"package_version": "1.0.0",
|
||||
"cura_version": 4,
|
||||
"sdk_version": 4,
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "Ultimaker",
|
||||
|
@ -74,7 +74,7 @@
|
|||
"display_name": "Cura Profile Reader",
|
||||
"description": "Provides support for importing Cura profiles.",
|
||||
"package_version": "1.0.0",
|
||||
"cura_version": 4,
|
||||
"sdk_version": 4,
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "Ultimaker",
|
||||
|
@ -91,7 +91,7 @@
|
|||
"display_name": "Cura Profile Writer",
|
||||
"description": "Provides support for exporting Cura profiles.",
|
||||
"package_version": "1.0.0",
|
||||
"cura_version": 4,
|
||||
"sdk_version": 4,
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "Ultimaker",
|
||||
|
@ -108,7 +108,7 @@
|
|||
"display_name": "Firmware Update Checker",
|
||||
"description": "Checks for firmware updates.",
|
||||
"package_version": "1.0.0",
|
||||
"cura_version": 4,
|
||||
"sdk_version": 4,
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "Ultimaker",
|
||||
|
@ -125,7 +125,7 @@
|
|||
"display_name": "Compressed G-code Reader",
|
||||
"description": "Reads g-code from a compressed archive.",
|
||||
"package_version": "1.0.0",
|
||||
"cura_version": 4,
|
||||
"sdk_version": 4,
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "Ultimaker",
|
||||
|
@ -142,7 +142,7 @@
|
|||
"display_name": "Compressed G-code Writer",
|
||||
"description": "Writes g-code to a compressed archive.",
|
||||
"package_version": "1.0.0",
|
||||
"cura_version": 4,
|
||||
"sdk_version": 4,
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "Ultimaker",
|
||||
|
@ -159,7 +159,7 @@
|
|||
"display_name": "G-Code Profile Reader",
|
||||
"description": "Provides support for importing profiles from g-code files.",
|
||||
"package_version": "1.0.0",
|
||||
"cura_version": 4,
|
||||
"sdk_version": 4,
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "Ultimaker",
|
||||
|
@ -176,7 +176,7 @@
|
|||
"display_name": "G-Code Reader",
|
||||
"description": "Allows loading and displaying G-code files.",
|
||||
"package_version": "1.0.0",
|
||||
"cura_version": 4,
|
||||
"sdk_version": 4,
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "VictorLarchenko",
|
||||
|
@ -193,7 +193,7 @@
|
|||
"display_name": "G-Code Writer",
|
||||
"description": "Writes g-code to a file.",
|
||||
"package_version": "1.0.0",
|
||||
"cura_version": 4,
|
||||
"sdk_version": 4,
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "Ultimaker",
|
||||
|
@ -210,7 +210,7 @@
|
|||
"display_name": "Image Reader",
|
||||
"description": "Enables ability to generate printable geometry from 2D image files.",
|
||||
"package_version": "1.0.0",
|
||||
"cura_version": 4,
|
||||
"sdk_version": 4,
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "Ultimaker",
|
||||
|
@ -227,7 +227,7 @@
|
|||
"display_name": "Legacy Cura Profile Reader",
|
||||
"description": "Provides support for importing profiles from legacy Cura versions.",
|
||||
"package_version": "1.0.0",
|
||||
"cura_version": 4,
|
||||
"sdk_version": 4,
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "Ultimaker",
|
||||
|
@ -244,7 +244,7 @@
|
|||
"display_name": "Machine Settings Action",
|
||||
"description": "Provides a way to change machine settings (such as build volume, nozzle size, etc.).",
|
||||
"package_version": "1.0.0",
|
||||
"cura_version": 4,
|
||||
"sdk_version": 4,
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "fieldOfView",
|
||||
|
@ -261,7 +261,7 @@
|
|||
"display_name": "Model Checker",
|
||||
"description": "Checks models and print configuration for possible printing issues and give suggestions.",
|
||||
"package_version": "0.1.0",
|
||||
"cura_version": 4,
|
||||
"sdk_version": 4,
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "Ultimaker",
|
||||
|
@ -278,7 +278,7 @@
|
|||
"display_name": "Monitor Stage",
|
||||
"description": "Provides a monitor stage in Cura.",
|
||||
"package_version": "1.0.0",
|
||||
"cura_version": 4,
|
||||
"sdk_version": 4,
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "Ultimaker",
|
||||
|
@ -295,7 +295,7 @@
|
|||
"display_name": "Per-Object Settings Tool",
|
||||
"description": "Provides the per-model settings.",
|
||||
"package_version": "1.0.0",
|
||||
"cura_version": 4,
|
||||
"sdk_version": 4,
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "Ultimaker",
|
||||
|
@ -312,7 +312,7 @@
|
|||
"display_name": "Post Processing",
|
||||
"description": "Extension that allows for user created scripts for post processing.",
|
||||
"package_version": "2.2.0",
|
||||
"cura_version": 4,
|
||||
"sdk_version": 4,
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "Ultimaker",
|
||||
|
@ -329,7 +329,7 @@
|
|||
"display_name": "Prepare Stage",
|
||||
"description": "Provides a prepare stage in Cura.",
|
||||
"package_version": "1.0.0",
|
||||
"cura_version": 4,
|
||||
"sdk_version": 4,
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "Ultimaker",
|
||||
|
@ -346,7 +346,7 @@
|
|||
"display_name": "Removable Drive Output Device",
|
||||
"description": "Provides removable drive hotplugging and writing support.",
|
||||
"package_version": "1.0.0",
|
||||
"cura_version": 4,
|
||||
"sdk_version": 4,
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "Ultimaker",
|
||||
|
@ -363,7 +363,7 @@
|
|||
"display_name": "Simulation View",
|
||||
"description": "Provides the Simulation view.",
|
||||
"package_version": "1.0.0",
|
||||
"cura_version": 4,
|
||||
"sdk_version": 4,
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "Ultimaker",
|
||||
|
@ -380,7 +380,7 @@
|
|||
"display_name": "Slice Info",
|
||||
"description": "Submits anonymous slice info. Can be disabled through preferences.",
|
||||
"package_version": "1.0.0",
|
||||
"cura_version": 4,
|
||||
"sdk_version": 4,
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "Ultimaker",
|
||||
|
@ -397,7 +397,7 @@
|
|||
"display_name": "Solid View",
|
||||
"description": "Provides a normal solid mesh view.",
|
||||
"package_version": "1.0.0",
|
||||
"cura_version": 4,
|
||||
"sdk_version": 4,
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "Ultimaker",
|
||||
|
@ -414,7 +414,7 @@
|
|||
"display_name": "Support Eraser Tool",
|
||||
"description": "Creates an eraser mesh to block the printing of support in certain places.",
|
||||
"package_version": "1.0.0",
|
||||
"cura_version": 4,
|
||||
"sdk_version": 4,
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "Ultimaker",
|
||||
|
@ -431,7 +431,7 @@
|
|||
"display_name": "Toolbox",
|
||||
"description": "Find, manage and install new Cura packages.",
|
||||
"package_version": "1.0.0",
|
||||
"cura_version": 4,
|
||||
"sdk_version": 4,
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "Ultimaker",
|
||||
|
@ -448,7 +448,7 @@
|
|||
"display_name": "UFP Writer",
|
||||
"description": "Provides support for writing Ultimaker Format Packages.",
|
||||
"package_version": "1.0.0",
|
||||
"cura_version": 4,
|
||||
"sdk_version": 4,
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "Ultimaker",
|
||||
|
@ -465,7 +465,7 @@
|
|||
"display_name": "Ultimaker Machine Actions",
|
||||
"description": "Provides machine actions for Ultimaker machines (such as bed leveling wizard, selecting upgrades, etc.).",
|
||||
"package_version": "1.0.0",
|
||||
"cura_version": 4,
|
||||
"sdk_version": 4,
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "Ultimaker",
|
||||
|
@ -482,7 +482,7 @@
|
|||
"display_name": "UM3 Network Printing",
|
||||
"description": "Manages network connections to Ultimaker 3 printers.",
|
||||
"package_version": "1.0.0",
|
||||
"cura_version": 4,
|
||||
"sdk_version": 4,
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "Ultimaker",
|
||||
|
@ -499,7 +499,7 @@
|
|||
"display_name": "USB Printing",
|
||||
"description": "Accepts G-Code and sends them to a printer. Plugin can also update firmware.",
|
||||
"package_version": "1.0.0",
|
||||
"cura_version": 4,
|
||||
"sdk_version": 4,
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "Ultimaker",
|
||||
|
@ -516,7 +516,7 @@
|
|||
"display_name": "User Agreement",
|
||||
"description": "Ask the user once if he/she agrees with our license.",
|
||||
"package_version": "1.0.0",
|
||||
"cura_version": 4,
|
||||
"sdk_version": 4,
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "Ultimaker",
|
||||
|
@ -533,7 +533,7 @@
|
|||
"display_name": "Version Upgrade 2.1 to 2.2",
|
||||
"description": "Upgrades configurations from Cura 2.1 to Cura 2.2.",
|
||||
"package_version": "1.0.0",
|
||||
"cura_version": 4,
|
||||
"sdk_version": 4,
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "Ultimaker",
|
||||
|
@ -550,7 +550,7 @@
|
|||
"display_name": "Version Upgrade 2.2 to 2.4",
|
||||
"description": "Upgrades configurations from Cura 2.2 to Cura 2.4.",
|
||||
"package_version": "1.0.0",
|
||||
"cura_version": 4,
|
||||
"sdk_version": 4,
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "Ultimaker",
|
||||
|
@ -567,7 +567,7 @@
|
|||
"display_name": "Version Upgrade 2.5 to 2.6",
|
||||
"description": "Upgrades configurations from Cura 2.5 to Cura 2.6.",
|
||||
"package_version": "1.0.0",
|
||||
"cura_version": 4,
|
||||
"sdk_version": 4,
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "Ultimaker",
|
||||
|
@ -584,7 +584,7 @@
|
|||
"display_name": "Version Upgrade 2.6 to 2.7",
|
||||
"description": "Upgrades configurations from Cura 2.6 to Cura 2.7.",
|
||||
"package_version": "1.0.0",
|
||||
"cura_version": 4,
|
||||
"sdk_version": 4,
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "Ultimaker",
|
||||
|
@ -601,7 +601,7 @@
|
|||
"display_name": "Version Upgrade 2.7 to 3.0",
|
||||
"description": "Upgrades configurations from Cura 2.7 to Cura 3.0.",
|
||||
"package_version": "1.0.0",
|
||||
"cura_version": 4,
|
||||
"sdk_version": 4,
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "Ultimaker",
|
||||
|
@ -618,7 +618,7 @@
|
|||
"display_name": "Version Upgrade 3.0 to 3.1",
|
||||
"description": "Upgrades configurations from Cura 3.0 to Cura 3.1.",
|
||||
"package_version": "1.0.0",
|
||||
"cura_version": 4,
|
||||
"sdk_version": 4,
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "Ultimaker",
|
||||
|
@ -635,7 +635,7 @@
|
|||
"display_name": "Version Upgrade 3.2 to 3.3",
|
||||
"description": "Upgrades configurations from Cura 3.2 to Cura 3.3.",
|
||||
"package_version": "1.0.0",
|
||||
"cura_version": 4,
|
||||
"sdk_version": 4,
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "Ultimaker",
|
||||
|
@ -652,7 +652,7 @@
|
|||
"display_name": "Version Upgrade 3.3 to 3.4",
|
||||
"description": "Upgrades configurations from Cura 3.3 to Cura 3.4.",
|
||||
"package_version": "1.0.0",
|
||||
"cura_version": 4,
|
||||
"sdk_version": 4,
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "Ultimaker",
|
||||
|
@ -669,7 +669,7 @@
|
|||
"display_name": "X3D Reader",
|
||||
"description": "Provides support for reading X3D files.",
|
||||
"package_version": "0.5.0",
|
||||
"cura_version": 4,
|
||||
"sdk_version": 4,
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "SevaAlekseyev",
|
||||
|
@ -686,7 +686,7 @@
|
|||
"display_name": "XML Material Profiles",
|
||||
"description": "Provides capabilities to read and write XML-based material profiles.",
|
||||
"package_version": "1.0.0",
|
||||
"cura_version": 4,
|
||||
"sdk_version": 4,
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "Ultimaker",
|
||||
|
@ -703,7 +703,7 @@
|
|||
"display_name": "X-Ray View",
|
||||
"description": "Provides the X-Ray view.",
|
||||
"package_version": "1.0.0",
|
||||
"cura_version": 4,
|
||||
"sdk_version": 4,
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "Ultimaker",
|
||||
|
@ -720,7 +720,7 @@
|
|||
"display_name": "Dagoma Chromatik PLA",
|
||||
"description": "Filament testé et approuvé pour les imprimantes 3D Dagoma. Chromatik est l'idéal pour débuter et suivre les tutoriels premiers pas. Il vous offre qualité et résistance pour chacune de vos impressions.",
|
||||
"package_version": "1.0.0",
|
||||
"cura_version": 4,
|
||||
"sdk_version": 4,
|
||||
"website": "https://dagoma.fr/boutique/filaments.html",
|
||||
"author": {
|
||||
"author_id": "Dagoma",
|
||||
|
@ -737,7 +737,7 @@
|
|||
"display_name": "FABtotum ABS",
|
||||
"description": "This material is easy to be extruded but it is not the simplest to use. It is one of the most used in 3D printing to get very well finished objects. It is not sustainable and its smoke can be dangerous if inhaled. The reason to prefer this filament to PLA is mainly because of its precision and mechanical specs. ABS (for plastic) stands for Acrylonitrile Butadiene Styrene and it is a thermoplastic which is widely used in everyday objects. It can be printed with any FFF 3D printer which can get to high temperatures as it must be extruded in a range between 220° and 245°, so it’s compatible with all versions of the FABtotum Personal fabricator.",
|
||||
"package_version": "1.0.0",
|
||||
"cura_version": 4,
|
||||
"sdk_version": 4,
|
||||
"website": "https://store.fabtotum.com/eu/products/filaments.html?filament_type=40",
|
||||
"author": {
|
||||
"author_id": "FABtotum",
|
||||
|
@ -754,7 +754,7 @@
|
|||
"display_name": "FABtotum Nylon",
|
||||
"description": "When 3D printing started this material was not listed among the extrudable filaments. It is flexible as well as resistant to tractions. It is well known for its uses in textile but also in industries which require a strong and flexible material. There are different kinds of Nylon: 3D printing mostly uses Nylon 6 and Nylon 6.6, which are the most common. It requires higher temperatures to be printed, so a 3D printer must be able to reach them (around 240°C): the FABtotum, of course, can.",
|
||||
"package_version": "1.0.0",
|
||||
"cura_version": 4,
|
||||
"sdk_version": 4,
|
||||
"website": "https://store.fabtotum.com/eu/products/filaments.html?filament_type=53",
|
||||
"author": {
|
||||
"author_id": "FABtotum",
|
||||
|
@ -771,7 +771,7 @@
|
|||
"display_name": "FABtotum PLA",
|
||||
"description": "It is the most common filament used for 3D printing. It is studied to be bio-degradable as it comes from corn starch’s sugar mainly. It is completely made of renewable sources and has no footprint on polluting. PLA stands for PolyLactic Acid and it is a thermoplastic that today is still considered the easiest material to be 3D printed. It can be extruded at lower temperatures: the standard range of FABtotum’s one is between 185° and 195°.",
|
||||
"package_version": "1.0.0",
|
||||
"cura_version": 4,
|
||||
"sdk_version": 4,
|
||||
"website": "https://store.fabtotum.com/eu/products/filaments.html?filament_type=39",
|
||||
"author": {
|
||||
"author_id": "FABtotum",
|
||||
|
@ -788,7 +788,7 @@
|
|||
"display_name": "FABtotum TPU Shore 98A",
|
||||
"description": "",
|
||||
"package_version": "1.0.0",
|
||||
"cura_version": 4,
|
||||
"sdk_version": 4,
|
||||
"website": "https://store.fabtotum.com/eu/products/filaments.html?filament_type=66",
|
||||
"author": {
|
||||
"author_id": "FABtotum",
|
||||
|
@ -805,7 +805,7 @@
|
|||
"display_name": "Fiberlogy HD PLA",
|
||||
"description": "With our HD PLA you have many more options. You can use this material in two ways. Choose the one you like best. You can use it as a normal PLA and get prints characterized by a very good adhesion between the layers and high precision. You can also make your prints acquire similar properties to that of ABS – better impact resistance and high temperature resistance. All you need is an oven. Yes, an oven! By annealing our HD PLA in an oven, in accordance with the manual, you will avoid all the inconveniences of printing with ABS, such as unpleasant odour or hazardous fumes.",
|
||||
"package_version": "1.0.0",
|
||||
"cura_version": 4,
|
||||
"sdk_version": 4,
|
||||
"website": "http://fiberlogy.com/en/fiberlogy-filaments/filament-hd-pla/",
|
||||
"author": {
|
||||
"author_id": "Fiberlogy",
|
||||
|
@ -822,7 +822,7 @@
|
|||
"display_name": "Filo3D PLA",
|
||||
"description": "Fast, safe and reliable printing. PLA is ideal for the fast and reliable printing of parts and prototypes with a great surface quality.",
|
||||
"package_version": "1.0.0",
|
||||
"cura_version": 4,
|
||||
"sdk_version": 4,
|
||||
"website": "https://dagoma.fr",
|
||||
"author": {
|
||||
"author_id": "Dagoma",
|
||||
|
@ -839,7 +839,7 @@
|
|||
"display_name": "IMADE3D JellyBOX PETG",
|
||||
"description": "",
|
||||
"package_version": "1.0.0",
|
||||
"cura_version": 4,
|
||||
"sdk_version": 4,
|
||||
"website": "http://shop.imade3d.com/filament.html",
|
||||
"author": {
|
||||
"author_id": "IMADE3D",
|
||||
|
@ -856,7 +856,7 @@
|
|||
"display_name": "IMADE3D JellyBOX PLA",
|
||||
"description": "",
|
||||
"package_version": "1.0.0",
|
||||
"cura_version": 4,
|
||||
"sdk_version": 4,
|
||||
"website": "http://shop.imade3d.com/filament.html",
|
||||
"author": {
|
||||
"author_id": "IMADE3D",
|
||||
|
@ -873,7 +873,7 @@
|
|||
"display_name": "Octofiber PLA",
|
||||
"description": "PLA material from Octofiber.",
|
||||
"package_version": "1.0.0",
|
||||
"cura_version": 4,
|
||||
"sdk_version": 4,
|
||||
"website": "https://nl.octofiber.com/3d-printing-filament/pla.html",
|
||||
"author": {
|
||||
"author_id": "Octofiber",
|
||||
|
@ -890,7 +890,7 @@
|
|||
"display_name": "PolyFlex™ PLA",
|
||||
"description": "PolyFlex™ is a highly flexible yet easy to print 3D printing material. Featuring good elasticity and a large strain-to- failure, PolyFlex™ opens up a completely new realm of applications.",
|
||||
"package_version": "1.0.0",
|
||||
"cura_version": 4,
|
||||
"sdk_version": 4,
|
||||
"website": "http://www.polymaker.com/shop/polyflex/",
|
||||
"author": {
|
||||
"author_id": "Polymaker",
|
||||
|
@ -907,7 +907,7 @@
|
|||
"display_name": "PolyMax™ PLA",
|
||||
"description": "PolyMax™ PLA is a 3D printing material with excellent mechanical properties and printing quality. PolyMax™ PLA has an impact resistance of up to nine times that of regular PLA, and better overall mechanical properties than ABS.",
|
||||
"package_version": "1.0.0",
|
||||
"cura_version": 4,
|
||||
"sdk_version": 4,
|
||||
"website": "http://www.polymaker.com/shop/polymax/",
|
||||
"author": {
|
||||
"author_id": "Polymaker",
|
||||
|
@ -924,7 +924,7 @@
|
|||
"display_name": "PolyPlus™ PLA True Colour",
|
||||
"description": "PolyPlus™ PLA is a premium PLA designed for all desktop FDM/FFF 3D printers. It is produced with our patented Jam-Free™ technology that ensures consistent extrusion and prevents jams.",
|
||||
"package_version": "1.0.0",
|
||||
"cura_version": 4,
|
||||
"sdk_version": 4,
|
||||
"website": "http://www.polymaker.com/shop/polyplus-true-colour/",
|
||||
"author": {
|
||||
"author_id": "Polymaker",
|
||||
|
@ -941,7 +941,7 @@
|
|||
"display_name": "PolyWood™ PLA",
|
||||
"description": "PolyWood™ is a wood mimic printing material that contains no actual wood ensuring a clean Jam-Free™ printing experience.",
|
||||
"package_version": "1.0.0",
|
||||
"cura_version": 4,
|
||||
"sdk_version": 4,
|
||||
"website": "http://www.polymaker.com/shop/polywood/",
|
||||
"author": {
|
||||
"author_id": "Polymaker",
|
||||
|
@ -958,7 +958,7 @@
|
|||
"display_name": "Ultimaker ABS",
|
||||
"description": "Example package for material and quality profiles for Ultimaker materials.",
|
||||
"package_version": "1.0.0",
|
||||
"cura_version": 4,
|
||||
"sdk_version": 4,
|
||||
"website": "https://ultimaker.com/products/materials/abs",
|
||||
"author": {
|
||||
"author_id": "Ultimaker",
|
||||
|
@ -977,7 +977,7 @@
|
|||
"display_name": "Ultimaker CPE",
|
||||
"description": "Example package for material and quality profiles for Ultimaker materials.",
|
||||
"package_version": "1.0.0",
|
||||
"cura_version": 4,
|
||||
"sdk_version": 4,
|
||||
"website": "https://ultimaker.com/products/materials/abs",
|
||||
"author": {
|
||||
"author_id": "Ultimaker",
|
||||
|
@ -996,7 +996,7 @@
|
|||
"display_name": "Ultimaker Nylon",
|
||||
"description": "Example package for material and quality profiles for Ultimaker materials.",
|
||||
"package_version": "1.0.0",
|
||||
"cura_version": 4,
|
||||
"sdk_version": 4,
|
||||
"website": "https://ultimaker.com/products/materials/abs",
|
||||
"author": {
|
||||
"author_id": "Ultimaker",
|
||||
|
@ -1015,7 +1015,7 @@
|
|||
"display_name": "Ultimaker PC",
|
||||
"description": "Example package for material and quality profiles for Ultimaker materials.",
|
||||
"package_version": "1.0.0",
|
||||
"cura_version": 4,
|
||||
"sdk_version": 4,
|
||||
"website": "https://ultimaker.com/products/materials/pc",
|
||||
"author": {
|
||||
"author_id": "Ultimaker",
|
||||
|
@ -1034,7 +1034,7 @@
|
|||
"display_name": "Ultimaker PLA",
|
||||
"description": "Example package for material and quality profiles for Ultimaker materials.",
|
||||
"package_version": "1.0.0",
|
||||
"cura_version": 4,
|
||||
"sdk_version": 4,
|
||||
"website": "https://ultimaker.com/products/materials/abs",
|
||||
"author": {
|
||||
"author_id": "Ultimaker",
|
||||
|
@ -1053,7 +1053,7 @@
|
|||
"display_name": "Ultimaker PVA",
|
||||
"description": "Example package for material and quality profiles for Ultimaker materials.",
|
||||
"package_version": "1.0.0",
|
||||
"cura_version": 4,
|
||||
"sdk_version": 4,
|
||||
"website": "https://ultimaker.com/products/materials/abs",
|
||||
"author": {
|
||||
"author_id": "Ultimaker",
|
||||
|
@ -1072,7 +1072,7 @@
|
|||
"display_name": "Vertex Delta ABS",
|
||||
"description": "ABS material and quality files for the Delta Vertex K8800.",
|
||||
"package_version": "1.0.0",
|
||||
"cura_version": 4,
|
||||
"sdk_version": 4,
|
||||
"website": "https://vertex3dprinter.eu",
|
||||
"author": {
|
||||
"author_id": "Velleman",
|
||||
|
@ -1089,7 +1089,7 @@
|
|||
"display_name": "Vertex Delta PET",
|
||||
"description": "ABS material and quality files for the Delta Vertex K8800.",
|
||||
"package_version": "1.0.0",
|
||||
"cura_version": 4,
|
||||
"sdk_version": 4,
|
||||
"website": "https://vertex3dprinter.eu",
|
||||
"author": {
|
||||
"author_id": "Velleman",
|
||||
|
@ -1106,7 +1106,7 @@
|
|||
"display_name": "Vertex Delta PLA",
|
||||
"description": "ABS material and quality files for the Delta Vertex K8800.",
|
||||
"package_version": "1.0.0",
|
||||
"cura_version": 4,
|
||||
"sdk_version": 4,
|
||||
"website": "https://vertex3dprinter.eu",
|
||||
"author": {
|
||||
"author_id": "Velleman",
|
||||
|
@ -1123,7 +1123,7 @@
|
|||
"display_name": "Vertex Delta TPU",
|
||||
"description": "ABS material and quality files for the Delta Vertex K8800.",
|
||||
"package_version": "1.0.0",
|
||||
"cura_version": 4,
|
||||
"sdk_version": 4,
|
||||
"website": "https://vertex3dprinter.eu",
|
||||
"author": {
|
||||
"author_id": "Velleman",
|
||||
|
@ -1132,5 +1132,277 @@
|
|||
"website": "https://www.vellemanprojects.eu"
|
||||
}
|
||||
}
|
||||
},
|
||||
"ConsoleLogger": {
|
||||
"package_info": {
|
||||
"package_id": "ConsoleLogger",
|
||||
"package_type": "plugin",
|
||||
"display_name": "Console Logger",
|
||||
"description": "Outputs log information to the console.",
|
||||
"package_version": "1.0.0",
|
||||
"sdk_version": 4,
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "Ultimaker",
|
||||
"display_name": "Ultimaker B.V.",
|
||||
"email": "plugins@ultimaker.com",
|
||||
"website": "https://ultimaker.com"
|
||||
}
|
||||
}
|
||||
},
|
||||
"OBJReader": {
|
||||
"package_info": {
|
||||
"package_id": "OBJReader",
|
||||
"package_type": "plugin",
|
||||
"display_name": "Wavefront OBJ Reader",
|
||||
"description": "Makes it possible to read Wavefront OBJ files.",
|
||||
"package_version": "1.0.0",
|
||||
"sdk_version": 4,
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "Ultimaker",
|
||||
"display_name": "Ultimaker B.V.",
|
||||
"email": "plugins@ultimaker.com",
|
||||
"website": "https://ultimaker.com"
|
||||
}
|
||||
}
|
||||
},
|
||||
"OBJWriter": {
|
||||
"package_info": {
|
||||
"package_id": "OBJWriter",
|
||||
"package_type": "plugin",
|
||||
"display_name": "Wavefront OBJ Writer",
|
||||
"description": "Makes it possible to write Wavefront OBJ files.",
|
||||
"package_version": "1.0.0",
|
||||
"sdk_version": 4,
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "Ultimaker",
|
||||
"display_name": "Ultimaker B.V.",
|
||||
"email": "plugins@ultimaker.com",
|
||||
"website": "https://ultimaker.com"
|
||||
}
|
||||
}
|
||||
},
|
||||
"STLReader": {
|
||||
"package_info": {
|
||||
"package_id": "STLReader",
|
||||
"package_type": "plugin",
|
||||
"display_name": "STL Reader",
|
||||
"description": "Provides support for reading STL files.",
|
||||
"package_version": "1.0.0",
|
||||
"sdk_version": 4,
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "Ultimaker",
|
||||
"display_name": "Ultimaker B.V.",
|
||||
"email": "plugins@ultimaker.com",
|
||||
"website": "https://ultimaker.com"
|
||||
}
|
||||
}
|
||||
},
|
||||
"STLWriter": {
|
||||
"package_info": {
|
||||
"package_id": "STLWriter",
|
||||
"package_type": "plugin",
|
||||
"display_name": "STL Writer",
|
||||
"description": "Provides support for writing STL files.",
|
||||
"package_version": "1.0.0",
|
||||
"sdk_version": 4,
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "Ultimaker",
|
||||
"display_name": "Ultimaker B.V.",
|
||||
"email": "plugins@ultimaker.com",
|
||||
"website": "https://ultimaker.com"
|
||||
}
|
||||
}
|
||||
},
|
||||
"FileLogger": {
|
||||
"package_info": {
|
||||
"package_id": "FileLogger",
|
||||
"package_type": "plugin",
|
||||
"display_name": "File Logger",
|
||||
"description": "Outputs log information to a file in your settings folder.",
|
||||
"package_version": "1.0.0",
|
||||
"sdk_version": 4,
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "Ultimaker",
|
||||
"display_name": "Ultimaker B.V.",
|
||||
"email": "plugins@ultimaker.com",
|
||||
"website": "https://ultimaker.com"
|
||||
}
|
||||
}
|
||||
},
|
||||
"LocalContainerProvider": {
|
||||
"package_info": {
|
||||
"package_id": "LocalContainerProvider",
|
||||
"package_type": "plugin",
|
||||
"display_name": "Local Container Provider",
|
||||
"description": "Provides built-in setting containers that come with the installation of the application.",
|
||||
"package_version": "1.0.0",
|
||||
"sdk_version": 4,
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "Ultimaker",
|
||||
"display_name": "Ultimaker B.V.",
|
||||
"email": "plugins@ultimaker.com",
|
||||
"website": "https://ultimaker.com"
|
||||
}
|
||||
}
|
||||
},
|
||||
"LocalFileOutputDevice": {
|
||||
"package_info": {
|
||||
"package_id": "LocalFileOutputDevice",
|
||||
"package_type": "plugin",
|
||||
"display_name": "Local File Output Device",
|
||||
"description": "Enables saving to local files.",
|
||||
"package_version": "1.0.0",
|
||||
"sdk_version": 4,
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "Ultimaker",
|
||||
"display_name": "Ultimaker B.V.",
|
||||
"email": "plugins@ultimaker.com",
|
||||
"website": "https://ultimaker.com"
|
||||
}
|
||||
}
|
||||
},
|
||||
"CameraTool": {
|
||||
"package_info": {
|
||||
"package_id": "CameraTool",
|
||||
"package_type": "plugin",
|
||||
"display_name": "Camera Tool",
|
||||
"description": "Provides the tool to manipulate the camera.",
|
||||
"package_version": "1.0.0",
|
||||
"sdk_version": 4,
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "Ultimaker",
|
||||
"display_name": "Ultimaker B.V.",
|
||||
"email": "plugins@ultimaker.com",
|
||||
"website": "https://ultimaker.com"
|
||||
}
|
||||
}
|
||||
},
|
||||
"MirrorTool": {
|
||||
"package_info": {
|
||||
"package_id": "MirrorTool",
|
||||
"package_type": "plugin",
|
||||
"display_name": "Mirror Tool",
|
||||
"description": "Provides the Mirror tool.",
|
||||
"package_version": "1.0.0",
|
||||
"sdk_version": 4,
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "Ultimaker",
|
||||
"display_name": "Ultimaker B.V.",
|
||||
"email": "plugins@ultimaker.com",
|
||||
"website": "https://ultimaker.com"
|
||||
}
|
||||
}
|
||||
},
|
||||
"RotateTool": {
|
||||
"package_info": {
|
||||
"package_id": "RotateTool",
|
||||
"package_type": "plugin",
|
||||
"display_name": "Rotate Tool",
|
||||
"description": "Provides the Rotate tool.",
|
||||
"package_version": "1.0.0",
|
||||
"sdk_version": 4,
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "Ultimaker",
|
||||
"display_name": "Ultimaker B.V.",
|
||||
"email": "plugins@ultimaker.com",
|
||||
"website": "https://ultimaker.com"
|
||||
}
|
||||
}
|
||||
},
|
||||
"ScaleTool": {
|
||||
"package_info": {
|
||||
"package_id": "ScaleTool",
|
||||
"package_type": "plugin",
|
||||
"display_name": "Scale Tool",
|
||||
"description": "Provides the Scale tool.",
|
||||
"package_version": "1.0.0",
|
||||
"sdk_version": 4,
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "Ultimaker",
|
||||
"display_name": "Ultimaker B.V.",
|
||||
"email": "plugins@ultimaker.com",
|
||||
"website": "https://ultimaker.com"
|
||||
}
|
||||
}
|
||||
},
|
||||
"SelectionTool": {
|
||||
"package_info": {
|
||||
"package_id": "SelectionTool",
|
||||
"package_type": "plugin",
|
||||
"display_name": "Selection Tool",
|
||||
"description": "Provides the Selection tool.",
|
||||
"package_version": "1.0.0",
|
||||
"sdk_version": 4,
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "Ultimaker",
|
||||
"display_name": "Ultimaker B.V.",
|
||||
"email": "plugins@ultimaker.com",
|
||||
"website": "https://ultimaker.com"
|
||||
}
|
||||
}
|
||||
},
|
||||
"TranslateTool": {
|
||||
"package_info": {
|
||||
"package_id": "TranslateTool",
|
||||
"package_type": "plugin",
|
||||
"display_name": "Move Tool",
|
||||
"description": "Provides the Move tool.",
|
||||
"package_version": "1.0.0",
|
||||
"sdk_version": 4,
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "Ultimaker",
|
||||
"display_name": "Ultimaker B.V.",
|
||||
"email": "plugins@ultimaker.com",
|
||||
"website": "https://ultimaker.com"
|
||||
}
|
||||
}
|
||||
},
|
||||
"UpdateChecker": {
|
||||
"package_info": {
|
||||
"package_id": "UpdateChecker",
|
||||
"package_type": "plugin",
|
||||
"display_name": "Update Checker",
|
||||
"description": "Checks for updates of the software.",
|
||||
"package_version": "1.0.0",
|
||||
"sdk_version": 4,
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "Ultimaker",
|
||||
"display_name": "Ultimaker B.V.",
|
||||
"email": "plugins@ultimaker.com",
|
||||
"website": "https://ultimaker.com"
|
||||
}
|
||||
}
|
||||
},
|
||||
"SimpleView": {
|
||||
"package_info": {
|
||||
"package_id": "SimpleView",
|
||||
"package_type": "plugin",
|
||||
"display_name": "Simple View",
|
||||
"description": "Provides a simple solid mesh view.",
|
||||
"package_version": "1.0.0",
|
||||
"sdk_version": 4,
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "Ultimaker",
|
||||
"display_name": "Ultimaker B.V.",
|
||||
"email": "plugins@ultimaker.com",
|
||||
"website": "https://ultimaker.com"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1277,6 +1277,29 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"wall_min_flow":
|
||||
{
|
||||
"label": "Minimum Wall Flow",
|
||||
"description": "Minimum allowed percentage flow for a wall line. The wall overlap compensation reduces a wall's flow when it lies close to an existing wall. Walls whose flow is less than this value will be replaced with a travel move. When using this setting, you must enable the wall overlap compensation and print the outer wall before inner walls.",
|
||||
"unit": "%",
|
||||
"minimum_value": "0",
|
||||
"maximum_value": "100",
|
||||
"default_value": 0,
|
||||
"type": "float",
|
||||
"enabled": "travel_compensate_overlapping_walls_0_enabled or travel_compensate_overlapping_walls_x_enabled",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true
|
||||
},
|
||||
"wall_min_flow_retract":
|
||||
{
|
||||
"label": "Prefer Retract",
|
||||
"description": "If enabled, retraction is used rather than combing for travel moves that replace walls whose flow is below the minimum flow threshold.",
|
||||
"type": "bool",
|
||||
"default_value": false,
|
||||
"enabled": "(travel_compensate_overlapping_walls_0_enabled or travel_compensate_overlapping_walls_x_enabled) and wall_min_flow > 0",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true
|
||||
},
|
||||
"fill_perimeter_gaps":
|
||||
{
|
||||
"label": "Fill Gaps Between Walls",
|
||||
|
@ -1807,6 +1830,30 @@
|
|||
"limit_to_extruder": "infill_extruder_nr",
|
||||
"settable_per_mesh": true
|
||||
},
|
||||
"infill_support_enabled":
|
||||
{
|
||||
"label": "Infill Support",
|
||||
"description": "Print infill structures only where tops of the model should be supported. Enabling this reduces print time and material usage, but leads to ununiform object strength.",
|
||||
"type": "bool",
|
||||
"default_value": false,
|
||||
"enabled": "infill_sparse_density > 0",
|
||||
"limit_to_extruder": "infill_extruder_nr",
|
||||
"settable_per_mesh": true
|
||||
},
|
||||
"infill_support_angle":
|
||||
{
|
||||
"label": "Infill Overhang Angle",
|
||||
"description": "The minimum angle of internal overhangs for which infill is added. At a value of 0° objects are totally filled with infill, 90° will not provide any infill.",
|
||||
"unit": "°",
|
||||
"type": "float",
|
||||
"minimum_value": "0",
|
||||
"minimum_value_warning": "2",
|
||||
"maximum_value": "90",
|
||||
"default_value": 40,
|
||||
"enabled": "infill_sparse_density > 0 and infill_support_enabled",
|
||||
"limit_to_extruder": "infill_extruder_nr",
|
||||
"settable_per_mesh": true
|
||||
},
|
||||
"skin_preshrink":
|
||||
{
|
||||
"label": "Skin Removal Width",
|
||||
|
@ -3597,7 +3644,7 @@
|
|||
"description": "The extruder train to use for printing the support. This is used in multi-extrusion.",
|
||||
"type": "extruder",
|
||||
"default_value": "0",
|
||||
"value": "-1",
|
||||
"value": "defaultExtruderPosition()",
|
||||
"enabled": "(support_enable or support_tree_enable) and extruders_enabled_count > 1",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": false,
|
||||
|
@ -4338,7 +4385,7 @@
|
|||
"description": "The extruder train to use for printing the skirt/brim/raft. This is used in multi-extrusion.",
|
||||
"type": "extruder",
|
||||
"default_value": "0",
|
||||
"value": "-1",
|
||||
"value": "defaultExtruderPosition()",
|
||||
"enabled": "extruders_enabled_count > 1 and resolveOrValue('adhesion_type') != 'none'",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": false
|
||||
|
@ -5772,16 +5819,6 @@
|
|||
"limit_to_extruder": "infill_extruder_nr",
|
||||
"settable_per_mesh": true
|
||||
},
|
||||
"cross_infill_apply_pockets_alternatingly":
|
||||
{
|
||||
"label": "Alternate Cross 3D Pockets",
|
||||
"description": "Only apply pockets at half of the four-way crossings in the cross 3D pattern and alternate the location of the pockets between heights where the pattern is touching itself.",
|
||||
"type": "bool",
|
||||
"default_value": true,
|
||||
"enabled": "infill_pattern == 'cross_3d'",
|
||||
"limit_to_extruder": "infill_extruder_nr",
|
||||
"settable_per_mesh": true
|
||||
},
|
||||
"cross_infill_density_image":
|
||||
{
|
||||
"label": "Cross Infill Density Image",
|
||||
|
@ -5798,7 +5835,7 @@
|
|||
"description": "The file location of an image of which the brightness values determine the minimal density at the corresponding location in the support.",
|
||||
"type": "str",
|
||||
"default_value": "",
|
||||
"enabled": "infill_pattern == 'cross' or infill_pattern == 'cross_3d'",
|
||||
"enabled": "support_pattern == 'cross' or support_pattern == 'cross_3d'",
|
||||
"limit_to_extruder": "support_infill_extruder_nr",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true
|
||||
|
@ -5928,14 +5965,6 @@
|
|||
"limit_to_extruder": "support_infill_extruder_nr",
|
||||
"settable_per_mesh": true
|
||||
},
|
||||
"infill_hollow":
|
||||
{
|
||||
"label": "Hollow Out Objects",
|
||||
"description": "Remove all infill and make the inside of the object eligible for support.",
|
||||
"type": "bool",
|
||||
"default_value": false,
|
||||
"settable_per_mesh": true
|
||||
},
|
||||
"magic_fuzzy_skin_enabled":
|
||||
{
|
||||
"label": "Fuzzy Skin",
|
||||
|
@ -6672,14 +6701,6 @@
|
|||
"type": "float",
|
||||
"enabled": "bridge_settings_enabled and bridge_enable_more_layers",
|
||||
"settable_per_mesh": true
|
||||
},
|
||||
"wall_try_line_thickness":
|
||||
{
|
||||
"label": "Try Multiple Line Thicknesses",
|
||||
"description": "When creating inner walls, try various line thicknesses to fit the wall lines better in narrow spaces. This reduces or increases the inner wall line width by up to 0.01mm.",
|
||||
"default_value": false,
|
||||
"type": "bool",
|
||||
"settable_per_mesh": true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
"visible": true,
|
||||
"author": "Ruben Dulek",
|
||||
"manufacturer": "Malyan",
|
||||
"machine_x3g_variant": "r1d",
|
||||
"file_formats": "application/x3g"
|
||||
},
|
||||
|
||||
|
|
|
@ -122,7 +122,7 @@
|
|||
"raft_jerk": { "value": "jerk_layer_0" },
|
||||
"raft_margin": { "value": "10" },
|
||||
"raft_surface_layers": { "value": "1" },
|
||||
"retraction_amount": { "value": "2" },
|
||||
"retraction_amount": { "value": "6.5" },
|
||||
"retraction_count_max": { "value": "10" },
|
||||
"retraction_extrusion_window": { "value": "1" },
|
||||
"retraction_hop": { "value": "2" },
|
||||
|
|
|
@ -119,7 +119,7 @@
|
|||
"raft_margin": { "value": "10" },
|
||||
"raft_speed": { "value": "25" },
|
||||
"raft_surface_layers": { "value": "1" },
|
||||
"retraction_amount": { "value": "2" },
|
||||
"retraction_amount": { "value": "6.5" },
|
||||
"retraction_count_max": { "value": "10" },
|
||||
"retraction_extrusion_window": { "value": "1" },
|
||||
"retraction_hop": { "value": "2" },
|
||||
|
|
|
@ -323,10 +323,11 @@ UM.MainWindow
|
|||
{
|
||||
if (drop.urls.length > 0)
|
||||
{
|
||||
// As the drop area also supports plugins, first check if it's a plugin that was dropped.
|
||||
if (drop.urls.length == 1)
|
||||
|
||||
var nonPackages = [];
|
||||
for (var i = 0; i < drop.urls.length; i++)
|
||||
{
|
||||
var filename = drop.urls[0];
|
||||
var filename = drop.urls[i];
|
||||
if (filename.endsWith(".curapackage"))
|
||||
{
|
||||
// Try to install plugin & close.
|
||||
|
@ -334,11 +335,13 @@ UM.MainWindow
|
|||
packageInstallDialog.text = catalog.i18nc("@label", "This package will be installed after restarting.");
|
||||
packageInstallDialog.icon = StandardIcon.Information;
|
||||
packageInstallDialog.open();
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
nonPackages.push(filename);
|
||||
}
|
||||
}
|
||||
|
||||
openDialog.handleOpenFileUrls(drop.urls);
|
||||
openDialog.handleOpenFileUrls(nonPackages);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -80,14 +80,11 @@ Item {
|
|||
property int unremovableSpacing: 5
|
||||
text: PrintInformation.jobName
|
||||
horizontalAlignment: TextInput.AlignRight
|
||||
onTextChanged: {
|
||||
PrintInformation.setJobName(text);
|
||||
}
|
||||
onEditingFinished: {
|
||||
if (printJobTextfield.text != ''){
|
||||
var new_name = text == "" ? "unnamed" : text;
|
||||
PrintInformation.setJobName(new_name, true);
|
||||
printJobTextfield.focus = false;
|
||||
}
|
||||
}
|
||||
validator: RegExpValidator {
|
||||
regExp: /^[^\\ \/ \*\?\|\[\]]*$/
|
||||
}
|
||||
|
|
|
@ -92,6 +92,7 @@ Rectangle
|
|||
anchors.verticalCenter: buildplateIcon.verticalCenter
|
||||
anchors.leftMargin: Math.round(UM.Theme.getSize("default_margin").height / 2)
|
||||
text: configuration.buildplateConfiguration
|
||||
renderType: Text.NativeRendering
|
||||
color: textColor
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@ Column
|
|||
{
|
||||
id: extruderLabel
|
||||
text: catalog.i18nc("@label:extruder label", "Extruder")
|
||||
renderType: Text.NativeRendering
|
||||
elide: Text.ElideRight
|
||||
anchors.left: parent.left
|
||||
font: UM.Theme.getFont("default")
|
||||
|
@ -59,6 +60,7 @@ Column
|
|||
id: extruderNumberText
|
||||
anchors.centerIn: parent
|
||||
text: printCoreConfiguration.position + 1
|
||||
renderType: Text.NativeRendering
|
||||
font: UM.Theme.getFont("default")
|
||||
color: mainColor
|
||||
}
|
||||
|
@ -69,6 +71,7 @@ Column
|
|||
{
|
||||
id: materialLabel
|
||||
text: printCoreConfiguration.material.name
|
||||
renderType: Text.NativeRendering
|
||||
elide: Text.ElideRight
|
||||
width: parent.width
|
||||
font: UM.Theme.getFont("default_bold")
|
||||
|
@ -79,6 +82,7 @@ Column
|
|||
{
|
||||
id: printCoreTypeLabel
|
||||
text: printCoreConfiguration.hotendID
|
||||
renderType: Text.NativeRendering
|
||||
elide: Text.ElideRight
|
||||
width: parent.width
|
||||
font: UM.Theme.getFont("default")
|
||||
|
|
|
@ -13,7 +13,7 @@ Button
|
|||
id: base
|
||||
property var outputDevice: null
|
||||
property var matched: updateOnSync()
|
||||
text: matched == true ? "Yes" : "No"
|
||||
text: matched == true ? catalog.i18nc("@label:extruder label", "Yes") : catalog.i18nc("@label:extruder label", "No")
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
|
||||
|
|
|
@ -63,8 +63,7 @@ Menu
|
|||
exclusiveGroup: group
|
||||
onTriggered:
|
||||
{
|
||||
var activeExtruderIndex = Cura.ExtruderManager.activeExtruderIndex;
|
||||
Cura.MachineManager.setMaterial(activeExtruderIndex, model.container_node);
|
||||
Cura.MachineManager.setMaterial(extruderIndex, model.container_node);
|
||||
}
|
||||
}
|
||||
onObjectAdded: brandMaterialsMenu.insertItem(index, object)
|
||||
|
|
|
@ -404,10 +404,17 @@ TabView
|
|||
id: spinBox
|
||||
anchors.left: label.right
|
||||
value: {
|
||||
// In case the setting is not in the material...
|
||||
if (!isNaN(parseFloat(materialPropertyProvider.properties.value)))
|
||||
{
|
||||
return parseFloat(materialPropertyProvider.properties.value);
|
||||
}
|
||||
// ... we search in the variant, and if it is not there...
|
||||
if (!isNaN(parseFloat(variantPropertyProvider.properties.value)))
|
||||
{
|
||||
return parseFloat(variantPropertyProvider.properties.value);
|
||||
}
|
||||
// ... then look in the definition container.
|
||||
if (!isNaN(parseFloat(machinePropertyProvider.properties.value)))
|
||||
{
|
||||
return parseFloat(machinePropertyProvider.properties.value);
|
||||
|
@ -431,6 +438,13 @@ TabView
|
|||
key: model.key
|
||||
}
|
||||
UM.ContainerPropertyProvider
|
||||
{
|
||||
id: variantPropertyProvider
|
||||
containerId: Cura.MachineManager.activeVariantId
|
||||
watchedProperties: [ "value" ]
|
||||
key: model.key
|
||||
}
|
||||
UM.ContainerPropertyProvider
|
||||
{
|
||||
id: machinePropertyProvider
|
||||
containerId: Cura.MachineManager.activeDefinitionId
|
||||
|
|
|
@ -93,14 +93,7 @@ SettingItem
|
|||
{
|
||||
target: control
|
||||
property: "currentIndex"
|
||||
value:
|
||||
{
|
||||
if(propertyProvider.properties.value == -1)
|
||||
{
|
||||
return control.getIndexByPosition(Cura.MachineManager.defaultExtruderPosition);
|
||||
}
|
||||
return propertyProvider.properties.value
|
||||
}
|
||||
value: control.getIndexByPosition(propertyProvider.properties.value)
|
||||
// Sometimes when the value is already changed, the model is still being built.
|
||||
// The when clause ensures that the current index is not updated when this happens.
|
||||
when: control.model.items.length > 0
|
||||
|
|
|
@ -57,7 +57,8 @@ Item
|
|||
interval: 50
|
||||
running: false
|
||||
repeat: false
|
||||
onTriggered: {
|
||||
onTriggered:
|
||||
{
|
||||
var item = Cura.QualityProfilesDropDownMenuModel.getItem(qualitySlider.value);
|
||||
Cura.MachineManager.activeQualityGroup = item.quality_group;
|
||||
}
|
||||
|
@ -77,7 +78,8 @@ Item
|
|||
{
|
||||
// update needs to be called when the widgets are visible, otherwise the step width calculation
|
||||
// will fail because the width of an invisible item is 0.
|
||||
if (visible) {
|
||||
if (visible)
|
||||
{
|
||||
qualityModel.update();
|
||||
}
|
||||
}
|
||||
|
@ -97,24 +99,30 @@ Item
|
|||
property var qualitySliderAvailableMax: 0
|
||||
property var qualitySliderMarginRight: 0
|
||||
|
||||
function update () {
|
||||
function update ()
|
||||
{
|
||||
reset()
|
||||
|
||||
var availableMin = -1
|
||||
var availableMax = -1
|
||||
|
||||
for (var i = 0; i < Cura.QualityProfilesDropDownMenuModel.rowCount(); i++) {
|
||||
for (var i = 0; i < Cura.QualityProfilesDropDownMenuModel.rowCount(); i++)
|
||||
{
|
||||
var qualityItem = Cura.QualityProfilesDropDownMenuModel.getItem(i)
|
||||
|
||||
// Add each quality item to the UI quality model
|
||||
qualityModel.append(qualityItem)
|
||||
|
||||
// Set selected value
|
||||
if (Cura.MachineManager.activeQualityType == qualityItem.quality_type) {
|
||||
if (Cura.MachineManager.activeQualityType == qualityItem.quality_type)
|
||||
{
|
||||
// set to -1 when switching to user created profile so all ticks are clickable
|
||||
if (Cura.SimpleModeSettingsManager.isProfileUserCreated) {
|
||||
if (Cura.SimpleModeSettingsManager.isProfileUserCreated)
|
||||
{
|
||||
qualityModel.qualitySliderActiveIndex = -1
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
qualityModel.qualitySliderActiveIndex = i
|
||||
}
|
||||
|
||||
|
@ -122,18 +130,21 @@ Item
|
|||
}
|
||||
|
||||
// Set min available
|
||||
if (qualityItem.available && availableMin == -1) {
|
||||
if (qualityItem.available && availableMin == -1)
|
||||
{
|
||||
availableMin = i
|
||||
}
|
||||
|
||||
// Set max available
|
||||
if (qualityItem.available) {
|
||||
if (qualityItem.available)
|
||||
{
|
||||
availableMax = i
|
||||
}
|
||||
}
|
||||
|
||||
// Set total available ticks for active slider part
|
||||
if (availableMin != -1) {
|
||||
if (availableMin != -1)
|
||||
{
|
||||
qualityModel.availableTotalTicks = availableMax - availableMin + 1
|
||||
}
|
||||
|
||||
|
@ -145,16 +156,23 @@ Item
|
|||
qualityModel.qualitySliderAvailableMax = availableMax
|
||||
}
|
||||
|
||||
function calculateSliderStepWidth (totalTicks) {
|
||||
function calculateSliderStepWidth (totalTicks)
|
||||
{
|
||||
qualityModel.qualitySliderStepWidth = totalTicks != 0 ? Math.round((base.width * 0.55) / (totalTicks)) : 0
|
||||
}
|
||||
|
||||
function calculateSliderMargins (availableMin, availableMax, totalTicks) {
|
||||
if (availableMin == -1 || (availableMin == 0 && availableMax == 0)) {
|
||||
function calculateSliderMargins (availableMin, availableMax, totalTicks)
|
||||
{
|
||||
if (availableMin == -1 || (availableMin == 0 && availableMax == 0))
|
||||
{
|
||||
qualityModel.qualitySliderMarginRight = Math.round(base.width * 0.55)
|
||||
} else if (availableMin == availableMax) {
|
||||
}
|
||||
else if (availableMin == availableMax)
|
||||
{
|
||||
qualityModel.qualitySliderMarginRight = Math.round((totalTicks - availableMin) * qualitySliderStepWidth)
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
qualityModel.qualitySliderMarginRight = Math.round((totalTicks - availableMax) * qualitySliderStepWidth)
|
||||
}
|
||||
}
|
||||
|
@ -215,16 +233,24 @@ Item
|
|||
return result
|
||||
}
|
||||
|
||||
x: {
|
||||
x:
|
||||
{
|
||||
// Make sure the text aligns correctly with each tick
|
||||
if (qualityModel.totalTicks == 0) {
|
||||
if (qualityModel.totalTicks == 0)
|
||||
{
|
||||
// If there is only one tick, align it centrally
|
||||
return Math.round(((base.width * 0.55) - width) / 2)
|
||||
} else if (index == 0) {
|
||||
}
|
||||
else if (index == 0)
|
||||
{
|
||||
return Math.round(base.width * 0.55 / qualityModel.totalTicks) * index
|
||||
} else if (index == qualityModel.totalTicks) {
|
||||
}
|
||||
else if (index == qualityModel.totalTicks)
|
||||
{
|
||||
return Math.round(base.width * 0.55 / qualityModel.totalTicks) * index - width
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
return Math.round((base.width * 0.55 / qualityModel.totalTicks) * index - (width / 2))
|
||||
}
|
||||
}
|
||||
|
@ -291,7 +317,8 @@ Item
|
|||
Rectangle
|
||||
{
|
||||
id: rightArea
|
||||
width: {
|
||||
width:
|
||||
{
|
||||
if(qualityModel.availableTotalTicks == 0)
|
||||
return 0
|
||||
|
||||
|
@ -299,8 +326,10 @@ Item
|
|||
}
|
||||
height: parent.height
|
||||
color: "transparent"
|
||||
x: {
|
||||
if (qualityModel.availableTotalTicks == 0) {
|
||||
x:
|
||||
{
|
||||
if (qualityModel.availableTotalTicks == 0)
|
||||
{
|
||||
return 0
|
||||
}
|
||||
|
||||
|
@ -310,7 +339,8 @@ Item
|
|||
return totalGap
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
MouseArea
|
||||
{
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
enabled: Cura.SimpleModeSettingsManager.isProfileUserCreated == false
|
||||
|
@ -373,13 +403,16 @@ Item
|
|||
style: SliderStyle
|
||||
{
|
||||
//Draw Available line
|
||||
groove: Rectangle {
|
||||
groove: Rectangle
|
||||
{
|
||||
implicitHeight: 2 * screenScaleFactor
|
||||
color: UM.Theme.getColor("quality_slider_available")
|
||||
radius: Math.round(height / 2)
|
||||
}
|
||||
handle: Item {
|
||||
Rectangle {
|
||||
handle: Item
|
||||
{
|
||||
Rectangle
|
||||
{
|
||||
id: qualityhandleButton
|
||||
anchors.centerIn: parent
|
||||
color: UM.Theme.getColor("quality_slider_available")
|
||||
|
@ -391,11 +424,14 @@ Item
|
|||
}
|
||||
}
|
||||
|
||||
onValueChanged: {
|
||||
onValueChanged:
|
||||
{
|
||||
// only change if an active machine is set and the slider is visible at all.
|
||||
if (Cura.MachineManager.activeMachine != null && visible) {
|
||||
if (Cura.MachineManager.activeMachine != null && visible)
|
||||
{
|
||||
// prevent updating during view initializing. Trigger only if the value changed by user
|
||||
if (qualitySlider.value != qualityModel.qualitySliderActiveIndex && qualityModel.qualitySliderActiveIndex != -1) {
|
||||
if (qualitySlider.value != qualityModel.qualitySliderActiveIndex && qualityModel.qualitySliderActiveIndex != -1)
|
||||
{
|
||||
// start updating with short delay
|
||||
qualitySliderChangeTimer.start()
|
||||
}
|
||||
|
@ -587,8 +623,10 @@ Item
|
|||
// same operation
|
||||
var active_mode = UM.Preferences.getValue("cura/active_mode")
|
||||
|
||||
if (active_mode == 0 || active_mode == "simple") {
|
||||
if (active_mode == 0 || active_mode == "simple")
|
||||
{
|
||||
Cura.MachineManager.setSettingForAllExtruders("infill_sparse_density", "value", roundedSliderValue)
|
||||
Cura.MachineManager.resetSettingForAllExtruders("infill_line_distance")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -150,7 +150,20 @@ UM.Dialog
|
|||
width: parent.width
|
||||
Label
|
||||
{
|
||||
text: catalog.i18nc("@action:label", "Extruder %1").arg(modelData)
|
||||
text: {
|
||||
var extruder = Number(modelData)
|
||||
var extruder_id = ""
|
||||
if(!isNaN(extruder))
|
||||
{
|
||||
extruder_id = extruder + 1 // The extruder counter start from One and not Zero
|
||||
}
|
||||
else
|
||||
{
|
||||
extruder_id = modelData
|
||||
}
|
||||
|
||||
return catalog.i18nc("@action:label", "Extruder %1").arg(extruder_id)
|
||||
}
|
||||
font.bold: true
|
||||
}
|
||||
Row
|
||||
|
|
|
@ -34,6 +34,7 @@ retraction_hop_enabled
|
|||
|
||||
[cooling]
|
||||
cool_fan_enabled
|
||||
cool_fan_speed
|
||||
|
||||
[support]
|
||||
support_enable
|
||||
|
|
|
@ -87,6 +87,7 @@ gradual_infill_steps
|
|||
gradual_infill_step_height
|
||||
infill_before_walls
|
||||
min_infill_area
|
||||
infill_support_enabled
|
||||
skin_preshrink
|
||||
top_skin_preshrink
|
||||
bottom_skin_preshrink
|
||||
|
@ -369,7 +370,6 @@ spaghetti_infill_extra_volume
|
|||
support_conical_enabled
|
||||
support_conical_angle
|
||||
support_conical_min_width
|
||||
infill_hollow
|
||||
magic_fuzzy_skin_enabled
|
||||
magic_fuzzy_skin_thickness
|
||||
magic_fuzzy_skin_point_density
|
||||
|
|
|
@ -532,7 +532,7 @@ QtObject {
|
|||
|
||||
SequentialAnimation on x {
|
||||
id: xAnim
|
||||
property int animEndPoint: Theme.getSize("message").width - (Theme.getSize("default_margin").width * 2) - Theme.getSize("progressbar_control").width
|
||||
property int animEndPoint: Theme.getSize("message").width - Math.round((Theme.getSize("default_margin").width * 2.5)) - Theme.getSize("progressbar_control").width
|
||||
running: control.indeterminate && control.visible
|
||||
loops: Animation.Infinite
|
||||
NumberAnimation { from: 0; to: xAnim.animEndPoint; duration: 2000;}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue