Merge remote-tracking branch 'refs/remotes/Ultimaker/master'

This commit is contained in:
Thomas Karl Pietrowski 2016-09-14 22:02:26 +02:00
commit 35e1062eb9
90 changed files with 12299 additions and 872 deletions

View File

@ -14,6 +14,7 @@ add_custom_command(TARGET tests POST_BUILD COMMAND "PYTHONPATH=${CMAKE_SOURCE_DI
set(CURA_VERSION "master" CACHE STRING "Version name of Cura") set(CURA_VERSION "master" CACHE STRING "Version name of Cura")
set(CURA_BUILDTYPE "" CACHE STRING "Build type of Cura, eg. 'PPA'") set(CURA_BUILDTYPE "" CACHE STRING "Build type of Cura, eg. 'PPA'")
configure_file(${CMAKE_SOURCE_DIR}/cura.desktop.in ${CMAKE_BINARY_DIR}/cura.desktop @ONLY)
configure_file(cura/CuraVersion.py.in CuraVersion.py @ONLY) configure_file(cura/CuraVersion.py.in CuraVersion.py @ONLY)
# Macro needed to list all sub-directory of a directory. # Macro needed to list all sub-directory of a directory.
@ -76,7 +77,7 @@ if(NOT APPLE AND NOT WIN32)
FILES_MATCHING PATTERN *.py) FILES_MATCHING PATTERN *.py)
install(FILES ${CMAKE_BINARY_DIR}/CuraVersion.py install(FILES ${CMAKE_BINARY_DIR}/CuraVersion.py
DESTINATION lib/python${PYTHON_VERSION_MAJOR}/dist-packages/cura) DESTINATION lib/python${PYTHON_VERSION_MAJOR}/dist-packages/cura)
install(FILES cura.desktop install(FILES ${CMAKE_BINARY_DIR}/cura.desktop
DESTINATION ${CMAKE_INSTALL_DATADIR}/applications) DESTINATION ${CMAKE_INSTALL_DATADIR}/applications)
install(FILES cura.sharedmimeinfo install(FILES cura.sharedmimeinfo
DESTINATION ${CMAKE_INSTALL_DATADIR}/mime/packages/ DESTINATION ${CMAKE_INSTALL_DATADIR}/mime/packages/

View File

@ -5,9 +5,9 @@ Name[de]=Cura
GenericName=3D Printing Software GenericName=3D Printing Software
GenericName[de]=3D-Druck-Software GenericName[de]=3D-Druck-Software
Comment=Cura converts 3D models into paths for a 3D printer. It prepares your print for maximum accuracy, minimum printing time and good reliability with many extra features that make your print come out great. Comment=Cura converts 3D models into paths for a 3D printer. It prepares your print for maximum accuracy, minimum printing time and good reliability with many extra features that make your print come out great.
Exec=/usr/bin/cura %F Exec=@CMAKE_INSTALL_FULL_BINDIR@/cura %F
TryExec=/usr/bin/cura TryExec=@CMAKE_INSTALL_FULL_BINDIR@/cura
Icon=/usr/share/cura/resources/images/cura-icon.png Icon=@CMAKE_INSTALL_FULL_DATADIR@/cura/resources/images/cura-icon.png
Terminal=false Terminal=false
Type=Application Type=Application
MimeType=application/sla;application/vnd.ms-3mfdocument;application/prs.wavefront-obj;image/bmp;image/gif;image/jpeg;image/png MimeType=application/sla;application/vnd.ms-3mfdocument;application/prs.wavefront-obj;image/bmp;image/gif;image/jpeg;image/png

View File

@ -208,7 +208,7 @@ class BuildVolume(SceneNode):
"@info:status", "@info:status",
"The build volume height has been reduced due to the value of the" "The build volume height has been reduced due to the value of the"
" \"Print Sequence\" setting to prevent the gantry from colliding" " \"Print Sequence\" setting to prevent the gantry from colliding"
" with printed objects."), lifetime=10).show() " with printed models."), lifetime=10).show()
def getRaftThickness(self): def getRaftThickness(self):
return self._raft_thickness return self._raft_thickness
@ -265,7 +265,7 @@ class BuildVolume(SceneNode):
self._height = self._active_container_stack.getProperty("machine_height", "value") self._height = self._active_container_stack.getProperty("machine_height", "value")
rebuild_me = True rebuild_me = True
if setting_key in self._skirt_settings: if setting_key in self._skirt_settings or setting_key in self._prime_settings:
self._updateDisallowedAreas() self._updateDisallowedAreas()
rebuild_me = True rebuild_me = True
@ -377,3 +377,4 @@ class BuildVolume(SceneNode):
_skirt_settings = ["adhesion_type", "skirt_gap", "skirt_line_count", "skirt_brim_line_width", "brim_width", "brim_line_count", "raft_margin", "draft_shield_enabled", "draft_shield_dist", "xy_offset"] _skirt_settings = ["adhesion_type", "skirt_gap", "skirt_line_count", "skirt_brim_line_width", "brim_width", "brim_line_count", "raft_margin", "draft_shield_enabled", "draft_shield_dist", "xy_offset"]
_raft_settings = ["adhesion_type", "raft_base_thickness", "raft_interface_thickness", "raft_surface_layers", "raft_surface_thickness", "raft_airgap"] _raft_settings = ["adhesion_type", "raft_base_thickness", "raft_interface_thickness", "raft_surface_layers", "raft_surface_thickness", "raft_airgap"]
_prime_settings = ["extruder_prime_pos_x", "extruder_prime_pos_y", "extruder_prime_pos_z"]

View File

@ -30,9 +30,10 @@ class ConvexHullDecorator(SceneNodeDecorator):
def setNode(self, node): def setNode(self, node):
previous_node = self._node previous_node = self._node
# Disconnect from previous node signals
if previous_node is not None and node is not previous_node: if previous_node is not None and node is not previous_node:
previous_node.transformationChanged.connect(self._onChanged) previous_node.transformationChanged.disconnect(self._onChanged)
previous_node.parentChanged.connect(self._onChanged) previous_node.parentChanged.disconnect(self._onChanged)
super().setNode(node) super().setNode(node)
@ -286,5 +287,5 @@ class ConvexHullDecorator(SceneNodeDecorator):
_affected_settings = [ _affected_settings = [
"adhesion_type", "raft_base_thickness", "raft_interface_thickness", "raft_surface_layers", "adhesion_type", "raft_base_thickness", "raft_interface_thickness", "raft_surface_layers",
"raft_surface_thickness", "raft_airgap", "print_sequence", "raft_surface_thickness", "raft_airgap", "raft_margin", "print_sequence",
"skirt_gap", "skirt_line_count", "skirt_brim_line_width", "skirt_distance"] "skirt_gap", "skirt_line_count", "skirt_brim_line_width", "skirt_distance"]

View File

@ -23,7 +23,7 @@ class ConvexHullNode(SceneNode):
self._original_parent = parent self._original_parent = parent
# Color of the drawn convex hull # Color of the drawn convex hull
self._color = Color(35, 35, 35, 192) self._color = Color(0.4, 0.4, 0.4, 1.0)
# The y-coordinate of the convex hull mesh. Must not be 0, to prevent z-fighting. # The y-coordinate of the convex hull mesh. Must not be 0, to prevent z-fighting.
self._mesh_height = 0.1 self._mesh_height = 0.1

View File

@ -28,6 +28,7 @@ from cura.SetParentOperation import SetParentOperation
from UM.Settings.SettingDefinition import SettingDefinition, DefinitionPropertyType from UM.Settings.SettingDefinition import SettingDefinition, DefinitionPropertyType
from UM.Settings.ContainerRegistry import ContainerRegistry from UM.Settings.ContainerRegistry import ContainerRegistry
from UM.Settings.SettingFunction import SettingFunction
from UM.i18n import i18nCatalog from UM.i18n import i18nCatalog
@ -57,21 +58,12 @@ import copy
import urllib import urllib
numpy.seterr(all="ignore") numpy.seterr(all="ignore")
#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
if platform.linux_distribution()[0] in ("Ubuntu", ): # TODO: Needs a "if X11_GFX == 'nvidia'" here. The workaround is only needed on Ubuntu+NVidia drivers. Other drivers are not affected, but fine with this fix.
import ctypes
from ctypes.util import find_library
ctypes.CDLL(find_library('GL'), ctypes.RTLD_GLOBAL)
try: try:
from cura.CuraVersion import CuraVersion, CuraBuildType from cura.CuraVersion import CuraVersion, CuraBuildType
except ImportError: except ImportError:
CuraVersion = "master" # [CodeStyle: Reflecting imported value] CuraVersion = "master" # [CodeStyle: Reflecting imported value]
CuraBuildType = "" CuraBuildType = ""
class CuraApplication(QtApplication): class CuraApplication(QtApplication):
class ResourceTypes: class ResourceTypes:
QmlFiles = Resources.UserType + 1 QmlFiles = Resources.UserType + 1
@ -98,7 +90,10 @@ class CuraApplication(QtApplication):
SettingDefinition.addSupportedProperty("settable_per_meshgroup", DefinitionPropertyType.Any, default = True) SettingDefinition.addSupportedProperty("settable_per_meshgroup", DefinitionPropertyType.Any, default = True)
SettingDefinition.addSupportedProperty("settable_globally", DefinitionPropertyType.Any, default = True) SettingDefinition.addSupportedProperty("settable_globally", DefinitionPropertyType.Any, default = True)
SettingDefinition.addSupportedProperty("global_inherits_stack", DefinitionPropertyType.Function, default = "-1") SettingDefinition.addSupportedProperty("global_inherits_stack", DefinitionPropertyType.Function, default = "-1")
SettingDefinition.addSettingType("extruder", int, str, Validator) SettingDefinition.addSettingType("extruder", None, str, Validator)
SettingFunction.registerOperator("extruderValues", cura.Settings.ExtruderManager.getExtruderValues)
SettingFunction.registerOperator("extruderValue", cura.Settings.ExtruderManager.getExtruderValue)
## Add the 4 types of profiles to storage. ## Add the 4 types of profiles to storage.
Resources.addStorageType(self.ResourceTypes.QualityInstanceContainer, "quality") Resources.addStorageType(self.ResourceTypes.QualityInstanceContainer, "quality")
@ -128,6 +123,8 @@ class CuraApplication(QtApplication):
self._machine_action_manager = MachineActionManager.MachineActionManager() self._machine_action_manager = MachineActionManager.MachineActionManager()
self._machine_manager = None # This is initialized on demand. self._machine_manager = None # This is initialized on demand.
self._additional_components = {} # Components to add to certain areas in the interface
super().__init__(name = "cura", version = CuraVersion, buildtype = CuraBuildType) super().__init__(name = "cura", version = CuraVersion, buildtype = CuraBuildType)
self.setWindowIcon(QIcon(Resources.getPath(Resources.Images, "cura-icon.png"))) self.setWindowIcon(QIcon(Resources.getPath(Resources.Images, "cura-icon.png")))
@ -201,7 +198,7 @@ class CuraApplication(QtApplication):
"dialog_profile_path", "dialog_profile_path",
"dialog_material_path"]: "dialog_material_path"]:
Preferences.getInstance().addPreference("local_file/%s" % key, "~/") Preferences.getInstance().addPreference("local_file/%s" % key, os.path.expanduser("~/"))
Preferences.getInstance().setDefault("local_file/last_used_type", "text/x-gcode") Preferences.getInstance().setDefault("local_file/last_used_type", "text/x-gcode")
@ -233,13 +230,20 @@ class CuraApplication(QtApplication):
support support
support_enable support_enable
support_type support_type
support_roof_density support_interface_density
platform_adhesion platform_adhesion
adhesion_type adhesion_type
brim_width brim_width
raft_airgap raft_airgap
layer_0_z_overlap layer_0_z_overlap
raft_surface_layers raft_surface_layers
dual
adhesion_extruder_nr
support_extruder_nr
prime_tower_enable
prime_tower_size
prime_tower_position_x
prime_tower_position_y
meshfix meshfix
blackmagic blackmagic
print_sequence print_sequence
@ -553,12 +557,18 @@ class CuraApplication(QtApplication):
def deleteSelection(self): def deleteSelection(self):
if not self.getController().getToolsEnabled(): if not self.getController().getToolsEnabled():
return return
removed_group_nodes = []
op = GroupedOperation() op = GroupedOperation()
nodes = Selection.getAllSelectedObjects() nodes = Selection.getAllSelectedObjects()
for node in nodes: for node in nodes:
op.addOperation(RemoveSceneNodeOperation(node)) op.addOperation(RemoveSceneNodeOperation(node))
group_node = node.getParent()
if group_node and group_node.callDecoration("isGroup") and group_node not in removed_group_nodes:
remaining_nodes_in_group = list(set(group_node.getChildren()) - set(nodes))
if len(remaining_nodes_in_group) == 1:
removed_group_nodes.append(group_node)
op.addOperation(SetParentOperation(remaining_nodes_in_group[0], group_node.getParent()))
op.addOperation(RemoveSceneNodeOperation(group_node))
op.push() op.push()
pass pass
@ -584,8 +594,7 @@ class CuraApplication(QtApplication):
op.push() op.push()
if group_node: if group_node:
if len(group_node.getChildren()) == 1 and group_node.callDecoration("isGroup"): if len(group_node.getChildren()) == 1 and group_node.callDecoration("isGroup"):
group_node.getChildren()[0].translate(group_node.getPosition()) op.addOperation(SetParentOperation(group_node.getChildren()[0], group_node.getParent()))
group_node.getChildren()[0].setParent(group_node.getParent())
op = RemoveSceneNodeOperation(group_node) op = RemoveSceneNodeOperation(group_node)
op.push() op.push()
@ -627,6 +636,22 @@ class CuraApplication(QtApplication):
op = SetTransformOperation(node, Vector()) op = SetTransformOperation(node, Vector())
op.push() op.push()
## Select all nodes containing mesh data in the scene.
@pyqtSlot()
def selectAll(self):
if not self.getController().getToolsEnabled():
return
Selection.clear()
for node in DepthFirstIterator(self.getController().getScene().getRoot()):
if type(node) is not SceneNode:
continue
if not node.getMeshData() and not node.callDecoration("isGroup"):
continue # Node that doesnt have a mesh and is not a group.
if node.getParent() and node.getParent().callDecoration("isGroup"):
continue # Grouped nodes don't need resetting as their parent (the group) is resetted)
Selection.add(node)
## Delete all nodes containing mesh data in the scene. ## Delete all nodes containing mesh data in the scene.
@pyqtSlot() @pyqtSlot()
def deleteAll(self): def deleteAll(self):
@ -650,6 +675,7 @@ class CuraApplication(QtApplication):
op.addOperation(RemoveSceneNodeOperation(node)) op.addOperation(RemoveSceneNodeOperation(node))
op.push() op.push()
Selection.clear()
## Reset all translation on nodes with mesh data. ## Reset all translation on nodes with mesh data.
@pyqtSlot() @pyqtSlot()
@ -877,3 +903,21 @@ class CuraApplication(QtApplication):
def getBuildVolume(self): def getBuildVolume(self):
return self._volume return self._volume
additionalComponentsChanged = pyqtSignal(str, arguments = ["areaId"])
@pyqtProperty("QVariantMap", notify = additionalComponentsChanged)
def additionalComponents(self):
return self._additional_components
## Add a component to a list of components to be reparented to another area in the GUI.
# The actual reparenting is done by the area itself.
# \param area_id \type{str} Identifying name of the area to which the component should be reparented
# \param component \type{QQuickComponent} The component that should be reparented
@pyqtSlot(str, "QVariant")
def addAdditionalComponent(self, area_id, component):
if area_id not in self._additional_components:
self._additional_components[area_id] = []
self._additional_components[area_id].append(component)
self.additionalComponentsChanged.emit(area_id)

View File

@ -44,6 +44,7 @@ class MachineAction(QObject, PluginObject):
# /sa _reset # /sa _reset
@pyqtSlot() @pyqtSlot()
def reset(self): def reset(self):
self._component = None
self._finished = False self._finished = False
self._reset() self._reset()

View File

@ -57,9 +57,10 @@ class MachineActionManager(QObject):
def addRequiredAction(self, definition_id, action_key): def addRequiredAction(self, definition_id, action_key):
if action_key in self._machine_actions: if action_key in self._machine_actions:
if definition_id in self._required_actions: if definition_id in self._required_actions:
self._required_actions[definition_id] |= {self._machine_actions[action_key]} if self._machine_actions[action_key] not in self._required_actions[definition_id]:
self._required_actions[definition_id].append(self._machine_actions[action_key])
else: else:
self._required_actions[definition_id] = {self._machine_actions[action_key]} self._required_actions[definition_id] = [self._machine_actions[action_key]]
else: else:
raise UnknownMachineActionError("Action %s, which is required for %s is not known." % (action_key, definition_id)) raise UnknownMachineActionError("Action %s, which is required for %s is not known." % (action_key, definition_id))
@ -67,9 +68,10 @@ class MachineActionManager(QObject):
def addSupportedAction(self, definition_id, action_key): def addSupportedAction(self, definition_id, action_key):
if action_key in self._machine_actions: if action_key in self._machine_actions:
if definition_id in self._supported_actions: if definition_id in self._supported_actions:
self._supported_actions[definition_id] |= {self._machine_actions[action_key]} if self._machine_actions[action_key] not in self._supported_actions[definition_id]:
self._supported_actions[definition_id].append(self._machine_actions[action_key])
else: else:
self._supported_actions[definition_id] = {self._machine_actions[action_key]} self._supported_actions[definition_id] = [self._machine_actions[action_key]]
else: else:
Logger.log("w", "Unable to add %s to %s, as the action is not recognised", action_key, definition_id) Logger.log("w", "Unable to add %s to %s, as the action is not recognised", action_key, definition_id)

View File

@ -71,7 +71,6 @@ class PlatformPhysics:
# If there is no convex hull for the node, start calculating it and continue. # If there is no convex hull for the node, start calculating it and continue.
if not node.getDecorator(ConvexHullDecorator): if not node.getDecorator(ConvexHullDecorator):
node.addDecorator(ConvexHullDecorator()) node.addDecorator(ConvexHullDecorator())
node.callDecoration("recomputeConvexHull")
if Preferences.getInstance().getValue("physics/automatic_push_free"): if Preferences.getInstance().getValue("physics/automatic_push_free"):
# Check for collisions between convex hulls # Check for collisions between convex hulls
@ -95,11 +94,11 @@ class PlatformPhysics:
# Get the overlap distance for both convex hulls. If this returns None, there is no intersection. # Get the overlap distance for both convex hulls. If this returns None, there is no intersection.
head_hull = node.callDecoration("getConvexHullHead") head_hull = node.callDecoration("getConvexHullHead")
if head_hull: if head_hull:
overlap = head_hull.intersectsPolygon(other_node.callDecoration("getConvexHull")) overlap = head_hull.intersectsPolygon(other_node.callDecoration("getConvexHullHead"))
if not overlap: if not overlap:
other_head_hull = other_node.callDecoration("getConvexHullHead") other_head_hull = other_node.callDecoration("getConvexHullHead")
if other_head_hull: if other_head_hull:
overlap = node.callDecoration("getConvexHull").intersectsPolygon(other_head_hull) overlap = node.callDecoration("getConvexHullHead").intersectsPolygon(other_head_hull)
else: else:
own_convex_hull = node.callDecoration("getConvexHull") own_convex_hull = node.callDecoration("getConvexHull")
other_convex_hull = other_node.callDecoration("getConvexHull") other_convex_hull = other_node.callDecoration("getConvexHull")

View File

@ -31,10 +31,13 @@ class PrinterOutputDevice(QObject, OutputDevice):
self._head_y = 0 self._head_y = 0
self._head_z = 0 self._head_z = 0
self._connection_state = ConnectionState.closed self._connection_state = ConnectionState.closed
self._connection_text = ""
self._time_elapsed = 0 self._time_elapsed = 0
self._time_total = 0 self._time_total = 0
self._job_state = "" self._job_state = ""
self._job_name = "" self._job_name = ""
self._error_text = ""
self._accepts_commands = True
def requestWrite(self, node, file_name = None, filter_by_machine = False): def requestWrite(self, node, file_name = None, filter_by_machine = False):
raise NotImplementedError("requestWrite needs to be implemented") raise NotImplementedError("requestWrite needs to be implemented")
@ -69,6 +72,8 @@ class PrinterOutputDevice(QObject, OutputDevice):
# it also sends it's own device_id (for convenience sake) # it also sends it's own device_id (for convenience sake)
connectionStateChanged = pyqtSignal(str) connectionStateChanged = pyqtSignal(str)
connectionTextChanged = pyqtSignal()
timeElapsedChanged = pyqtSignal() timeElapsedChanged = pyqtSignal()
timeTotalChanged = pyqtSignal() timeTotalChanged = pyqtSignal()
@ -77,6 +82,10 @@ class PrinterOutputDevice(QObject, OutputDevice):
jobNameChanged = pyqtSignal() jobNameChanged = pyqtSignal()
errorTextChanged = pyqtSignal()
acceptsCommandsChanged = pyqtSignal()
@pyqtProperty(str, notify = jobStateChanged) @pyqtProperty(str, notify = jobStateChanged)
def jobState(self): def jobState(self):
return self._job_state return self._job_state
@ -102,6 +111,26 @@ class PrinterOutputDevice(QObject, OutputDevice):
self._job_name = name self._job_name = name
self.jobNameChanged.emit() self.jobNameChanged.emit()
@pyqtProperty(str, notify = errorTextChanged)
def errorText(self):
return self._error_text
## Set the error-text that is shown in the print monitor in case of an error
def setErrorText(self, error_text):
if self._error_text != error_text:
self._error_text = error_text
self.errorTextChanged.emit()
@pyqtProperty(bool, notify = acceptsCommandsChanged)
def acceptsCommands(self):
return self._accepts_commands
## Set a flag to signal the UI that the printer is not (yet) ready to receive commands
def setAcceptsCommands(self, accepts_commands):
if self._accepts_commands != accepts_commands:
self._accepts_commands = accepts_commands
self.acceptsCommandsChanged.emit()
## Get the bed temperature of the bed (if any) ## Get the bed temperature of the bed (if any)
# This function is "final" (do not re-implement) # This function is "final" (do not re-implement)
# /sa _getBedTemperature implementation function # /sa _getBedTemperature implementation function
@ -266,6 +295,16 @@ class PrinterOutputDevice(QObject, OutputDevice):
self._connection_state = connection_state self._connection_state = connection_state
self.connectionStateChanged.emit(self._id) self.connectionStateChanged.emit(self._id)
@pyqtProperty(str, notify = connectionTextChanged)
def connectionText(self):
return self._connection_text
## Set a text that is shown on top of the print monitor tab
def setConnectionText(self, connection_text):
if self._connection_text != connection_text:
self._connection_text = connection_text
self.connectionTextChanged.emit()
## Ensure that close gets called when object is destroyed ## Ensure that close gets called when object is destroyed
def __del__(self): def __del__(self):
self.close() self.close()

View File

@ -244,6 +244,7 @@ class ContainerManager(QObject):
if not type_name or entry["type"] == type_name: if not type_name or entry["type"] == type_name:
filters.append(filter_string) filters.append(filter_string)
filters.append("All Files (*)")
return filters return filters
## Export a container to a file ## Export a container to a file
@ -280,6 +281,9 @@ class ContainerManager(QObject):
return { "status": "error", "message": "Container not found"} return { "status": "error", "message": "Container not found"}
container = containers[0] container = containers[0]
if UM.Platform.isOSX() and "." in file_url:
file_url = file_url[:file_url.rfind(".")]
for suffix in mime_type.suffixes: for suffix in mime_type.suffixes:
if file_url.endswith(suffix): if file_url.endswith(suffix):
break break
@ -301,7 +305,7 @@ class ContainerManager(QObject):
with UM.SaveFile(file_url, "w") as f: with UM.SaveFile(file_url, "w") as f:
f.write(contents) f.write(contents)
return { "status": "success", "message": "Succesfully exported container"} return { "status": "success", "message": "Succesfully exported container", "path": file_url}
## Imports a profile from a file ## Imports a profile from a file
# #
@ -371,11 +375,20 @@ class ContainerManager(QObject):
"container": container_type "container": container_type
} }
suffix_list = "*." + mime_type.preferredSuffix suffix = mime_type.preferredSuffix
if UM.Platform.isOSX() and "." in suffix:
# OSX's File dialog is stupid and does not allow selecting files with a . in its name
suffix = suffix[suffix.index(".") + 1:]
suffix_list = "*." + suffix
for suffix in mime_type.suffixes: for suffix in mime_type.suffixes:
if suffix == mime_type.preferredSuffix: if suffix == mime_type.preferredSuffix:
continue continue
if UM.Platform.isOSX() and "." in suffix:
# OSX's File dialog is stupid and does not allow selecting files with a . in its name
suffix = suffix[suffix.index("."):]
suffix_list += ", *." + suffix suffix_list += ", *." + suffix
name_filter = "{0} ({1})".format(mime_type.comment, suffix_list) name_filter = "{0} ({1})".format(mime_type.comment, suffix_list)

View File

@ -41,7 +41,6 @@ class ContainerSettingsModel(ListModel):
keys = keys + list(container.getAllKeys()) keys = keys + list(container.getAllKeys())
keys = list(set(keys)) # remove duplicate keys keys = list(set(keys)) # remove duplicate keys
keys.sort()
for key in keys: for key in keys:
definition = None definition = None
@ -72,6 +71,7 @@ class ContainerSettingsModel(ListModel):
"unit": definition.unit, "unit": definition.unit,
"category": category.label "category": category.label
}) })
self.sort(lambda k: (k["category"], k["key"]))
## Set the ids of the containers which have the settings this model should list. ## Set the ids of the containers which have the settings this model should list.
# Also makes sure the model updates when the containers have property changes # Also makes sure the model updates when the containers have property changes

View File

@ -6,6 +6,7 @@ from PyQt5.QtCore import pyqtSignal, pyqtProperty, pyqtSlot, QObject, QVariant #
import UM.Application #To get the global container stack to find the current machine. import UM.Application #To get the global container stack to find the current machine.
import UM.Logger import UM.Logger
import UM.Settings.ContainerRegistry #Finding containers by ID. import UM.Settings.ContainerRegistry #Finding containers by ID.
import UM.Settings.SettingFunction
## Manages all existing extruder stacks. ## Manages all existing extruder stacks.
@ -92,6 +93,15 @@ class ExtruderManager(QObject):
return self._extruder_trains[global_container_stack.getId()][str(self._active_extruder_index)] return self._extruder_trains[global_container_stack.getId()][str(self._active_extruder_index)]
return None return None
## Get an extruder stack by index
def getExtruderStack(self, index):
global_container_stack = UM.Application.getInstance().getGlobalContainerStack()
if global_container_stack:
if global_container_stack.getId() in self._extruder_trains:
if str(index) in self._extruder_trains[global_container_stack.getId()]:
return self._extruder_trains[global_container_stack.getId()][str(index)]
return None
## Adds all extruders of a specific machine definition to the extruder ## Adds all extruders of a specific machine definition to the extruder
# manager. # manager.
# #
@ -273,3 +283,54 @@ class ExtruderManager(QObject):
global_stack = UM.Application.getInstance().getGlobalContainerStack() global_stack = UM.Application.getInstance().getGlobalContainerStack()
if global_stack and global_stack.getBottom(): if global_stack and global_stack.getBottom():
self.addMachineExtruders(global_stack.getBottom(), global_stack.getId()) self.addMachineExtruders(global_stack.getBottom(), global_stack.getId())
## Get all extruder values for a certain setting.
#
# This is exposed to SettingFunction so it can be used in value functions.
#
# \param key The key of the setting to retieve values for.
#
# \return A list of values for all extruders. If an extruder does not have a value, it will not be in the list.
# If no extruder has the value, the list will contain the global value.
@staticmethod
def getExtruderValues(key):
global_stack = UM.Application.getInstance().getGlobalContainerStack()
result = []
for extruder in ExtruderManager.getInstance().getMachineExtruders(global_stack.getId()):
value = extruder.getRawProperty(key, "value")
if not value:
continue
if isinstance(value, UM.Settings.SettingFunction):
value = value(extruder)
result.append(value)
if not result:
result.append(global_stack.getProperty(key, "value"))
return result
## Get the value for a setting from a specific extruder.
#
# This is exposed to SettingFunction to use in value functions.
#
# \param extruder_index The index of the extruder to get the value from.
# \param key The key of the setting to get the value of.
#
# \return The value of the setting for the specified extruder or for the
# global stack if not found.
@staticmethod
def getExtruderValue(extruder_index, key):
extruder = ExtruderManager.getInstance().getExtruderStack(extruder_index)
if extruder:
value = extruder.getRawProperty(key, "value")
if isinstance(value, UM.Settings.SettingFunction):
value = value(extruder)
else: #Just a value from global.
value = UM.Application.getInstance().getGlobalContainerStack().getProperty(key, "value")
return value

View File

@ -20,7 +20,7 @@ class ExtrudersModel(UM.Qt.ListModel.ListModel):
NameRole = Qt.UserRole + 2 NameRole = Qt.UserRole + 2
## Colour of the material loaded in the extruder. ## Colour of the material loaded in the extruder.
ColourRole = Qt.UserRole + 3 ColorRole = Qt.UserRole + 3
## Index of the extruder, which is also the value of the setting itself. ## Index of the extruder, which is also the value of the setting itself.
# #
@ -31,7 +31,7 @@ class ExtrudersModel(UM.Qt.ListModel.ListModel):
## List of colours to display if there is no material or the material has no known ## List of colours to display if there is no material or the material has no known
# colour. # colour.
defaultColours = ["#ffc924", "#86ec21", "#22eeee", "#245bff", "#9124ff", "#ff24c8"] defaultColors = ["#ffc924", "#86ec21", "#22eeee", "#245bff", "#9124ff", "#ff24c8"]
## Initialises the extruders model, defining the roles and listening for ## Initialises the extruders model, defining the roles and listening for
# changes in the data. # changes in the data.
@ -42,7 +42,7 @@ class ExtrudersModel(UM.Qt.ListModel.ListModel):
self.addRoleName(self.IdRole, "id") self.addRoleName(self.IdRole, "id")
self.addRoleName(self.NameRole, "name") self.addRoleName(self.NameRole, "name")
self.addRoleName(self.ColourRole, "colour") self.addRoleName(self.ColorRole, "color")
self.addRoleName(self.IndexRole, "index") self.addRoleName(self.IndexRole, "index")
self._add_global = False self._add_global = False
@ -104,11 +104,11 @@ class ExtrudersModel(UM.Qt.ListModel.ListModel):
if global_container_stack: if global_container_stack:
if self._add_global: if self._add_global:
material = global_container_stack.findContainer({ "type": "material" }) material = global_container_stack.findContainer({ "type": "material" })
colour = material.getMetaDataEntry("color_code", default = self.defaultColours[0]) if material else self.defaultColours[0] color = material.getMetaDataEntry("color_code", default = self.defaultColors[0]) if material else self.defaultColors[0]
item = { item = {
"id": global_container_stack.getId(), "id": global_container_stack.getId(),
"name": "Global", "name": "Global",
"colour": colour, "color": color,
"index": -1 "index": -1
} }
self.appendItem(item) self.appendItem(item)
@ -125,12 +125,12 @@ class ExtrudersModel(UM.Qt.ListModel.ListModel):
position = int(position) position = int(position)
except ValueError: #Not a proper int. except ValueError: #Not a proper int.
position = -1 position = -1
default_colour = self.defaultColours[position] if position >= 0 and position < len(self.defaultColours) else self.defaultColours[0] default_color = self.defaultColors[position] if position >= 0 and position < len(self.defaultColors) else self.defaultColors[0]
colour = material.getMetaDataEntry("color_code", default = default_colour) if material else default_colour color = material.getMetaDataEntry("color_code", default = default_color) if material else default_color
item = { #Construct an item with only the relevant information. item = { #Construct an item with only the relevant information.
"id": extruder.getId(), "id": extruder.getId(),
"name": extruder_name, "name": extruder_name,
"colour": colour, "color": color,
"index": position "index": position
} }
self.appendItem(item) self.appendItem(item)

View File

@ -26,7 +26,7 @@ class MachineManager(QObject):
self._global_container_stack = None self._global_container_stack = None
Application.getInstance().globalContainerStackChanged.connect(self._onGlobalContainerChanged) Application.getInstance().globalContainerStackChanged.connect(self._onGlobalContainerChanged)
self._global_stack_valid = None self._active_stack_valid = None
self._onGlobalContainerChanged() self._onGlobalContainerChanged()
ExtruderManager.getInstance().activeExtruderChanged.connect(self._onActiveExtruderStackChanged) ExtruderManager.getInstance().activeExtruderChanged.connect(self._onActiveExtruderStackChanged)
@ -74,7 +74,7 @@ class MachineManager(QObject):
activeStackChanged = pyqtSignal() activeStackChanged = pyqtSignal()
globalValueChanged = pyqtSignal() # Emitted whenever a value inside global container is changed. globalValueChanged = pyqtSignal() # Emitted whenever a value inside global container is changed.
globalValidationChanged = pyqtSignal() # Emitted whenever a validation inside global container is changed activeValidationChanged = pyqtSignal() # Emitted whenever a validation inside active container is changed
blurSettings = pyqtSignal() # Emitted to force fields in the advanced sidebar to un-focus, so they update properly blurSettings = pyqtSignal() # Emitted to force fields in the advanced sidebar to un-focus, so they update properly
@ -165,7 +165,7 @@ class MachineManager(QObject):
# Save the material that needs to be changed. Multiple changes will be handled by the callback. # Save the material that needs to be changed. Multiple changes will be handled by the callback.
self._auto_materials_changed[str(index)] = containers[0].getId() self._auto_materials_changed[str(index)] = containers[0].getId()
Application.getInstance().messageBox(catalog.i18nc("@window:title", "Changes on the Printer"), catalog.i18nc("@label", "Do you want to change the materials and hotends to match the material in your printer?"), Application.getInstance().messageBox(catalog.i18nc("@window:title", "Changes on the Printer"), catalog.i18nc("@label", "Do you want to change the materials and hotends to match the material in your printer?"),
catalog.i18nc("@label", "The materials and / or hotends on your printer were changed. For best results always slice for the materials . hotends that are inserted in your printer."), catalog.i18nc("@label", "The materials and / or hotends on your printer were changed. For best results always slice for the materials and hotends that are inserted in your printer."),
buttons = QMessageBox.Yes + QMessageBox.No, icon = QMessageBox.Question, callback = self._materialHotendChangedCallback) buttons = QMessageBox.Yes + QMessageBox.No, icon = QMessageBox.Question, callback = self._materialHotendChangedCallback)
else: else:
@ -269,25 +269,29 @@ class MachineManager(QObject):
if property_name == "global_inherits_stack": if property_name == "global_inherits_stack":
if self._active_container_stack and self._active_container_stack != self._global_container_stack: if self._active_container_stack and self._active_container_stack != self._global_container_stack:
# Update the global user value when the "global_inherits_stack" function points to a different stack # Update the global user value when the "global_inherits_stack" function points to a different stack
stack_index = int(self._active_container_stack.getProperty(key, property_name)) extruder_stacks = list(ExtruderManager.getInstance().getMachineExtruders(self._global_container_stack.getId()))
extruder_stacks = [stack for stack in ExtruderManager.getInstance().getMachineExtruders(self._global_container_stack.getId())] target_stack_position = int(self._active_container_stack.getProperty(key, "global_inherits_stack"))
if target_stack_position == -1: # Prevent -1 from selecting wrong stack.
target_stack = self._active_container_stack
else:
target_stack = extruder_stacks[target_stack_position]
if len(extruder_stacks) > stack_index: new_value = target_stack.getProperty(key, "value")
new_value = extruder_stacks[stack_index].getProperty(key, "value")
if self._global_container_stack.getProperty(key, "value") != new_value: if self._global_container_stack.getProperty(key, "value") != new_value:
self._global_container_stack.getTop().setProperty(key, "value", new_value) self._global_container_stack.getTop().setProperty(key, "value", new_value)
if property_name == "validationState": if property_name == "validationState":
if self._global_stack_valid: if self._active_stack_valid:
changed_validation_state = self._active_container_stack.getProperty(key, property_name) changed_validation_state = self._active_container_stack.getProperty(key, property_name)
if changed_validation_state in (UM.Settings.ValidatorState.Exception, UM.Settings.ValidatorState.MaximumError, UM.Settings.ValidatorState.MinimumError): if changed_validation_state in (UM.Settings.ValidatorState.Exception, UM.Settings.ValidatorState.MaximumError, UM.Settings.ValidatorState.MinimumError):
self._global_stack_valid = False self._active_stack_valid = False
self.globalValidationChanged.emit() self.activeValidationChanged.emit()
else: else:
has_errors = self._checkStackForErrors(self._active_container_stack) has_errors = self._checkStackForErrors(self._active_container_stack)
if not has_errors: if not has_errors:
self._global_stack_valid = True self._active_stack_valid = True
self.globalValidationChanged.emit() self.activeValidationChanged.emit()
def _onGlobalContainerChanged(self): def _onGlobalContainerChanged(self):
if self._global_container_stack: if self._global_container_stack:
self._global_container_stack.nameChanged.disconnect(self._onMachineNameChanged) self._global_container_stack.nameChanged.disconnect(self._onMachineNameChanged)
@ -310,8 +314,6 @@ class MachineManager(QObject):
self._global_container_stack.nameChanged.connect(self._onMachineNameChanged) self._global_container_stack.nameChanged.connect(self._onMachineNameChanged)
self._global_container_stack.containersChanged.connect(self._onInstanceContainersChanged) self._global_container_stack.containersChanged.connect(self._onInstanceContainersChanged)
self._global_container_stack.propertyChanged.connect(self._onGlobalPropertyChanged) self._global_container_stack.propertyChanged.connect(self._onGlobalPropertyChanged)
self._global_stack_valid = not self._checkStackForErrors(self._global_container_stack)
self.globalValidationChanged.emit()
material = self._global_container_stack.findContainer({"type": "material"}) material = self._global_container_stack.findContainer({"type": "material"})
material.nameChanged.connect(self._onMaterialNameChanged) material.nameChanged.connect(self._onMaterialNameChanged)
@ -329,6 +331,8 @@ class MachineManager(QObject):
self._active_container_stack.propertyChanged.connect(self._onGlobalPropertyChanged) self._active_container_stack.propertyChanged.connect(self._onGlobalPropertyChanged)
else: else:
self._active_container_stack = self._global_container_stack self._active_container_stack = self._global_container_stack
self._active_stack_valid = not self._checkStackForErrors(self._active_container_stack)
self.activeValidationChanged.emit()
def _onInstanceContainersChanged(self, container): def _onInstanceContainersChanged(self, container):
container_type = container.getMetaDataEntry("type") container_type = container.getMetaDataEntry("type")
@ -433,11 +437,11 @@ class MachineManager(QObject):
return len(user_settings) != 0 return len(user_settings) != 0
## Check if the global profile does not contain error states ## Check if the global profile does not contain error states
# Note that the _global_stack_valid is cached due to performance issues # Note that the _active_stack_valid is cached due to performance issues
# Calling _checkStackForErrors on every change is simply too expensive # Calling _checkStackForErrors on every change is simply too expensive
@pyqtProperty(bool, notify = globalValidationChanged) @pyqtProperty(bool, notify = activeValidationChanged)
def isGlobalStackValid(self): def isActiveStackValid(self):
return bool(self._global_stack_valid) return bool(self._active_stack_valid)
@pyqtProperty(str, notify = activeStackChanged) @pyqtProperty(str, notify = activeStackChanged)
def activeUserProfileId(self): def activeUserProfileId(self):
@ -740,10 +744,7 @@ class MachineManager(QObject):
# If the machine that is being removed is the currently active machine, set another machine as the active machine. # If the machine that is being removed is the currently active machine, set another machine as the active machine.
activate_new_machine = (self._global_container_stack and self._global_container_stack.getId() == machine_id) activate_new_machine = (self._global_container_stack and self._global_container_stack.getId() == machine_id)
stacks = UM.Settings.ContainerRegistry.getInstance().findContainerStacks(id = machine_id) ExtruderManager.getInstance().removeMachineExtruders(machine_id)
if not stacks:
return
ExtruderManager.getInstance().removeMachineExtruders(stacks[0].getBottom().getId())
containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(type = "user", machine = machine_id) containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(type = "user", machine = machine_id)
for container in containers: for container in containers:

View File

@ -10,6 +10,8 @@ from UM.Settings.InstanceContainer import InstanceContainer
from UM.Settings.ContainerRegistry import ContainerRegistry from UM.Settings.ContainerRegistry import ContainerRegistry
import UM.Logger import UM.Logger
import cura.Settings
from UM.Application import Application from UM.Application import Application
## A decorator that adds a container stack to a Node. This stack should be queried for all settings regarding ## A decorator that adds a container stack to a Node. This stack should be queried for all settings regarding
@ -26,7 +28,11 @@ class SettingOverrideDecorator(SceneNodeDecorator):
self._stack.setDirty(False) # This stack does not need to be saved. self._stack.setDirty(False) # This stack does not need to be saved.
self._instance = InstanceContainer(container_id = "SettingOverrideInstanceContainer") self._instance = InstanceContainer(container_id = "SettingOverrideInstanceContainer")
self._stack.addContainer(self._instance) self._stack.addContainer(self._instance)
self._extruder_stack = None #Stack upon which our stack is based.
if cura.Settings.ExtruderManager.getInstance().extruderCount > 1:
self._extruder_stack = cura.Settings.ExtruderManager.getInstance().activeExtruderStackId
else:
self._extruder_stack = None
self._stack.propertyChanged.connect(self._onSettingChanged) self._stack.propertyChanged.connect(self._onSettingChanged)
@ -41,6 +47,10 @@ class SettingOverrideDecorator(SceneNodeDecorator):
deep_copy = SettingOverrideDecorator() deep_copy = SettingOverrideDecorator()
## Copy the instance ## Copy the instance
deep_copy._instance = copy.deepcopy(self._instance, memo) deep_copy._instance = copy.deepcopy(self._instance, memo)
# Properly set the right extruder on the copy
deep_copy.setActiveExtruder(self._extruder_stack)
## Set the copied instance as the first (and only) instance container of the stack. ## Set the copied instance as the first (and only) instance container of the stack.
deep_copy._stack.replaceContainer(0, deep_copy._instance) deep_copy._stack.replaceContainer(0, deep_copy._instance)
return deep_copy return deep_copy
@ -61,7 +71,11 @@ class SettingOverrideDecorator(SceneNodeDecorator):
if self._extruder_stack: if self._extruder_stack:
extruder_stack = ContainerRegistry.getInstance().findContainerStacks(id = self._extruder_stack) extruder_stack = ContainerRegistry.getInstance().findContainerStacks(id = self._extruder_stack)
if extruder_stack: if extruder_stack:
if self._stack.getNextStack():
old_extruder_stack_id = self._stack.getNextStack().getId() old_extruder_stack_id = self._stack.getNextStack().getId()
else:
old_extruder_stack_id = ""
self._stack.setNextStack(extruder_stack[0]) self._stack.setNextStack(extruder_stack[0])
if self._stack.getNextStack().getId() != old_extruder_stack_id: #Only reslice if the extruder changed. if self._stack.getNextStack().getId() != old_extruder_stack_id: #Only reslice if the extruder changed.
Application.getInstance().getBackend().forceSlice() Application.getInstance().getBackend().forceSlice()
@ -75,6 +89,7 @@ class SettingOverrideDecorator(SceneNodeDecorator):
# \param extruder_stack_id The new extruder stack to print with. # \param extruder_stack_id The new extruder stack to print with.
def setActiveExtruder(self, extruder_stack_id): def setActiveExtruder(self, extruder_stack_id):
self._extruder_stack = extruder_stack_id self._extruder_stack = extruder_stack_id
self._updateNextStack()
self.activeExtruderChanged.emit() self.activeExtruderChanged.emit()
def getStack(self): def getStack(self):

View File

@ -5,6 +5,18 @@
import os import os
import sys import sys
import platform
from UM.Platform import Platform
#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
if platform.linux_distribution()[0] in ("debian", "Ubuntu", "LinuxMint"): # TODO: Needs a "if X11_GFX == 'nvidia'" here. The workaround is only needed on Ubuntu+NVidia drivers. Other drivers are not affected, but fine with this fix.
import ctypes
from ctypes.util import find_library
libGL = find_library("GL")
ctypes.CDLL(libGL, ctypes.RTLD_GLOBAL)
#WORKAROUND: GITHUB-704 GITHUB-708 #WORKAROUND: GITHUB-704 GITHUB-708
# It looks like setuptools creates a .pth file in # It looks like setuptools creates a .pth file in

View File

@ -9,18 +9,20 @@ from UM.Math.Vector import Vector
from UM.Scene.SceneNode import SceneNode from UM.Scene.SceneNode import SceneNode
from UM.Scene.GroupDecorator import GroupDecorator from UM.Scene.GroupDecorator import GroupDecorator
from UM.Math.Quaternion import Quaternion from UM.Math.Quaternion import Quaternion
from UM.Job import Job from UM.Job import Job
import math import math
import zipfile import zipfile
try:
import xml.etree.cElementTree as ET
except ImportError:
import xml.etree.ElementTree as ET import xml.etree.ElementTree as ET
## Base implementation for reading 3MF files. Has no support for textures. Only loads meshes! ## Base implementation for reading 3MF files. Has no support for textures. Only loads meshes!
class ThreeMFReader(MeshReader): class ThreeMFReader(MeshReader):
def __init__(self): def __init__(self):
super(ThreeMFReader, self).__init__() super().__init__()
self._supported_extensions = [".3mf"] self._supported_extensions = [".3mf"]
self._namespaces = { self._namespaces = {
@ -116,4 +118,10 @@ class ThreeMFReader(MeshReader):
except Exception as e: except Exception as e:
Logger.log("e", "exception occured in 3mf reader: %s", e) Logger.log("e", "exception occured in 3mf reader: %s", e)
try: # Selftest - There might be more functions that should fail
boundingBox = result.getBoundingBox()
boundingBox.isValid()
except:
return None
return result return result

View File

@ -68,6 +68,7 @@ class ChangeLog(Extension, QObject,):
line = line.replace("[","") line = line.replace("[","")
line = line.replace("]","") line = line.replace("]","")
open_version = Version(line) open_version = Version(line)
open_header = ""
self._change_logs[open_version] = collections.OrderedDict() self._change_logs[open_version] = collections.OrderedDict()
elif line.startswith("*"): elif line.startswith("*"):
open_header = line.replace("*","") open_header = line.replace("*","")

View File

@ -13,6 +13,7 @@ message Slice
repeated ObjectList object_lists = 1; // The meshgroups to be printed one after another repeated ObjectList object_lists = 1; // The meshgroups to be printed one after another
SettingList global_settings = 2; // The global settings used for the whole print job SettingList global_settings = 2; // The global settings used for the whole print job
repeated Extruder extruders = 3; // The settings sent to each extruder object repeated Extruder extruders = 3; // The settings sent to each extruder object
repeated SettingExtruder global_inherits_stack = 4; //From which stack the setting would inherit if not defined in a stack.
} }
message Extruder message Extruder
@ -108,8 +109,14 @@ message Setting {
bytes value = 2; // The value of the setting bytes value = 2; // The value of the setting
} }
message SettingExtruder {
string name = 1; //The setting key.
int32 extruder = 2; //From which extruder stack the setting should inherit.
}
message GCodePrefix { message GCodePrefix {
bytes data = 2; // Header string to be prenpended before the rest of the gcode sent from the engine bytes data = 2; //Header string to be prepended before the rest of the g-code sent from the engine.
} }
message SlicingFinished { message SlicingFinished {

View File

@ -16,6 +16,7 @@ from UM.Platform import Platform
import cura.Settings import cura.Settings
from cura.OneAtATimeIterator import OneAtATimeIterator from cura.OneAtATimeIterator import OneAtATimeIterator
from cura.Settings.ExtruderManager import ExtruderManager
from . import ProcessSlicedLayersJob from . import ProcessSlicedLayersJob
from . import ProcessGCodeJob from . import ProcessGCodeJob
from . import StartSliceJob from . import StartSliceJob
@ -31,7 +32,6 @@ import Arcus
from UM.i18n import i18nCatalog from UM.i18n import i18nCatalog
catalog = i18nCatalog("cura") catalog = i18nCatalog("cura")
class CuraEngineBackend(Backend): class CuraEngineBackend(Backend):
## Starts the back-end plug-in. ## Starts the back-end plug-in.
# #
@ -39,13 +39,30 @@ class CuraEngineBackend(Backend):
# with the back-end in general. # with the back-end in general.
def __init__(self): def __init__(self):
super().__init__() super().__init__()
# Find out where the engine is located, and how it is called.
# Find out where the engine is located, and how it is called. This depends on how Cura is packaged and which OS we are running on. # This depends on how Cura is packaged and which OS we are running on.
default_engine_location = os.path.join(Application.getInstallPrefix(), "bin", "CuraEngine") executable_name = "CuraEngine"
if hasattr(sys, "frozen"):
default_engine_location = os.path.join(os.path.dirname(os.path.abspath(sys.executable)), "CuraEngine")
if Platform.isWindows(): if Platform.isWindows():
default_engine_location += ".exe" executable_name += ".exe"
default_engine_location = executable_name
if os.path.exists(os.path.join(Application.getInstallPrefix(), "bin", executable_name)):
default_engine_location = os.path.join(Application.getInstallPrefix(), "bin", executable_name)
if hasattr(sys, "frozen"):
default_engine_location = os.path.join(os.path.dirname(os.path.abspath(sys.executable)), executable_name)
if Platform.isLinux() and not default_engine_location:
if not os.getenv("PATH"):
raise OSError("There is something wrong with your Linux installation.")
for pathdir in os.getenv("PATH").split(os.pathsep):
execpath = os.path.join(pathdir, executable_name)
if os.path.exists(execpath):
default_engine_location = execpath
break
if not default_engine_location:
raise EnvironmentError("Could not find CuraEngine")
Logger.log("i", "Found CuraEngine at: %s" %(default_engine_location))
default_engine_location = os.path.abspath(default_engine_location) default_engine_location = os.path.abspath(default_engine_location)
Preferences.getInstance().addPreference("backend/location", default_engine_location) Preferences.getInstance().addPreference("backend/location", default_engine_location)
@ -92,7 +109,7 @@ class CuraEngineBackend(Backend):
self._always_restart = True # Always restart the engine when starting a new slice. Don't keep the process running. TODO: Fix engine statelessness. self._always_restart = True # Always restart the engine when starting a new slice. Don't keep the process running. TODO: Fix engine statelessness.
self._process_layers_job = None # The currently active job to process layers, or None if it is not processing layers. self._process_layers_job = None # The currently active job to process layers, or None if it is not processing layers.
self._backend_log_max_lines = 200 # Maximal count of lines to buffer self._backend_log_max_lines = 20000 # Maximum number of lines to buffer
self._error_message = None # Pop-up message that shows errors. self._error_message = None # Pop-up message that shows errors.
self.backendQuit.connect(self._onBackendQuit) self.backendQuit.connect(self._onBackendQuit)
@ -117,9 +134,10 @@ class CuraEngineBackend(Backend):
# \return list of commands and args / parameters. # \return list of commands and args / parameters.
def getEngineCommand(self): def getEngineCommand(self):
json_path = Resources.getPath(Resources.DefinitionContainers, "fdmprinter.def.json") 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, "-vv"] return [Preferences.getInstance().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. ## Emitted when we get a message containing print duration and material amount.
# This also implies the slicing has finished.
# \param time The amount of time the print will take. # \param time The amount of time the print will take.
# \param material_amount The amount of material the print will use. # \param material_amount The amount of material the print will use.
printDurationMessage = Signal() printDurationMessage = Signal()
@ -177,6 +195,11 @@ class CuraEngineBackend(Backend):
self.slicingCancelled.emit() self.slicingCancelled.emit()
self.processingProgress.emit(0) self.processingProgress.emit(0)
Logger.log("d", "Attempting to kill the engine process") Logger.log("d", "Attempting to kill the engine process")
if Application.getInstance().getCommandLineOption("external-backend", False):
self._createSocket()
return
if self._process is not None: if self._process is not None:
Logger.log("d", "Killing engine process") Logger.log("d", "Killing engine process")
try: try:
@ -204,7 +227,7 @@ class CuraEngineBackend(Backend):
if job.getResult() == StartSliceJob.StartJobResult.SettingError: if job.getResult() == StartSliceJob.StartJobResult.SettingError:
if Application.getInstance().getPlatformActivity: if Application.getInstance().getPlatformActivity:
self._error_message = Message(catalog.i18nc("@info:status", "Unable to slice. Please check your setting values for errors."), lifetime = 10) self._error_message = Message(catalog.i18nc("@info:status", "Unable to slice. Please check your setting values for errors."))
self._error_message.show() self._error_message.show()
self.backendStateChange.emit(BackendState.Error) self.backendStateChange.emit(BackendState.Error)
else: else:
@ -213,7 +236,7 @@ class CuraEngineBackend(Backend):
if job.getResult() == StartSliceJob.StartJobResult.NothingToSlice: if job.getResult() == StartSliceJob.StartJobResult.NothingToSlice:
if Application.getInstance().getPlatformActivity: if Application.getInstance().getPlatformActivity:
self._error_message = Message(catalog.i18nc("@info:status", "Unable to slice. No suitable objects found."), lifetime = 10) self._error_message = Message(catalog.i18nc("@info:status", "Unable to slice. No suitable models found."))
self._error_message.show() self._error_message.show()
self.backendStateChange.emit(BackendState.Error) self.backendStateChange.emit(BackendState.Error)
else: else:
@ -252,6 +275,9 @@ class CuraEngineBackend(Backend):
return return
super()._onSocketError(error) super()._onSocketError(error)
if error.getErrorCode() == Arcus.ErrorCode.Debug:
return
self._terminate() self._terminate()
if error.getErrorCode() not in [Arcus.ErrorCode.BindFailedError, Arcus.ErrorCode.ConnectionResetError, Arcus.ErrorCode.Debug]: if error.getErrorCode() not in [Arcus.ErrorCode.BindFailedError, Arcus.ErrorCode.ConnectionResetError, Arcus.ErrorCode.Debug]:
@ -313,7 +339,7 @@ class CuraEngineBackend(Backend):
## Called when a print time message is received from the engine. ## Called when a print time message is received from the engine.
# #
# \param message The protobuf message containing the print time and # \param message The protobuff message containing the print time and
# material amount per extruder # material amount per extruder
def _onPrintTimeMaterialEstimates(self, message): def _onPrintTimeMaterialEstimates(self, message):
material_amounts = [] material_amounts = []
@ -388,22 +414,35 @@ class CuraEngineBackend(Backend):
if self._global_container_stack: if self._global_container_stack:
self._global_container_stack.propertyChanged.disconnect(self._onSettingChanged) self._global_container_stack.propertyChanged.disconnect(self._onSettingChanged)
self._global_container_stack.containersChanged.disconnect(self._onChanged) self._global_container_stack.containersChanged.disconnect(self._onChanged)
extruders = list(ExtruderManager.getInstance().getMachineExtruders(self._global_container_stack.getId()))
if extruders:
for extruder in extruders:
extruder.propertyChanged.disconnect(self._onSettingChanged)
self._global_container_stack = Application.getInstance().getGlobalContainerStack() self._global_container_stack = Application.getInstance().getGlobalContainerStack()
if self._global_container_stack: if self._global_container_stack:
self._global_container_stack.propertyChanged.connect(self._onSettingChanged) # Note: Only starts slicing when the value changed. self._global_container_stack.propertyChanged.connect(self._onSettingChanged) # Note: Only starts slicing when the value changed.
self._global_container_stack.containersChanged.connect(self._onChanged) self._global_container_stack.containersChanged.connect(self._onChanged)
extruders = list(ExtruderManager.getInstance().getMachineExtruders(self._global_container_stack.getId()))
if extruders:
for extruder in extruders:
extruder.propertyChanged.connect(self._onSettingChanged)
self._onActiveExtruderChanged() self._onActiveExtruderChanged()
self._onChanged() self._onChanged()
def _onActiveExtruderChanged(self): def _onActiveExtruderChanged(self):
if self._global_container_stack:
# Connect all extruders of the active machine. This might cause a few connects that have already happend,
# but that shouldn't cause issues as only new / unique connections are added.
extruders = list(ExtruderManager.getInstance().getMachineExtruders(self._global_container_stack.getId()))
if extruders:
for extruder in extruders:
extruder.propertyChanged.connect(self._onSettingChanged)
if self._active_extruder_stack: if self._active_extruder_stack:
self._active_extruder_stack.propertyChanged.disconnect(self._onSettingChanged)
self._active_extruder_stack.containersChanged.disconnect(self._onChanged) self._active_extruder_stack.containersChanged.disconnect(self._onChanged)
self._active_extruder_stack = cura.Settings.ExtruderManager.getInstance().getActiveExtruderStack() self._active_extruder_stack = cura.Settings.ExtruderManager.getInstance().getActiveExtruderStack()
if self._active_extruder_stack: if self._active_extruder_stack:
self._active_extruder_stack.propertyChanged.connect(self._onSettingChanged) # Note: Only starts slicing when the value changed.
self._active_extruder_stack.containersChanged.connect(self._onChanged) self._active_extruder_stack.containersChanged.connect(self._onChanged)

View File

@ -56,10 +56,9 @@ class ProcessSlicedLayersJob(Job):
## Remove old layer data (if any) ## Remove old layer data (if any)
for node in DepthFirstIterator(self._scene.getRoot()): for node in DepthFirstIterator(self._scene.getRoot()):
if type(node) is SceneNode and node.getMeshData():
if node.callDecoration("getLayerData"): if node.callDecoration("getLayerData"):
self._scene.getRoot().removeChild(node) node.getParent().removeChild(node)
Job.yieldThread() break
if self._abort_requested: if self._abort_requested:
if self._progress: if self._progress:
self._progress.hide() self._progress.hide()
@ -74,7 +73,7 @@ class ProcessSlicedLayersJob(Job):
# instead simply offset all other layers so the lowest layer is always 0. # instead simply offset all other layers so the lowest layer is always 0.
min_layer_number = 0 min_layer_number = 0
for layer in self._layers: for layer in self._layers:
if(layer.id < min_layer_number): if layer.id < min_layer_number:
min_layer_number = layer.id min_layer_number = layer.id
current_layer = 0 current_layer = 0
@ -117,7 +116,6 @@ class ProcessSlicedLayersJob(Job):
new_points[:, 1] = points[:, 2] new_points[:, 1] = points[:, 2]
new_points[:, 2] = -points[:, 1] new_points[:, 2] = -points[:, 1]
this_poly = LayerPolygon.LayerPolygon(layer_data, extruder, line_types, new_points, line_widths) this_poly = LayerPolygon.LayerPolygon(layer_data, extruder, line_types, new_points, line_widths)
this_poly.buildCache() this_poly.buildCache()
@ -185,4 +183,3 @@ class ProcessSlicedLayersJob(Job):
else: else:
if self._progress: if self._progress:
self._progress.hide() self._progress.hide()

View File

@ -13,6 +13,7 @@ from UM.Scene.SceneNode import SceneNode
from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
from UM.Settings.Validator import ValidatorState from UM.Settings.Validator import ValidatorState
from UM.Settings.SettingRelation import RelationType
from cura.OneAtATimeIterator import OneAtATimeIterator from cura.OneAtATimeIterator import OneAtATimeIterator
@ -24,6 +25,7 @@ class StartJobResult(IntEnum):
SettingError = 3 SettingError = 3
NothingToSlice = 4 NothingToSlice = 4
## Formatter class that handles token expansion in start/end gcod ## Formatter class that handles token expansion in start/end gcod
class GcodeStartEndFormatter(Formatter): class GcodeStartEndFormatter(Formatter):
def get_value(self, key, args, kwargs): # [CodeStyle: get_value is an overridden function from the Formatter class] def get_value(self, key, args, kwargs): # [CodeStyle: get_value is an overridden function from the Formatter class]
@ -37,6 +39,7 @@ class GcodeStartEndFormatter(Formatter):
Logger.log("w", "Incorrectly formatted placeholder '%s' in start/end gcode", key) Logger.log("w", "Incorrectly formatted placeholder '%s' in start/end gcode", key)
return "{" + str(key) + "}" return "{" + str(key) + "}"
## Job class that builds up the message of scene data to send to CuraEngine. ## Job class that builds up the message of scene data to send to CuraEngine.
class StartSliceJob(Job): class StartSliceJob(Job):
def __init__(self, slice_message): def __init__(self, slice_message):
@ -71,7 +74,7 @@ class StartSliceJob(Job):
return return
# Don't slice if there is a setting with an error value. # Don't slice if there is a setting with an error value.
if self._checkStackForErrors(stack): if not Application.getInstance().getMachineManager().isActiveStackValid:
self.setResult(StartJobResult.SettingError) self.setResult(StartJobResult.SettingError)
return return
@ -123,11 +126,15 @@ class StartSliceJob(Job):
if temp_list: if temp_list:
object_groups.append(temp_list) object_groups.append(temp_list)
# There are cases when there is nothing to slice. This can happen due to one at a time slicing not being
# able to find a possible sequence or because there are no objects on the build plate (or they are outside
# the build volume)
if not object_groups: if not object_groups:
self.setResult(StartJobResult.NothingToSlice) self.setResult(StartJobResult.NothingToSlice)
return return
self._buildGlobalSettingsMessage(stack) self._buildGlobalSettingsMessage(stack)
self._buildGlobalInheritsStackMessage(stack)
for extruder_stack in cura.Settings.ExtruderManager.getInstance().getMachineExtruders(stack.getId()): for extruder_stack in cura.Settings.ExtruderManager.getInstance().getMachineExtruders(stack.getId()):
self._buildExtruderMessage(extruder_stack) self._buildExtruderMessage(extruder_stack)
@ -171,6 +178,7 @@ class StartSliceJob(Job):
Logger.logException("w", "Unable to do token replacement on start/end gcode") Logger.logException("w", "Unable to do token replacement on start/end gcode")
return str(value).encode("utf-8") return str(value).encode("utf-8")
## Create extruder message from stack
def _buildExtruderMessage(self, stack): def _buildExtruderMessage(self, stack):
message = self._slice_message.addRepeatedMessage("extruders") message = self._slice_message.addRepeatedMessage("extruders")
message.id = int(stack.getMetaDataEntry("position")) message.id = int(stack.getMetaDataEntry("position"))
@ -209,11 +217,54 @@ class StartSliceJob(Job):
else: else:
setting_message.value = str(value).encode("utf-8") setting_message.value = str(value).encode("utf-8")
## Sends for some settings which extruder they should fallback to if not
# set.
#
# This is only set for settings that have the global_inherits_stack
# property.
#
# \param stack The global stack with all settings, from which to read the
# global_inherits_stack property.
def _buildGlobalInheritsStackMessage(self, stack):
for key in stack.getAllKeys():
extruder = int(round(float(stack.getProperty(key, "global_inherits_stack"))))
if extruder >= 0: #Set to a specific extruder.
setting_extruder = self._slice_message.addRepeatedMessage("global_inherits_stack")
setting_extruder.name = key
setting_extruder.extruder = extruder
## Check if a node has per object settings and ensure that they are set correctly in the message
# \param node \type{SceneNode} Node to check.
# \param message object_lists message to put the per object settings in
def _handlePerObjectSettings(self, node, message): def _handlePerObjectSettings(self, node, message):
stack = node.callDecoration("getStack") stack = node.callDecoration("getStack")
# Check if the node has a stack attached to it and the stack has any settings in the top container.
if stack: if stack:
for key in stack.getAllKeys(): # Check all settings for relations, so we can also calculate the correct values for dependant settings.
changed_setting_keys = set(stack.getTop().getAllKeys())
for key in stack.getTop().getAllKeys():
instance = stack.getTop().getInstance(key)
self._addRelations(changed_setting_keys, instance.definition.relations)
Job.yieldThread()
# Ensure that the engine is aware what the build extruder is
if stack.getProperty("machine_extruder_count", "value") > 1:
changed_setting_keys.add("extruder_nr")
# Get values for all changed settings
for key in changed_setting_keys:
setting = message.addRepeatedMessage("settings") setting = message.addRepeatedMessage("settings")
setting.name = key setting.name = key
setting.value = str(stack.getProperty(key, "value")).encode("utf-8") setting.value = str(stack.getProperty(key, "value")).encode("utf-8")
Job.yieldThread() Job.yieldThread()
## Recursive function to put all settings that require eachother for value changes in a list
# \param relations_set \type{set} Set of keys (strings) of settings that are influenced
# \param relations list of relation objects that need to be checked.
def _addRelations(self, relations_set, relations):
for relation in filter(lambda r: r.role == "value", relations):
if relation.type == RelationType.RequiresTarget:
continue
relations_set.add(relation.target.key)
self._addRelations(relations_set, relation.target.relations)

View File

@ -9,6 +9,8 @@ import UM.Settings.ContainerRegistry
from cura.CuraApplication import CuraApplication from cura.CuraApplication import CuraApplication
from cura.Settings.ExtruderManager import ExtruderManager from cura.Settings.ExtruderManager import ExtruderManager
from UM.Settings.InstanceContainer import InstanceContainer
import re #For escaping characters in the settings. import re #For escaping characters in the settings.
import json import json
@ -61,6 +63,20 @@ class GCodeWriter(MeshWriter):
return False return False
## Create a new container with container 2 as base and container 1 written over it.
def _createFlattenedContainerInstance(self, instance_container1, instance_container2):
flat_container = InstanceContainer(instance_container2.getName())
flat_container.setDefinition(instance_container2.getDefinition())
flat_container.setMetaData(instance_container2.getMetaData())
for key in instance_container2.getAllKeys():
flat_container.setProperty(key, "value", instance_container2.getProperty(key, "value"))
for key in instance_container1.getAllKeys():
flat_container.setProperty(key, "value", instance_container1.getProperty(key, "value"))
return flat_container
## Serialises a container stack to prepare it for writing at the end of the ## Serialises a container stack to prepare it for writing at the end of the
# g-code. # g-code.
# #
@ -74,38 +90,21 @@ class GCodeWriter(MeshWriter):
prefix_length = len(prefix) prefix_length = len(prefix)
container_with_profile = stack.findContainer({"type": "quality"}) container_with_profile = stack.findContainer({"type": "quality"})
machine_manager = CuraApplication.getInstance().getMachineManager() if not container_with_profile:
Logger.log("e", "No valid quality profile found, not writing settings to GCode!")
# Duplicate the current quality profile and update it with any user settings. return ""
flat_quality_id = machine_manager.duplicateContainer(container_with_profile.getId())
flat_quality = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(id = flat_quality_id)[0]
flat_quality._dirty = False
user_settings = stack.getTop()
# We don't want to send out any signals, so disconnect them.
flat_quality.propertyChanged.disconnectAll()
for key in user_settings.getAllKeys():
flat_quality.setProperty(key, "value", user_settings.getProperty(key, "value"))
serialized = flat_quality.serialize()
flat_global_container = self._createFlattenedContainerInstance(stack.getTop(),container_with_profile)
serialized = flat_global_container.serialize()
data = {"global_quality": serialized} data = {"global_quality": serialized}
manager = ExtruderManager.getInstance()
for extruder in manager.getMachineExtruders(stack.getId()): for extruder in ExtruderManager.getInstance().getMachineExtruders(stack.getId()):
extruder_quality = extruder.findContainer({"type": "quality"}) extruder_quality = extruder.findContainer({"type": "quality"})
if not extruder_quality:
Logger.log("w", "No extruder quality profile found, not writing quality for extruder %s to file!", extruder.getId())
continue
flat_extruder_quality_id = machine_manager.duplicateContainer(extruder_quality.getId()) flat_extruder_quality = self._createFlattenedContainerInstance(extruder.getTop(), extruder_quality)
flat_extruder_quality = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(id=flat_extruder_quality_id)[0]
flat_extruder_quality._dirty = False
extruder_user_settings = extruder.getTop()
# We don't want to send out any signals, so disconnect them.
flat_extruder_quality.propertyChanged.disconnectAll()
for key in extruder_user_settings.getAllKeys():
flat_extruder_quality.setProperty(key, "value", extruder_user_settings.getProperty(key, "value"))
extruder_serialized = flat_extruder_quality.serialize() extruder_serialized = flat_extruder_quality.serialize()
data.setdefault("extruder_quality", []).append(extruder_serialized) data.setdefault("extruder_quality", []).append(extruder_serialized)

View File

@ -11,6 +11,7 @@ from UM.Math.Color import Color
from UM.Mesh.MeshBuilder import MeshBuilder from UM.Mesh.MeshBuilder import MeshBuilder
from UM.Job import Job from UM.Job import Job
from UM.Preferences import Preferences from UM.Preferences import Preferences
from UM.Logger import Logger
from UM.View.RenderBatch import RenderBatch from UM.View.RenderBatch import RenderBatch
from UM.View.GL.OpenGL import OpenGL from UM.View.GL.OpenGL import OpenGL
@ -34,7 +35,7 @@ class LayerView(View):
self._shader = None self._shader = None
self._selection_shader = None self._selection_shader = None
self._num_layers = 0 self._num_layers = 0
self._layer_percentage = 0 # what percentage of layers need to be shown (SLider gives value between 0 - 100) self._layer_percentage = 0 # what percentage of layers need to be shown (Slider gives value between 0 - 100)
self._proxy = LayerViewProxy.LayerViewProxy() self._proxy = LayerViewProxy.LayerViewProxy()
self._controller.getScene().getRoot().childrenChanged.connect(self._onSceneChanged) self._controller.getScene().getRoot().childrenChanged.connect(self._onSceneChanged)
self._max_layers = 0 self._max_layers = 0
@ -43,12 +44,14 @@ class LayerView(View):
self._current_layer_jumps = None self._current_layer_jumps = None
self._top_layers_job = None self._top_layers_job = None
self._activity = False self._activity = False
self._old_max_layers = 0
Preferences.getInstance().addPreference("view/top_layer_count", 5) Preferences.getInstance().addPreference("view/top_layer_count", 5)
Preferences.getInstance().addPreference("view/only_show_top_layers", False)
Preferences.getInstance().preferenceChanged.connect(self._onPreferencesChanged) Preferences.getInstance().preferenceChanged.connect(self._onPreferencesChanged)
self._solid_layers = int(Preferences.getInstance().getValue("view/top_layer_count")) 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._busy = False self._busy = False
def getActivity(self): def getActivity(self):
@ -100,7 +103,7 @@ class LayerView(View):
continue continue
# Render all layers below a certain number as line mesh instead of vertices. # Render all layers below a certain number as line mesh instead of vertices.
if self._current_layer_num - self._solid_layers > -1: if self._current_layer_num - self._solid_layers > -1 and not self._only_show_top_layers:
start = 0 start = 0
end = 0 end = 0
element_counts = layer_data.getElementCounts() element_counts = layer_data.getElementCounts()
@ -126,14 +129,12 @@ class LayerView(View):
if self._current_layer_num > self._max_layers: if self._current_layer_num > self._max_layers:
self._current_layer_num = self._max_layers self._current_layer_num = self._max_layers
self.resetLayerData()
self._startUpdateTopLayers() self._startUpdateTopLayers()
self.currentLayerNumChanged.emit() self.currentLayerNumChanged.emit()
def calculateMaxLayers(self): def calculateMaxLayers(self):
scene = self.getController().getScene() scene = self.getController().getScene()
renderer = self.getRenderer() # TODO: @UnusedVariable
self._activity = True self._activity = True
self._old_max_layers = self._max_layers self._old_max_layers = self._max_layers
@ -199,7 +200,7 @@ class LayerView(View):
if not job.getResult(): if not job.getResult():
return return
self.resetLayerData() # Reset the layer data only when job is done. Doing it now prevents "blinking" data.
self._current_layer_mesh = job.getResult().get("layers") self._current_layer_mesh = job.getResult().get("layers")
self._current_layer_jumps = job.getResult().get("jumps") self._current_layer_jumps = job.getResult().get("jumps")
self._controller.getScene().sceneChanged.emit(self._controller.getScene().getRoot()) self._controller.getScene().sceneChanged.emit(self._controller.getScene().getRoot())
@ -207,14 +208,15 @@ class LayerView(View):
self._top_layers_job = None self._top_layers_job = None
def _onPreferencesChanged(self, preference): def _onPreferencesChanged(self, preference):
if preference != "view/top_layer_count": if preference != "view/top_layer_count" and preference != "view/only_show_top_layers":
return return
self._solid_layers = int(Preferences.getInstance().getValue("view/top_layer_count")) 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.resetLayerData()
self._startUpdateTopLayers() self._startUpdateTopLayers()
class _CreateTopLayersJob(Job): class _CreateTopLayersJob(Job):
def __init__(self, scene, layer_number, solid_layers): def __init__(self, scene, layer_number, solid_layers):
super().__init__() super().__init__()
@ -242,20 +244,20 @@ class _CreateTopLayersJob(Job):
try: try:
layer = layer_data.getLayer(layer_number).createMesh() layer = layer_data.getLayer(layer_number).createMesh()
except Exception as e: except Exception:
print(e) Logger.logException("w", "An exception occurred while creating layer mesh.")
return return
if not layer or layer.getVertices() is None: if not layer or layer.getVertices() is None:
continue continue
layer_mesh.addIndices(layer_mesh._vertex_count+layer.getIndices()) layer_mesh.addIndices(layer_mesh.getVertexCount() + layer.getIndices())
layer_mesh.addVertices(layer.getVertices()) layer_mesh.addVertices(layer.getVertices())
# Scale layer color by a brightness factor based on the current layer number # Scale layer color by a brightness factor based on the current layer number
# This will result in a range of 0.5 - 1.0 to multiply colors by. # This will result in a range of 0.5 - 1.0 to multiply colors by.
brightness = numpy.ones((1, 4), dtype=numpy.float32) * (2.0 - (i / self._solid_layers)) / 2.0 brightness = numpy.ones((1, 4), dtype=numpy.float32) * (2.0 - (i / self._solid_layers)) / 2.0
brightness[0, 3] = 1.0; brightness[0, 3] = 1.0
layer_mesh.addColors(layer.getColors() * brightness) layer_mesh.addColors(layer.getColors() * brightness)
if self._cancel: if self._cancel:

View File

@ -13,7 +13,40 @@ import ".."
Button { Button {
id: base; id: base;
style: UM.Theme.styles.sidebar_category; style: ButtonStyle {
background: Item { }
label: Row
{
spacing: UM.Theme.getSize("default_lining").width
UM.RecolorImage
{
anchors.verticalCenter: parent.verticalCenter
height: label.height / 2
width: height
source: control.checked ? UM.Theme.getIcon("arrow_bottom") : UM.Theme.getIcon("arrow_right");
color: control.hovered ? palette.highlight : palette.buttonText
}
UM.RecolorImage
{
anchors.verticalCenter: parent.verticalCenter
height: label.height
width: height
source: control.iconSource
color: control.hovered ? palette.highlight : palette.buttonText
}
Label
{
id: label
anchors.verticalCenter: parent.verticalCenter
text: control.text
color: control.hovered ? palette.highlight : palette.buttonText
font.bold: true
}
SystemPalette { id: palette }
}
}
signal showTooltip(string text); signal showTooltip(string text);
signal hideTooltip(); signal hideTooltip();

View File

@ -16,17 +16,17 @@ UM.TooltipArea
width: childrenRect.width; width: childrenRect.width;
height: childrenRect.height; height: childrenRect.height;
Button CheckBox
{ {
id: check id: check
text: definition.label text: definition.label
checked: addedSettingsModel.getVisible(model.key)
onClicked: onClicked:
{ {
addedSettingsModel.setVisible(model.key, true); addedSettingsModel.setVisible(model.key, checked);
settingPickDialog.visible = false UM.ActiveTool.forceUpdate();
UM.ActiveTool.forceUpdate()
} }
} }
} }

View File

@ -1,14 +1,18 @@
# Copyright (c) 2016 Ultimaker B.V.
# Cura is released under the terms of the AGPLv3 or higher.
from PyQt5.QtCore import QObject, pyqtProperty, pyqtSignal from PyQt5.QtCore import QObject, pyqtProperty, pyqtSignal
from UM.Application import Application from UM.Application import Application
from UM.Settings.SettingInstance import SettingInstance from UM.Settings.SettingInstance import SettingInstance
from UM.Logger import Logger from UM.Logger import Logger
import UM.Settings.Models import UM.Settings.Models
from cura.Settings.ExtruderManager import ExtruderManager #To get global-inherits-stack setting values from different extruders.
from cura.Settings.SettingOverrideDecorator import SettingOverrideDecorator from cura.Settings.SettingOverrideDecorator import SettingOverrideDecorator
## The per object setting visibility handler ensures that only setting defintions that have a matching instance Container ## The per object setting visibility handler ensures that only setting
# are returned as visible. # definitions that have a matching instance Container are returned as visible.
class PerObjectSettingVisibilityHandler(UM.Settings.Models.SettingVisibilityHandler): class PerObjectSettingVisibilityHandler(UM.Settings.Models.SettingVisibilityHandler):
def __init__(self, parent = None, *args, **kwargs): def __init__(self, parent = None, *args, **kwargs):
super().__init__(parent = parent, *args, **kwargs) super().__init__(parent = parent, *args, **kwargs)
@ -54,10 +58,23 @@ class PerObjectSettingVisibilityHandler(UM.Settings.Models.SettingVisibilityHand
if not settings.getInstance(item): if not settings.getInstance(item):
definition = self._stack.getSettingDefinition(item) definition = self._stack.getSettingDefinition(item)
if definition: if definition:
settings.addInstance(SettingInstance(definition, settings)) new_instance = SettingInstance(definition, settings)
stack_nr = -1
if definition.global_inherits_stack and self._stack.getProperty("machine_extruder_count", "value") > 1:
#Obtain the value from the correct container stack. Only once, upon adding the setting.
stack_nr = str(int(round(float(self._stack.getProperty(item, "global_inherits_stack"))))) #Stack to get the setting from. Round it and remove the fractional part.
if stack_nr not in ExtruderManager.getInstance().extruderIds and self._stack.getProperty("extruder_nr", "value"): #Property not defined, but we have an extruder number.
stack_nr = str(int(round(float(self._stack.getProperty("extruder_nr", "value")))))
if stack_nr in ExtruderManager.getInstance().extruderIds: #We have either a global_inherits_stack or an extruder_nr.
stack = UM.Settings.ContainerRegistry.getInstance().findContainerStacks(id = ExtruderManager.getInstance().extruderIds[stack_nr])[0]
else:
stack = UM.Application.getInstance().getGlobalContainerStack()
new_instance.setProperty("value", stack.getProperty(item, "value"))
new_instance.resetState() # Ensure that the state is not seen as a user state.
settings.addInstance(new_instance)
visibility_changed = True visibility_changed = True
else: else:
Logger.log("w", "Unable to add instance (%s) to perobject visibility because we couldn't find the matching definition", item) Logger.log("w", "Unable to add instance (%s) to per-object visibility because we couldn't find the matching definition", item)
if visibility_changed: if visibility_changed:
self.visibilityChanged.emit() self.visibilityChanged.emit()

View File

@ -31,7 +31,7 @@ Item {
spacing: UM.Theme.getSize("default_margin").width spacing: UM.Theme.getSize("default_margin").width
Label Label
{ {
text: catalog.i18nc("@label", "Print object with") text: catalog.i18nc("@label", "Print model with")
anchors.verticalCenter: extruderSelector.verticalCenter anchors.verticalCenter: extruderSelector.verticalCenter
color: UM.Theme.getColor("setting_control_text") color: UM.Theme.getColor("setting_control_text")
@ -47,7 +47,9 @@ Item {
id: extruders_model id: extruders_model
onRowsInserted: extruderSelector.visible = extruders_model.rowCount() > 1 onRowsInserted: extruderSelector.visible = extruders_model.rowCount() > 1
onModelReset: extruderSelector.visible = extruders_model.rowCount() > 1 onModelReset: extruderSelector.visible = extruders_model.rowCount() > 1
onModelChanged: extruderSelector.color = extruders_model.getItem(extruderSelector.currentIndex).color
} }
property string color: extruders_model.getItem(extruderSelector.currentIndex).color
visible: extruders_model.rowCount() > 1 visible: extruders_model.rowCount() > 1
textRole: "name" textRole: "name"
width: UM.Theme.getSize("setting_control").width width: UM.Theme.getSize("setting_control").width
@ -88,7 +90,7 @@ Item {
anchors.leftMargin: UM.Theme.getSize("default_lining").width anchors.leftMargin: UM.Theme.getSize("default_lining").width
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
color: extruders_model.getItem(extruderSelector.currentIndex).colour color: extruderSelector.color
border.width: UM.Theme.getSize("default_lining").width border.width: UM.Theme.getSize("default_lining").width
border.color: !enabled ? UM.Theme.getColor("setting_control_disabled_border") : UM.Theme.getColor("setting_control_border") border.color: !enabled ? UM.Theme.getColor("setting_control_disabled_border") : UM.Theme.getColor("setting_control_border")
} }
@ -126,7 +128,11 @@ Item {
} }
} }
onActivated: UM.ActiveTool.setProperty("SelectedActiveExtruder", extruders_model.getItem(index).id); onActivated:
{
UM.ActiveTool.setProperty("SelectedActiveExtruder", extruders_model.getItem(index).id);
extruderSelector.color = extruders_model.getItem(index).color;
}
onModelChanged: updateCurrentIndex(); onModelChanged: updateCurrentIndex();
function updateCurrentIndex() function updateCurrentIndex()
@ -136,6 +142,7 @@ Item {
if(extruders_model.getItem(i).id == UM.ActiveTool.properties.getValue("SelectedActiveExtruder")) if(extruders_model.getItem(i).id == UM.ActiveTool.properties.getValue("SelectedActiveExtruder"))
{ {
extruderSelector.currentIndex = i; extruderSelector.currentIndex = i;
extruderSelector.color = extruders_model.getItem(i).color;
return; return;
} }
} }
@ -144,6 +151,10 @@ Item {
} }
} }
Column
{
spacing: UM.Theme.getSize("default_lining").height
Repeater Repeater
{ {
id: contents id: contents
@ -166,8 +177,8 @@ Item {
Loader Loader
{ {
id: settingLoader id: settingLoader
width: UM.Theme.getSize("setting").width; width: UM.Theme.getSize("setting").width
height: UM.Theme.getSize("section").height; height: UM.Theme.getSize("section").height
property var definition: model property var definition: model
property var settingDefinitionsModel: addedSettingsModel property var settingDefinitionsModel: addedSettingsModel
@ -212,7 +223,7 @@ Item {
Button Button
{ {
width: UM.Theme.getSize("setting").height; width: UM.Theme.getSize("setting").height / 2;
height: UM.Theme.getSize("setting").height; height: UM.Theme.getSize("setting").height;
onClicked: addedSettingsModel.setVisible(model.key, false); onClicked: addedSettingsModel.setVisible(model.key, false);
@ -224,17 +235,17 @@ Item {
UM.RecolorImage UM.RecolorImage
{ {
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter width: parent.width
width: parent.width/2
height: parent.height / 2 height: parent.height / 2
sourceSize.width: width sourceSize.width: width
sourceSize.height: width sourceSize.height: width
color: control.hovered ? UM.Theme.getColor("setting_control_button_hover") : UM.Theme.getColor("setting_control_button") color: control.hovered ? UM.Theme.getColor("setting_control_button_hover") : UM.Theme.getColor("setting_control_button")
source: UM.Theme.getIcon("cross1") source: UM.Theme.getIcon("minus")
} }
} }
} }
} }
UM.SettingPropertyProvider UM.SettingPropertyProvider
{ {
id: provider id: provider
@ -243,6 +254,8 @@ Item {
key: model.key key: model.key
watchedProperties: [ "value", "enabled", "validationState" ] watchedProperties: [ "value", "enabled", "validationState" ]
storeIndex: 0 storeIndex: 0
removeUnusedValue: false
}
} }
} }
} }
@ -253,7 +266,7 @@ Item {
height: UM.Theme.getSize("setting").height; height: UM.Theme.getSize("setting").height;
visible: parseInt(UM.Preferences.getValue("cura/active_mode")) == 1 visible: parseInt(UM.Preferences.getValue("cura/active_mode")) == 1
text: catalog.i18nc("@action:button", "Add Setting"); text: catalog.i18nc("@action:button", "Select settings");
style: ButtonStyle style: ButtonStyle
{ {
@ -293,16 +306,28 @@ Item {
UM.Dialog { UM.Dialog {
id: settingPickDialog id: settingPickDialog
title: catalog.i18nc("@title:window", "Pick a Setting to Customize") title: catalog.i18nc("@title:window", "Select Settings to Customize for this model")
width: screenScaleFactor * 360;
property string labelFilter: "" property string labelFilter: ""
onVisibilityChanged:
{
// force updating the model to sync it with addedSettingsModel
if(visible)
{
listview.model.forceUpdate()
}
}
TextField { TextField {
id: filter; id: filter
anchors { anchors {
top: parent.top; top: parent.top
left: parent.left; left: parent.left
right: parent.right; right: toggleShowAll.left
rightMargin: UM.Theme.getSize("default_margin").width
} }
placeholderText: catalog.i18nc("@label:textbox", "Filter..."); placeholderText: catalog.i18nc("@label:textbox", "Filter...");
@ -320,6 +345,23 @@ Item {
} }
} }
CheckBox
{
id: toggleShowAll
anchors {
top: parent.top
right: parent.right
}
text: catalog.i18nc("@label:checkbox", "Show all")
checked: listview.model.showAll
onClicked:
{
listview.model.showAll = checked;
}
}
ScrollView ScrollView
{ {
id: scrollView id: scrollView
@ -373,7 +415,7 @@ Item {
rightButtons: [ rightButtons: [
Button { Button {
text: catalog.i18nc("@action:button", "Cancel"); text: catalog.i18nc("@action:button", "Close");
onClicked: { onClicked: {
settingPickDialog.visible = false; settingPickDialog.visible = false;
} }

View File

@ -11,15 +11,15 @@ i18n_catalog = i18nCatalog("cura")
def getMetaData(): def getMetaData():
return { return {
"plugin": { "plugin": {
"name": i18n_catalog.i18nc("@label", "Per Object Settings Tool"), "name": i18n_catalog.i18nc("@label", "Per Model Settings Tool"),
"author": "Ultimaker", "author": "Ultimaker",
"version": "1.0", "version": "1.0",
"description": i18n_catalog.i18nc("@info:whatsthis", "Provides the Per Object Settings."), "description": i18n_catalog.i18nc("@info:whatsthis", "Provides the Per Model Settings."),
"api": 3 "api": 3
}, },
"tool": { "tool": {
"name": i18n_catalog.i18nc("@label", "Per Object Settings"), "name": i18n_catalog.i18nc("@label", "Per Model Settings"),
"description": i18n_catalog.i18nc("@info:tooltip", "Configure Per Object Settings"), "description": i18n_catalog.i18nc("@info:tooltip", "Configure Per Model Settings"),
"icon": "setting_per_object", "icon": "setting_per_object",
"tool_panel": "PerObjectSettingsPanel.qml", "tool_panel": "PerObjectSettingsPanel.qml",
"weight": 3 "weight": 3

View File

@ -11,11 +11,8 @@ from UM.i18n import i18nCatalog
from UM.Logger import Logger from UM.Logger import Logger
from UM.Platform import Platform from UM.Platform import Platform
from UM.Qt.Duration import DurationFormat from UM.Qt.Duration import DurationFormat
from UM.Job import Job
import collections
import json
import os.path
import copy
import platform import platform
import math import math
import urllib.request import urllib.request
@ -24,6 +21,36 @@ import ssl
catalog = i18nCatalog("cura") catalog = i18nCatalog("cura")
class SliceInfoJob(Job):
data = None
url = None
def __init__(self, url, data):
super().__init__()
self.url = url
self.data = data
def run(self):
if not self.url or not self.data:
Logger.log("e", "URL or DATA for sending slice info was not set!")
return
# Submit data
kwoptions = {"data" : self.data,
"timeout" : 5
}
if Platform.isOSX():
kwoptions["context"] = ssl._create_unverified_context()
try:
f = urllib.request.urlopen(self.url, **kwoptions)
Logger.log("i", "Sent anonymous slice info to %s", self.url)
f.close()
except urllib.error.HTTPError as http_exception:
Logger.log("e", "An HTTP error occurred while trying to send slice information: %s" % http_exception)
except Exception as e: # We don't want any exception to cause problems
Logger.log("e", "An exception occurred while trying to send slice information: %s" % e)
## This Extension runs in the background and sends several bits of information to the Ultimaker servers. ## This Extension runs in the background and sends several bits of information to the Ultimaker servers.
# The data is only sent when the user in question gave permission to do so. All data is anonymous and # The data is only sent when the user in question gave permission to do so. All data is anonymous and
@ -112,19 +139,10 @@ class SliceInfo(Extension):
submitted_data = urllib.parse.urlencode(submitted_data) submitted_data = urllib.parse.urlencode(submitted_data)
binary_data = submitted_data.encode("utf-8") binary_data = submitted_data.encode("utf-8")
# Submit data # Sending slice info non-blocking
kwoptions = {"data" : binary_data, reportJob = SliceInfoJob(self.info_url, binary_data)
"timeout" : 1 reportJob.start()
}
if Platform.isOSX():
kwoptions["context"] = ssl._create_unverified_context()
try:
f = urllib.request.urlopen(self.info_url, **kwoptions)
Logger.log("i", "Sent anonymous slice info to %s", self.info_url)
f.close()
except Exception as e: except Exception as e:
Logger.logException("e", "An exception occurred while trying to send slice information")
except:
# We really can't afford to have a mistake here, as this would break the sending of g-code to a device # We really can't afford to have a mistake here, as this would break the sending of g-code to a device
# (Either saving or directly to a printer). The functionality of the slice data is not *that* important. # (Either saving or directly to a printer). The functionality of the slice data is not *that* important.
pass Logger.log("e", "Exception raised while sending slice info: %s" %(repr(e))) # But we should be notified about these problems of course.

View File

@ -3,10 +3,12 @@
from UM.View.View import View from UM.View.View import View
from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
from UM.Scene.Selection import Selection
from UM.Resources import Resources from UM.Resources import Resources
from UM.Application import Application from UM.Application import Application
from UM.Preferences import Preferences from UM.Preferences import Preferences
from UM.View.Renderer import Renderer from UM.View.Renderer import Renderer
from UM.Settings.Validator import ValidatorState
from UM.View.GL.OpenGL import OpenGL from UM.View.GL.OpenGL import OpenGL
@ -39,39 +41,47 @@ class SolidView(View):
self._disabled_shader.setUniformValue("u_diffuseColor2", [0.68, 0.68, 0.68, 1.0]) self._disabled_shader.setUniformValue("u_diffuseColor2", [0.68, 0.68, 0.68, 1.0])
self._disabled_shader.setUniformValue("u_width", 50.0) self._disabled_shader.setUniformValue("u_width", 50.0)
if Application.getInstance().getGlobalContainerStack(): multi_extrusion = False
global_container_stack = Application.getInstance().getGlobalContainerStack()
if global_container_stack:
if Preferences.getInstance().getValue("view/show_overhang"): if Preferences.getInstance().getValue("view/show_overhang"):
angle = Application.getInstance().getGlobalContainerStack().getProperty("support_angle", "value") angle = global_container_stack.getProperty("support_angle", "value")
if angle is not None: if angle is not None and global_container_stack.getProperty("support_angle", "validationState") == ValidatorState.Valid:
self._enabled_shader.setUniformValue("u_overhangAngle", math.cos(math.radians(90 - angle))) self._enabled_shader.setUniformValue("u_overhangAngle", math.cos(math.radians(90 - angle)))
else: else:
self._enabled_shader.setUniformValue("u_overhangAngle", math.cos(math.radians(0))) #Overhang angle of 0 causes no area at all to be marked as overhang. self._enabled_shader.setUniformValue("u_overhangAngle", math.cos(math.radians(0))) #Overhang angle of 0 causes no area at all to be marked as overhang.
else: else:
self._enabled_shader.setUniformValue("u_overhangAngle", math.cos(math.radians(0))) self._enabled_shader.setUniformValue("u_overhangAngle", math.cos(math.radians(0)))
multi_extrusion = global_container_stack.getProperty("machine_extruder_count", "value") > 1
for node in DepthFirstIterator(scene.getRoot()): for node in DepthFirstIterator(scene.getRoot()):
if not node.render(renderer): if not node.render(renderer):
if node.getMeshData() and node.isVisible(): if node.getMeshData() and node.isVisible():
# TODO: Find a better way to handle this
#if node.getBoundingBoxMesh():
# renderer.queueNode(scene.getRoot(), mesh = node.getBoundingBoxMesh(),mode = Renderer.RenderLines)
uniforms = {} uniforms = {}
if self._extruders_model.rowCount() > 0: if not multi_extrusion:
if global_container_stack:
material = global_container_stack.findContainer({ "type": "material" })
material_color = material.getMetaDataEntry("color_code", default = self._extruders_model.defaultColors[0]) if material else self._extruders_model.defaultColors[0]
else:
material_color = self._extruders_model.defaultColors[0]
else:
# Get color to render this mesh in from ExtrudersModel # Get color to render this mesh in from ExtrudersModel
extruder_index = 0 extruder_index = 0
extruder_id = node.callDecoration("getActiveExtruder") extruder_id = node.callDecoration("getActiveExtruder")
if extruder_id: if extruder_id:
extruder_index = max(0, self._extruders_model.find("id", extruder_id)) extruder_index = max(0, self._extruders_model.find("id", extruder_id))
extruder_color = self._extruders_model.getItem(extruder_index)["colour"] material_color = self._extruders_model.getItem(extruder_index)["color"]
try: try:
# Colors are passed as rgb hex strings (eg "#ffffff"), and the shader needs # Colors are passed as rgb hex strings (eg "#ffffff"), and the shader needs
# an rgba list of floats (eg [1.0, 1.0, 1.0, 1.0]) # an rgba list of floats (eg [1.0, 1.0, 1.0, 1.0])
uniforms["diffuse_color"] = [ uniforms["diffuse_color"] = [
int(extruder_color[1:3], 16) / 255, int(material_color[1:3], 16) / 255,
int(extruder_color[3:5], 16) / 255, int(material_color[3:5], 16) / 255,
int(extruder_color[5:7], 16) / 255, int(material_color[5:7], 16) / 255,
1.0 1.0
] ]
except ValueError: except ValueError:
@ -84,7 +94,7 @@ class SolidView(View):
renderer.queueNode(node, shader = self._enabled_shader, uniforms = uniforms) renderer.queueNode(node, shader = self._enabled_shader, uniforms = uniforms)
else: else:
renderer.queueNode(node, material = self._enabled_shader, uniforms = uniforms) renderer.queueNode(node, material = self._enabled_shader, uniforms = uniforms)
if node.callDecoration("isGroup"): if node.callDecoration("isGroup") and Selection.isSelected(node):
renderer.queueNode(scene.getRoot(), mesh = node.getBoundingBoxMesh(), mode = Renderer.RenderLines) renderer.queueNode(scene.getRoot(), mesh = node.getBoundingBoxMesh(), mode = Renderer.RenderLines)
def endRendering(self): def endRendering(self):

View File

@ -32,16 +32,16 @@ UM.Dialog
} }
text: { text: {
if (manager.progress == 0) if (manager.firmwareUpdateCompleteStatus)
{
//: Firmware update status label
return catalog.i18nc("@label","Starting firmware update, this may take a while.")
}
else if (manager.progress > 99)
{ {
//: Firmware update status label //: Firmware update status label
return catalog.i18nc("@label","Firmware update completed.") return catalog.i18nc("@label","Firmware update completed.")
} }
else if (manager.progress == 0)
{
//: Firmware update status label
return catalog.i18nc("@label","Starting firmware update, this may take a while.")
}
else else
{ {
//: Firmware update status label //: Firmware update status label
@ -55,10 +55,10 @@ UM.Dialog
ProgressBar ProgressBar
{ {
id: prog id: prog
value: manager.progress value: manager.firmwareUpdateCompleteStatus ? 100 : manager.progress
minimumValue: 0 minimumValue: 0
maximumValue: 100 maximumValue: 100
indeterminate: manager.progress < 100 indeterminate: (manager.progress < 1) && (!manager.firmwareUpdateCompleteStatus)
anchors anchors
{ {
left: parent.left; left: parent.left;
@ -79,7 +79,7 @@ UM.Dialog
Button Button
{ {
text: catalog.i18nc("@action:button","Close"); text: catalog.i18nc("@action:button","Close");
enabled: manager.progress >= 100; enabled: manager.firmwareUpdateCompleteStatus;
onClicked: base.visible = false; onClicked: base.visible = false;
} }
] ]

View File

@ -21,12 +21,14 @@ catalog = i18nCatalog("cura")
class USBPrinterOutputDevice(PrinterOutputDevice): class USBPrinterOutputDevice(PrinterOutputDevice):
def __init__(self, serial_port): def __init__(self, serial_port):
super().__init__(serial_port) super().__init__(serial_port)
self.setName(catalog.i18nc("@item:inmenu", "USB printing")) self.setName(catalog.i18nc("@item:inmenu", "USB printing"))
self.setShortDescription(catalog.i18nc("@action:button", "Print via USB")) self.setShortDescription(catalog.i18nc("@action:button", "Print via USB"))
self.setDescription(catalog.i18nc("@info:tooltip", "Print via USB")) self.setDescription(catalog.i18nc("@info:tooltip", "Print via USB"))
self.setIconName("print") self.setIconName("print")
self.setConnectionText(catalog.i18nc("@info:status", "Connected via USB"))
self._serial = None self._serial = None
self._serial_port = serial_port self._serial_port = serial_port
@ -85,12 +87,14 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
self._updating_firmware = False self._updating_firmware = False
self._firmware_file_name = None self._firmware_file_name = None
self._firmware_update_finished = False
self._error_message = None self._error_message = None
onError = pyqtSignal() onError = pyqtSignal()
firmwareUpdateComplete = pyqtSignal() firmwareUpdateComplete = pyqtSignal()
firmwareUpdateChange = pyqtSignal()
endstopStateChanged = pyqtSignal(str ,bool, arguments = ["key","state"]) endstopStateChanged = pyqtSignal(str ,bool, arguments = ["key","state"])
@ -170,6 +174,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
## Private function (threaded) that actually uploads the firmware. ## Private function (threaded) that actually uploads the firmware.
def _updateFirmware(self): def _updateFirmware(self):
self.setProgress(0, 100) self.setProgress(0, 100)
self._firmware_update_finished = False
if self._connection_state != ConnectionState.closed: if self._connection_state != ConnectionState.closed:
self.close() self.close()
@ -177,10 +182,11 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
if len(hex_file) == 0: if len(hex_file) == 0:
Logger.log("e", "Unable to read provided hex file. Could not update firmware") Logger.log("e", "Unable to read provided hex file. Could not update firmware")
self._updateFirmware_completed()
return return
programmer = stk500v2.Stk500v2() programmer = stk500v2.Stk500v2()
programmer.progressCallback = self.setProgress programmer.progress_callback = self.setProgress
try: try:
programmer.connect(self._serial_port) programmer.connect(self._serial_port)
@ -192,6 +198,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
if not programmer.isConnected(): if not programmer.isConnected():
Logger.log("e", "Unable to connect with serial. Could not update firmware") Logger.log("e", "Unable to connect with serial. Could not update firmware")
self._updateFirmware_completed()
return return
self._updating_firmware = True self._updating_firmware = True
@ -201,14 +208,22 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
self._updating_firmware = False self._updating_firmware = False
except Exception as e: except Exception as e:
Logger.log("e", "Exception while trying to update firmware %s" %e) Logger.log("e", "Exception while trying to update firmware %s" %e)
self._updating_firmware = False self._updateFirmware_completed()
return return
programmer.close() programmer.close()
self.setProgress(100, 100) self._updateFirmware_completed()
return
## Private function which makes sure that firmware update process has completed/ended
def _updateFirmware_completed(self):
self.setProgress(100, 100)
self._firmware_update_finished = True
self.resetFirmwareUpdate(update_has_finished=True)
self.firmwareUpdateComplete.emit() self.firmwareUpdateComplete.emit()
return
## Upload new firmware to machine ## Upload new firmware to machine
# \param filename full path of firmware file to be uploaded # \param filename full path of firmware file to be uploaded
def updateFirmware(self, file_name): def updateFirmware(self, file_name):
@ -216,6 +231,14 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
self._firmware_file_name = file_name self._firmware_file_name = file_name
self._update_firmware_thread.start() self._update_firmware_thread.start()
@property
def firmwareUpdateFinished(self):
return self._firmware_update_finished
def resetFirmwareUpdate(self, update_has_finished = False):
self._firmware_update_finished = update_has_finished
self.firmwareUpdateChange.emit()
@pyqtSlot() @pyqtSlot()
def startPollEndstop(self): def startPollEndstop(self):
if not self._poll_endstop: if not self._poll_endstop:
@ -270,11 +293,13 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
while timeout_time > time.time(): while timeout_time > time.time():
line = self._readline() line = self._readline()
if line is None: if line is None:
Logger.log("d", "No response from serial connection received.")
# Something went wrong with reading, could be that close was called. # Something went wrong with reading, could be that close was called.
self.setConnectionState(ConnectionState.closed) self.setConnectionState(ConnectionState.closed)
return return
if b"T:" in line: if b"T:" in line:
Logger.log("d", "Correct response for auto-baudrate detection received.")
self._serial.timeout = 0.5 self._serial.timeout = 0.5
sucesfull_responses += 1 sucesfull_responses += 1
if sucesfull_responses >= self._required_responses_auto_baud: if sucesfull_responses >= self._required_responses_auto_baud:

View File

@ -48,15 +48,24 @@ class USBPrinterOutputDeviceManager(QObject, OutputDevicePlugin, Extension):
connectionStateChanged = pyqtSignal() connectionStateChanged = pyqtSignal()
progressChanged = pyqtSignal() progressChanged = pyqtSignal()
firmwareUpdateChange = pyqtSignal()
@pyqtProperty(float, notify = progressChanged) @pyqtProperty(float, notify = progressChanged)
def progress(self): def progress(self):
progress = 0 progress = 0
for printer_name, device in self._usb_output_devices.items(): # TODO: @UnusedVariable "printer_name" for printer_name, device in self._usb_output_devices.items(): # TODO: @UnusedVariable "printer_name"
progress += device.progress progress += device.progress
return progress / len(self._usb_output_devices) return progress / len(self._usb_output_devices)
## Return True if all printers finished firmware update
@pyqtProperty(float, notify = firmwareUpdateChange)
def firmwareUpdateCompleteStatus(self):
complete = True
for printer_name, device in self._usb_output_devices.items(): # TODO: @UnusedVariable "printer_name"
if not device.firmwareUpdateFinished:
complete = False
return complete
def start(self): def start(self):
self._check_updates = True self._check_updates = True
self._update_thread.start() self._update_thread.start()
@ -93,13 +102,20 @@ class USBPrinterOutputDeviceManager(QObject, OutputDevicePlugin, Extension):
Message(i18n_catalog.i18nc("@info","Cannot update firmware, there were no connected printers found.")).show() Message(i18n_catalog.i18nc("@info","Cannot update firmware, there were no connected printers found.")).show()
return return
for printer_connection in self._usb_output_devices:
self._usb_output_devices[printer_connection].resetFirmwareUpdate()
self.spawnFirmwareInterface("") self.spawnFirmwareInterface("")
for printer_connection in self._usb_output_devices: for printer_connection in self._usb_output_devices:
try: try:
self._usb_output_devices[printer_connection].updateFirmware(Resources.getPath(CuraApplication.ResourceTypes.Firmware, self._getDefaultFirmwareName())) self._usb_output_devices[printer_connection].updateFirmware(Resources.getPath(CuraApplication.ResourceTypes.Firmware, self._getDefaultFirmwareName()))
except FileNotFoundError: except FileNotFoundError:
# Should only happen in dev environments where the resources/firmware folder is absent.
self._usb_output_devices[printer_connection].setProgress(100, 100) self._usb_output_devices[printer_connection].setProgress(100, 100)
Logger.log("w", "No firmware found for printer %s", printer_connection) Logger.log("w", "No firmware found for printer %s called '%s'" %(printer_connection, self._getDefaultFirmwareName()))
Message(i18n_catalog.i18nc("@info",
"Could not find firmware required for the printer at %s.") % printer_connection).show()
self._firmware_view.close()
continue continue
@pyqtSlot(str, result = bool) @pyqtSlot(str, result = bool)
@ -110,7 +126,7 @@ class USBPrinterOutputDeviceManager(QObject, OutputDevicePlugin, Extension):
self._usb_output_devices[serial_port].updateFirmware(Resources.getPath(CuraApplication.ResourceTypes.Firmware, self._getDefaultFirmwareName())) self._usb_output_devices[serial_port].updateFirmware(Resources.getPath(CuraApplication.ResourceTypes.Firmware, self._getDefaultFirmwareName()))
except FileNotFoundError: except FileNotFoundError:
self._firmware_view.close() self._firmware_view.close()
Logger.log("e", "Could not find firmware required for this machine") Logger.log("e", "Could not find firmware required for this machine called '%s'" %(self._getDefaultFirmwareName()))
return False return False
return True return True
return False return False
@ -150,6 +166,7 @@ class USBPrinterOutputDeviceManager(QObject, OutputDevicePlugin, Extension):
"bq_hephestos_2" : "MarlinHephestos2.hex", "bq_hephestos_2" : "MarlinHephestos2.hex",
"ultimaker_original" : "MarlinUltimaker-{baudrate}.hex", "ultimaker_original" : "MarlinUltimaker-{baudrate}.hex",
"ultimaker_original_plus" : "MarlinUltimaker-UMOP-{baudrate}.hex", "ultimaker_original_plus" : "MarlinUltimaker-UMOP-{baudrate}.hex",
"ultimaker_original_dual" : "MarlinUltimaker-{baudrate}-dual.hex",
"ultimaker2" : "MarlinUltimaker2.hex", "ultimaker2" : "MarlinUltimaker2.hex",
"ultimaker2_go" : "MarlinUltimaker2go.hex", "ultimaker2_go" : "MarlinUltimaker2go.hex",
"ultimaker2_plus" : "MarlinUltimaker2plus.hex", "ultimaker2_plus" : "MarlinUltimaker2plus.hex",
@ -157,6 +174,7 @@ class USBPrinterOutputDeviceManager(QObject, OutputDevicePlugin, Extension):
"ultimaker2_extended_plus" : "MarlinUltimaker2extended-plus.hex", "ultimaker2_extended_plus" : "MarlinUltimaker2extended-plus.hex",
} }
machine_with_heated_bed = {"ultimaker_original" : "MarlinUltimaker-HBK-{baudrate}.hex", machine_with_heated_bed = {"ultimaker_original" : "MarlinUltimaker-HBK-{baudrate}.hex",
"ultimaker_original_dual" : "MarlinUltimaker-HBK-{baudrate}-dual.hex",
} }
##TODO: Add check for multiple extruders ##TODO: Add check for multiple extruders
hex_file = None hex_file = None
@ -200,6 +218,7 @@ class USBPrinterOutputDeviceManager(QObject, OutputDevicePlugin, Extension):
device.connectionStateChanged.connect(self._onConnectionStateChanged) device.connectionStateChanged.connect(self._onConnectionStateChanged)
device.connect() device.connect()
device.progressChanged.connect(self.progressChanged) device.progressChanged.connect(self.progressChanged)
device.firmwareUpdateChange.connect(self.firmwareUpdateChange)
self._usb_output_devices[serial_port] = device self._usb_output_devices[serial_port] = device
## If one of the states of the connected devices change, we might need to add / remove them from the global list. ## If one of the states of the connected devices change, we might need to add / remove them from the global list.

View File

@ -10,7 +10,7 @@ catalog = i18nCatalog("cura")
class BedLevelMachineAction(MachineAction): class BedLevelMachineAction(MachineAction):
def __init__(self): def __init__(self):
super().__init__("BedLevel", catalog.i18nc("@action", "Level bed")) super().__init__("BedLevel", catalog.i18nc("@action", "Level build plate"))
self._qml_url = "BedLevelMachineAction.qml" self._qml_url = "BedLevelMachineAction.qml"
self._bed_level_position = 0 self._bed_level_position = 0
@ -18,6 +18,11 @@ class BedLevelMachineAction(MachineAction):
pass pass
def _reset(self): def _reset(self):
self._bed_level_position = 0
pass
@pyqtSlot()
def startBedLeveling(self):
self._bed_level_position = 0 self._bed_level_position = 0
printer_output_devices = self._getPrinterOutputDevices() printer_output_devices = self._getPrinterOutputDevices()
if printer_output_devices: if printer_output_devices:
@ -52,4 +57,5 @@ class BedLevelMachineAction(MachineAction):
output_device.moveHead(0, 0, -3) output_device.moveHead(0, 0, -3)
self._bed_level_position += 1 self._bed_level_position += 1
elif self._bed_level_position >= 3: elif self._bed_level_position >= 3:
output_device.sendCommand("M18") # Turn off all motors so the user can move the axes
self.setFinished() self.setFinished()

View File

@ -24,7 +24,7 @@ Cura.MachineAction
{ {
id: pageTitle id: pageTitle
width: parent.width width: parent.width
text: catalog.i18nc("@title", "Bed Leveling") text: catalog.i18nc("@title", "Build Plate Leveling")
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
font.pointSize: 18; font.pointSize: 18;
} }
@ -44,40 +44,40 @@ Cura.MachineAction
anchors.topMargin: UM.Theme.getSize("default_margin").height anchors.topMargin: UM.Theme.getSize("default_margin").height
width: parent.width width: parent.width
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
text: catalog.i18nc("@label", "For every position; insert a piece of paper under the nozzle and adjust the print bed height. The print bed height is right when the paper is slightly gripped by the tip of the nozzle.") text: catalog.i18nc("@label", "For every position; insert a piece of paper under the nozzle and adjust the print build plate height. The print build plate height is right when the paper is slightly gripped by the tip of the nozzle.")
} }
Item Row
{ {
id: bedlevelingWrapper id: bedlevelingWrapper
anchors.top: bedlevelingText.bottom anchors.top: bedlevelingText.bottom
anchors.topMargin: UM.Theme.getSize("default_margin").height anchors.topMargin: UM.Theme.getSize("default_margin").height
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
height: skipBedlevelingButton.height width: childrenRect.width
width: bedlevelingButton.width + skipBedlevelingButton.width + UM.Theme.getSize("default_margin").height < bedLevelMachineAction.width ? bedlevelingButton.width + skipBedlevelingButton.width + UM.Theme.getSize("default_margin").height : bedLevelMachineAction.width spacing: UM.Theme.getSize("default_margin").width
Button Button
{ {
id: bedlevelingButton id: startBedLevelingButton
anchors.top: parent.top text: catalog.i18nc("@action:button","Start Build Plate Leveling")
anchors.left: parent.left
text: catalog.i18nc("@action:button","Move to Next Position");
onClicked: onClicked:
{ {
manager.moveToNextLevelPosition() startBedLevelingButton.visible = false;
bedlevelingButton.visible = true;
checkupMachineAction.heatupHotendStarted = false;
checkupMachineAction.heatupBedStarted = false;
manager.startCheck();
} }
} }
Button Button
{ {
id: skipBedlevelingButton id: bedlevelingButton
anchors.top: parent.width < bedLevelMachineAction.width ? parent.top : bedlevelingButton.bottom text: catalog.i18nc("@action:button","Move to Next Position")
anchors.topMargin: parent.width < bedLevelMachineAction.width ? 0 : UM.Theme.getSize("default_margin").height/2 visible: false
anchors.left: parent.width < bedLevelMachineAction.width ? bedlevelingButton.right : parent.left
anchors.leftMargin: parent.width < bedLevelMachineAction.width ? UM.Theme.getSize("default_margin").width : 0
text: catalog.i18nc("@action:button","Skip bed leveling");
onClicked: onClicked:
{ {
manager.setFinished() manager.moveToNextLevelPosition();
} }
} }
} }

View File

@ -155,6 +155,7 @@ class UMOCheckupMachineAction(MachineAction):
if output_devices: if output_devices:
self._output_device = output_devices[0] self._output_device = output_devices[0]
try: try:
self._output_device.sendCommand("M18") # Turn off all motors so the user can move the axes
self._output_device.startPollEndstop() self._output_device.startPollEndstop()
self._output_device.bedTemperatureChanged.connect(self.bedTemperatureChanged) self._output_device.bedTemperatureChanged.connect(self.bedTemperatureChanged)
self._output_device.hotendTemperaturesChanged.connect(self.hotendTemperatureChanged) self._output_device.hotendTemperaturesChanged.connect(self.hotendTemperatureChanged)

View File

@ -39,38 +39,26 @@ Cura.MachineAction
text: catalog.i18nc("@label", "It's a good idea to do a few sanity checks on your Ultimaker. You can skip this step if you know your machine is functional"); text: catalog.i18nc("@label", "It's a good idea to do a few sanity checks on your Ultimaker. You can skip this step if you know your machine is functional");
} }
Item Row
{ {
id: startStopButtons id: startStopButtons
anchors.top: pageDescription.bottom anchors.top: pageDescription.bottom
anchors.topMargin: UM.Theme.getSize("default_margin").height anchors.topMargin: UM.Theme.getSize("default_margin").height
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
height: childrenRect.height width: childrenRect.width
width: startCheckButton.width + skipCheckButton.width + UM.Theme.getSize("default_margin").height < checkupMachineAction.width ? startCheckButton.width + skipCheckButton.width + UM.Theme.getSize("default_margin").height : checkupMachineAction.width spacing: UM.Theme.getSize("default_margin").width
Button Button
{ {
id: startCheckButton id: startCheckButton
anchors.top: parent.top
anchors.left: parent.left
text: catalog.i18nc("@action:button","Start Printer Check"); text: catalog.i18nc("@action:button","Start Printer Check");
onClicked: onClicked:
{ {
checkupMachineAction.heatupHotendStarted = false checkupMachineAction.heatupHotendStarted = false;
checkupMachineAction.heatupBedStarted = false checkupMachineAction.heatupBedStarted = false;
manager.startCheck() manager.startCheck();
startCheckButton.visible = false;
} }
} }
Button
{
id: skipCheckButton
anchors.top: parent.width < checkupMachineAction.width ? parent.top : startCheckButton.bottom
anchors.topMargin: parent.width < checkupMachineAction.width ? 0 : UM.Theme.getSize("default_margin").height/2
anchors.left: parent.width < checkupMachineAction.width ? startCheckButton.right : parent.left
anchors.leftMargin: parent.width < checkupMachineAction.width ? UM.Theme.getSize("default_margin").width : 0
text: catalog.i18nc("@action:button", "Skip Printer Check");
onClicked: manager.setFinished()
}
} }
Item Item
@ -232,7 +220,7 @@ Cura.MachineAction
anchors.left: parent.left anchors.left: parent.left
anchors.top: nozzleTempLabel.bottom anchors.top: nozzleTempLabel.bottom
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
text: catalog.i18nc("@label","Bed temperature check:") text: catalog.i18nc("@label","Build plate temperature check:")
visible: checkupMachineAction.usbConnected && manager.hasHeatedBed visible: checkupMachineAction.usbConnected && manager.hasHeatedBed
} }

View File

@ -12,6 +12,9 @@ class UMOUpgradeSelection(MachineAction):
super().__init__("UMOUpgradeSelection", catalog.i18nc("@action", "Select upgrades")) super().__init__("UMOUpgradeSelection", catalog.i18nc("@action", "Select upgrades"))
self._qml_url = "UMOUpgradeSelectionMachineAction.qml" self._qml_url = "UMOUpgradeSelectionMachineAction.qml"
def _reset(self):
self.heatedBedChanged.emit()
heatedBedChanged = pyqtSignal() heatedBedChanged = pyqtSignal()
@pyqtProperty(bool, notify = heatedBedChanged) @pyqtProperty(bool, notify = heatedBedChanged)

View File

@ -42,9 +42,9 @@ Cura.MachineAction
anchors.top: pageDescription.bottom anchors.top: pageDescription.bottom
anchors.topMargin: UM.Theme.getSize("default_margin").height anchors.topMargin: UM.Theme.getSize("default_margin").height
text: catalog.i18nc("@label", "Heated bed (official kit or self-built)") text: catalog.i18nc("@label", "Heated Build Plate (official kit or self-built)")
checked: manager.hasHeatedBed checked: manager.hasHeatedBed
onClicked: manager.hasHeatedBed ? manager.removeHeatedBed() : manager.addHeatedBed() onClicked: checked ? manager.addHeatedBed() : manager.removeHeatedBed()
} }
UM.I18nCatalog { id: catalog; name: "cura"; } UM.I18nCatalog { id: catalog; name: "cura"; }

View File

@ -56,29 +56,21 @@ Cura.MachineAction
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
text: catalog.i18nc("@label", "Cura requires these new features and thus your firmware will most likely need to be upgraded. You can do so now."); text: catalog.i18nc("@label", "Cura requires these new features and thus your firmware will most likely need to be upgraded. You can do so now.");
} }
Item Row
{ {
anchors.top: upgradeText2.bottom anchors.top: upgradeText2.bottom
anchors.topMargin: UM.Theme.getSize("default_margin").height anchors.topMargin: UM.Theme.getSize("default_margin").height
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
width: upgradeButton.width + skipUpgradeButton.width + UM.Theme.getSize("default_margin").height < upgradeFirmwareMachineAction.width ? upgradeButton.width + skipUpgradeButton.width + UM.Theme.getSize("default_margin").height : upgradeFirmwareMachineAction.width width: childrenRect.width
spacing: UM.Theme.getSize("default_margin").width
Button Button
{ {
id: upgradeButton id: upgradeButton
anchors.top: parent.top
anchors.left: parent.left
text: catalog.i18nc("@action:button","Upgrade to Marlin Firmware"); text: catalog.i18nc("@action:button","Upgrade to Marlin Firmware");
onClicked: Cura.USBPrinterManager.updateAllFirmware() onClicked:
}
Button
{ {
id: skipUpgradeButton Cura.USBPrinterManager.updateAllFirmware()
anchors.top: parent.width < upgradeFirmwareMachineAction.width ? parent.top : upgradeButton.bottom }
anchors.topMargin: parent.width < upgradeFirmwareMachineAction.width ? 0 : UM.Theme.getSize("default_margin").height / 2
anchors.left: parent.width < upgradeFirmwareMachineAction.width ? upgradeButton.right : parent.left
anchors.leftMargin: parent.width < upgradeFirmwareMachineAction.width ? UM.Theme.getSize("default_margin").width : 0
text: catalog.i18nc("@action:button", "Skip Upgrade");
onClicked: manager.setFinished()
} }
} }
} }

View File

@ -73,13 +73,21 @@ class MachineInstance:
config.set("general", "version", "2") # Hard-code version 2, since if this number changes the programmer MUST change this entire function. config.set("general", "version", "2") # Hard-code version 2, since if this number changes the programmer MUST change this entire function.
import VersionUpgrade21to22 # Import here to prevent circular dependencies. import VersionUpgrade21to22 # Import here to prevent circular dependencies.
has_machine_qualities = self._type_name in VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.machinesWithMachineQuality()
type_name = VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translatePrinter(self._type_name) type_name = VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translatePrinter(self._type_name)
active_profile = VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translateProfile(self._active_profile_name)
active_material = VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translateProfile(self._active_material_name) active_material = VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translateProfile(self._active_material_name)
variant = VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translateVariant(self._variant_name, type_name) variant = VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translateVariant(self._variant_name, type_name)
variant_materials = VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translateVariantForMaterials(self._variant_name, type_name)
active_profile = VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translateProfile(self._active_profile_name)
if has_machine_qualities: #This machine now has machine-quality profiles.
active_profile += "_" + active_material + "_" + variant
active_material += "_" + variant_materials #That means that the profile was split into multiple.
current_settings = "empty" #The profile didn't know the definition ID when it was upgraded, so it will have been invalid. Sorry, your current settings are lost now.
else:
current_settings = self._name + "_current_settings"
containers = [ containers = [
self._name + "_current_settings", current_settings,
active_profile, active_profile,
active_material, active_material,
variant, variant,
@ -97,4 +105,4 @@ class MachineInstance:
output = io.StringIO() output = io.StringIO()
config.write(output) config.write(output)
return self._filename, output.getvalue() return [self._filename], [output.getvalue()]

View File

@ -77,4 +77,4 @@ class Preferences:
#Output the result as a string. #Output the result as a string.
output = io.StringIO() output = io.StringIO()
self._config.write(output) self._config.write(output)
return self._filename, output.getvalue() return [self._filename], [output.getvalue()]

View File

@ -91,7 +91,7 @@ class Profile:
translated_machine = VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translatePrinter(self._machine_type_id) translated_machine = VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translatePrinter(self._machine_type_id)
config.set("general", "definition", translated_machine) config.set("general", "definition", translated_machine)
else: else:
config.set("general", "definition", "fdmprinter") config.set("general", "definition", "fdmprinter") #In this case, the machine definition is unknown, and it might now have machine-specific profiles, in which case this will fail.
config.add_section("metadata") config.add_section("metadata")
if self._type: if self._type:
@ -105,8 +105,6 @@ class Profile:
config.set("metadata", "variant", VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translateVariant(self._machine_variant_name, self._machine_type_id)) config.set("metadata", "variant", VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translateVariant(self._machine_variant_name, self._machine_type_id))
else: else:
config.set("metadata", "variant", self._machine_variant_name) config.set("metadata", "variant", self._machine_variant_name)
if self._material_name and self._type != "material":
config.set("metadata", "material", self._material_name)
if self._settings: if self._settings:
VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translateSettings(self._settings) VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translateSettings(self._settings)
@ -128,6 +126,34 @@ class Profile:
for item in disabled_settings_defaults[1:]: for item in disabled_settings_defaults[1:]:
disabled_defaults_string += "," + str(item) disabled_defaults_string += "," + str(item)
#Material metadata may cause the file to split, so do it last to minimise processing time (do more with the copy).
filenames = []
configs = []
if self._material_name and self._type != "material":
config.set("metadata", "material", self._material_name)
filenames.append(self._filename)
configs.append(config)
elif self._type != "material" and self._machine_type_id in VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.machinesWithMachineQuality():
#Split this profile into multiple profiles, one for each material.
_new_materials = VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.machinesWithMachineQuality()[self._machine_type_id]["materials"]
_new_variants = VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.machinesWithMachineQuality()[self._machine_type_id]["variants"]
translated_machine = VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translatePrinter(self._machine_type_id)
for material_id in _new_materials:
for variant_id in _new_variants:
variant_id_new = VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translateVariant(variant_id, translated_machine)
filenames.append("{profile}_{material}_{variant}".format(profile = self._filename, material = material_id, variant = variant_id_new))
config_copy = configparser.ConfigParser(interpolation = None)
config_copy.read_dict(config) #Copy the config to a new ConfigParser instance.
variant_id_new_materials = VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translateVariantForMaterials(variant_id, translated_machine)
config_copy.set("metadata", "material", "{material}_{variant}".format(material = material_id, variant = variant_id_new_materials))
configs.append(config_copy)
else:
configs.append(config)
filenames.append(self._filename)
outputs = []
for config in configs:
output = io.StringIO() output = io.StringIO()
config.write(output) config.write(output)
return self._filename, output.getvalue() outputs.append(output.getvalue())
return filenames, outputs

View File

@ -9,6 +9,29 @@ from . import MachineInstance # To upgrade machine instances.
from . import Preferences #To upgrade preferences. from . import Preferences #To upgrade preferences.
from . import Profile # To upgrade profiles. from . import Profile # To upgrade profiles.
## Which machines have material-specific profiles in the new version?
#
# These are the 2.1 machine identities with "has_machine_materials": true in
# their definitions in Cura 2.2. So these are the machines for which profiles
# need to split into multiple profiles, one for each material and variant.
#
# Each machine has the materials and variants listed in which it needs to
# split, since those might be different per machine.
#
# This should contain the definition as they are stated in the profiles. The
# inheritance structure cannot be found at this stage, since the definitions
# may have changed in later versions than 2.2.
_machines_with_machine_quality = {
"ultimaker2plus": {
"materials": { "generic_abs", "generic_cpe", "generic_pla", "generic_pva" },
"variants": { "0.25 mm", "0.4 mm", "0.6 mm", "0.8 mm" }
},
"ultimaker2_extended_plus": {
"materials": { "generic_abs", "generic_cpe", "generic_pla", "generic_pva" },
"variants": { "0.25 mm", "0.4 mm", "0.6 mm", "0.8 mm" }
}
}
## How to translate printer names from the old version to the new. ## How to translate printer names from the old version to the new.
_printer_translations = { _printer_translations = {
"ultimaker2plus": "ultimaker2_plus" "ultimaker2plus": "ultimaker2_plus"
@ -34,7 +57,14 @@ _setting_name_translations = {
"skirt_line_width": "skirt_brim_line_width", "skirt_line_width": "skirt_brim_line_width",
"skirt_minimal_length": "skirt_brim_minimal_length", "skirt_minimal_length": "skirt_brim_minimal_length",
"skirt_speed": "skirt_brim_speed", "skirt_speed": "skirt_brim_speed",
"speed_support_lines": "speed_support_infill" "speed_support_lines": "speed_support_infill",
"speed_support_roof": "speed_support_interface",
"support_roof_density": "support_interface_density",
"support_roof_enable": "support_interface_enable",
"support_roof_extruder_nr": "support_interface_extruder_nr",
"support_roof_line_distance": "support_interface_line_distance",
"support_roof_line_width": "support_interface_line_width",
"support_roof_pattern": "support_interface_pattern"
} }
## How to translate variants of specific machines from the old version to the ## How to translate variants of specific machines from the old version to the
@ -54,6 +84,24 @@ _variant_translations = {
} }
} }
## Cura 2.2's material profiles use a different naming scheme for variants.
#
# Getting pretty stressed out by this sort of thing...
_variant_translations_materials = {
"ultimaker2_plus": {
"0.25 mm": "ultimaker2_plus_0.25_mm",
"0.4 mm": "ultimaker2_plus_0.4_mm",
"0.6 mm": "ultimaker2_plus_0.6_mm",
"0.8 mm": "ultimaker2_plus_0.8_mm"
},
"ultimaker2_extended_plus": {
"0.25 mm": "ultimaker2_plus_0.25_mm",
"0.4 mm": "ultimaker2_plus_0.4_mm",
"0.6 mm": "ultimaker2_plus_0.6_mm",
"0.8 mm": "ultimaker2_plus_0.8_mm"
}
}
## Converts configuration from Cura 2.1's file formats to Cura 2.2's. ## Converts configuration from Cura 2.1's file formats to Cura 2.2's.
# #
# It converts the machine instances and profiles. # It converts the machine instances and profiles.
@ -70,6 +118,13 @@ class VersionUpgrade21to22(VersionUpgrade):
parser.read_string(serialised) parser.read_string(serialised)
return int(parser.get("general", "version")) #Explicitly give an exception when this fails. That means that the file format is not recognised. return int(parser.get("general", "version")) #Explicitly give an exception when this fails. That means that the file format is not recognised.
## Gets a set of the machines which now have per-material quality profiles.
#
# \return A set of machine identifiers.
@staticmethod
def machinesWithMachineQuality():
return _machines_with_machine_quality
## Converts machine instances from format version 1 to version 2. ## Converts machine instances from format version 1 to version 2.
# #
# \param serialised The serialised machine instance in version 1. # \param serialised The serialised machine instance in version 1.
@ -146,32 +201,11 @@ class VersionUpgrade21to22(VersionUpgrade):
for key, value in settings.items(): for key, value in settings.items():
if key == "fill_perimeter_gaps": #Setting is removed. if key == "fill_perimeter_gaps": #Setting is removed.
del settings[key] del settings[key]
elif key == "remove_overlapping_walls_0_enabled": #Setting is functionally replaced.
del settings[key]
settings["travel_compensate_overlapping_walls_0_enabled"] = value
elif key == "remove_overlapping_walls_enabled": #Setting is functionally replaced.
del settings[key]
settings["travel_compensate_overlapping_walls_enabled"] = value
elif key == "remove_overlapping_walls_x_enabled": #Setting is functionally replaced.
del settings[key]
settings["travel_compensate_overlapping_walls_x_enabled"] = value
elif key == "retraction_combing": #Combing was made into an enum instead of a boolean. elif key == "retraction_combing": #Combing was made into an enum instead of a boolean.
settings[key] = "off" if (value == "False") else "all" settings[key] = "off" if (value == "False") else "all"
elif key == "retraction_hop": #Setting key was changed. elif key in _setting_name_translations:
del settings[key] del settings[key]
settings["retraction_hop_enabled"] = value settings[_setting_name_translations[key]] = value
elif key == "skirt_minimal_length": #Setting key was changed.
del settings[key]
settings["skirt_brim_minimal_length"] = value
elif key == "skirt_line_width": #Setting key was changed.
del settings[key]
settings["skirt_brim_line_width"] = value
elif key == "skirt_speed": #Setting key was changed.
del settings[key]
settings["skirt_brim_speed"] = value
elif key == "speed_support_lines": #Setting key was changed.
del settings[key]
settings["speed_support_infill"] = value
return settings return settings
## Translates a setting name for the change from Cura 2.1 to 2.2. ## Translates a setting name for the change from Cura 2.1 to 2.2.
@ -195,3 +229,17 @@ class VersionUpgrade21to22(VersionUpgrade):
if machine in _variant_translations and variant in _variant_translations[machine]: if machine in _variant_translations and variant in _variant_translations[machine]:
return _variant_translations[machine][variant] return _variant_translations[machine][variant]
return variant return variant
## Translates a variant name for the change from Cura 2.1 to 2.2 in
# material profiles.
#
# \param variant The name of the variant in Cura 2.1.
# \param machine The name of the machine this variant is part of in Cura
# 2.2's naming.
# \return The name of the corresponding variant for in material profiles
# in Cura 2.2.
@staticmethod
def translateVariantForMaterials(variant, machine):
if machine in _variant_translations_materials and variant in _variant_translations_materials[machine]:
return _variant_translations_materials[machine][variant]
return variant

View File

@ -161,7 +161,7 @@
}, },
"platform_adhesion": "platform_adhesion":
{ {
"label": "Platform Adhesion", "label": "Build Plate Adhesion",
"type": "category", "type": "category",
"icon": "category_adhesion", "icon": "category_adhesion",
"description": "Adhesion", "description": "Adhesion",

File diff suppressed because it is too large Load Diff

View File

@ -54,13 +54,13 @@
"speed_travel": { "default_value": 150 }, "speed_travel": { "default_value": 150 },
"speed_layer_0": { "speed_layer_0": {
"minimum_value": "0.1", "minimum_value": "0.1",
"default": 15.0 "default_value": 15.0
}, },
"infill_overlap": { "default": 10 }, "infill_overlap": { "default_value": 10 },
"cool_fan_enabled": { "default": false }, "cool_fan_enabled": { "default_value": false },
"cool_fan_speed": { "default": 0 }, "cool_fan_speed": { "default_value": 0 },
"skirt_line_count": { "default": 3 }, "skirt_line_count": { "default_value": 3 },
"skirt_gap": { "default": 4 }, "skirt_gap": { "default_value": 4 },
"skirt_brim_minimal_length": { "default": 200 } "skirt_brim_minimal_length": { "default_value": 200 }
} }
} }

View File

@ -7,6 +7,7 @@
"visible": true, "visible": true,
"author": "Calvindog717", "author": "Calvindog717",
"manufacturer": "PrintrBot", "manufacturer": "PrintrBot",
"category": "Other",
"file_formats": "text/x-gcode" "file_formats": "text/x-gcode"
}, },

View File

@ -7,5 +7,16 @@
"author": "Ultimaker", "author": "Ultimaker",
"manufacturer": "Ultimaker", "manufacturer": "Ultimaker",
"visible": false "visible": false
},
"overrides": {
"material_print_temperature": {
"minimum_value": "0"
},
"material_bed_temperature": {
"minimum_value": "0"
},
"material_standby_temperature": {
"minimum_value": "0"
}
} }
} }

View File

@ -8,6 +8,7 @@
"author": "Ultimaker", "author": "Ultimaker",
"manufacturer": "Ultimaker", "manufacturer": "Ultimaker",
"category": "Ultimaker", "category": "Ultimaker",
"weight": 3,
"file_formats": "text/x-gcode", "file_formats": "text/x-gcode",
"icon": "icon_ultimaker2.png", "icon": "icon_ultimaker2.png",
"platform": "ultimaker2_platform.obj", "platform": "ultimaker2_platform.obj",
@ -17,10 +18,12 @@
}, },
"overrides": { "overrides": {
"machine_start_gcode" : { "machine_start_gcode" : {
"default_value": "" "default_value": "",
"value": "\"\" if machine_gcode_flavor == \"UltiGCode\" else \"G21 ;metric values\\nG90 ;absolute positioning\\nM82 ;set extruder to absolute mode\\nM107 ;start with the fan off\\nG1 X10 Y0 F4000;move X/Y to min endstops\\nG28 Z0 ;move Z to bottom endstops\\nG1 Z15.0 F9000 ;move the platform to 15mm\\nG92 E0 ;zero the extruded length\\nG1 F200 E10 ;extrude 10 mm of feed stock\\nG92 E0 ;zero the extruded length again\\nG1 F9000\\n;Put printing message on LCD screen\\nM117 Printing...\""
}, },
"machine_end_gcode" : { "machine_end_gcode" : {
"default_value": "" "default_value": "",
"value": "\"\" if machine_gcode_flavor == \"UltiGCode\" else \"M104 S0 ;extruder heater off\\nM140 S0 ;heated bed heater off (if you have it)\\nG91 ;relative positioning\\nG1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\\nG1 Z+0.5 E-5 X-20 Y-20 F9000 ;move Z up a bit and retract filament even more\\nG28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\\nM84 ;steppers off\\nG90 ;absolute positioning\""
}, },
"machine_width": { "machine_width": {
"default_value": 223 "default_value": 223
@ -57,7 +60,7 @@
"default_value": 2 "default_value": 2
}, },
"gantry_height": { "gantry_height": {
"default_value": 55 "default_value": 48
}, },
"machine_use_extruder_offset_to_offset_coords": { "machine_use_extruder_offset_to_offset_coords": {
"default_value": true "default_value": true
@ -82,10 +85,10 @@
"default_value": 45 "default_value": 45
}, },
"material_print_temperature": { "material_print_temperature": {
"enabled": "False" "enabled": "not (material_flow_dependent_temperature) and machine_gcode_flavor != \"UltiGCode\""
}, },
"material_bed_temperature": { "material_bed_temperature": {
"enabled": "False" "enabled": "machine_heated_bed and machine_gcode_flavor != \"UltiGCode\""
}, },
"machine_max_feedrate_x": { "machine_max_feedrate_x": {
"default_value": 300 "default_value": 300
@ -103,22 +106,22 @@
"default_value": 3000 "default_value": 3000
}, },
"material_diameter": { "material_diameter": {
"enabled": "False" "enabled": "machine_gcode_flavor != \"UltiGCode\""
}, },
"material_flow": { "material_flow": {
"enabled": "False" "enabled": "machine_gcode_flavor != \"UltiGCode\""
}, },
"retraction_amount": { "retraction_amount": {
"enabled": "False" "enabled": "retraction_enable and machine_gcode_flavor != \"UltiGCode\""
}, },
"retraction_speed": { "retraction_speed": {
"enabled": "False" "enabled": "retraction_enable and machine_gcode_flavor != \"UltiGCode\""
}, },
"retraction_retract_speed": { "retraction_retract_speed": {
"enabled": "False" "enabled": "retraction_enable and machine_gcode_flavor != \"UltiGCode\""
}, },
"retraction_prime_speed": { "retraction_prime_speed": {
"enabled": "False" "enabled": "retraction_enable and machine_gcode_flavor != \"UltiGCode\""
} }
} }
} }

View File

@ -7,6 +7,7 @@
"author": "Ultimaker", "author": "Ultimaker",
"manufacturer": "Ultimaker", "manufacturer": "Ultimaker",
"category": "Ultimaker", "category": "Ultimaker",
"weight": 3,
"file_formats": "text/x-gcode", "file_formats": "text/x-gcode",
"icon": "icon_ultimaker2.png", "icon": "icon_ultimaker2.png",
"platform": "ultimaker2_platform.obj", "platform": "ultimaker2_platform.obj",

View File

@ -7,6 +7,7 @@
"author": "Ultimaker", "author": "Ultimaker",
"manufacturer": "Ultimaker", "manufacturer": "Ultimaker",
"category": "Ultimaker", "category": "Ultimaker",
"weight": 2,
"file_formats": "text/x-gcode", "file_formats": "text/x-gcode",
"platform": "ultimaker2_platform.obj", "platform": "ultimaker2_platform.obj",
"platform_texture": "Ultimaker2ExtendedPlusbackplate.png", "platform_texture": "Ultimaker2ExtendedPlusbackplate.png",

View File

@ -7,6 +7,7 @@
"author": "Ultimaker", "author": "Ultimaker",
"manufacturer": "Ultimaker", "manufacturer": "Ultimaker",
"category": "Ultimaker", "category": "Ultimaker",
"weight": 3,
"file_formats": "text/x-gcode", "file_formats": "text/x-gcode",
"icon": "icon_ultimaker2.png", "icon": "icon_ultimaker2.png",
"platform": "ultimaker2go_platform.obj", "platform": "ultimaker2go_platform.obj",

View File

@ -7,6 +7,7 @@
"author": "Ultimaker", "author": "Ultimaker",
"manufacturer": "Ultimaker", "manufacturer": "Ultimaker",
"category": "Ultimaker", "category": "Ultimaker",
"weight": 1,
"file_formats": "text/x-gcode", "file_formats": "text/x-gcode",
"platform": "ultimaker2_platform.obj", "platform": "ultimaker2_platform.obj",
"platform_texture": "Ultimaker2Plusbackplate.png", "platform_texture": "Ultimaker2Plusbackplate.png",

View File

@ -8,6 +8,7 @@
"author": "Ultimaker", "author": "Ultimaker",
"manufacturer": "Ultimaker", "manufacturer": "Ultimaker",
"category": "Ultimaker", "category": "Ultimaker",
"weight": 4,
"file_formats": "text/x-gcode", "file_formats": "text/x-gcode",
"icon": "icon_ultimaker.png", "icon": "icon_ultimaker.png",
"platform": "ultimaker_platform.stl", "platform": "ultimaker_platform.stl",
@ -15,7 +16,7 @@
"preferred_material": "*pla*", "preferred_material": "*pla*",
"preferred_quality": "*normal*", "preferred_quality": "*normal*",
"first_start_actions": ["UMOUpgradeSelection", "UMOCheckup", "BedLevel"], "first_start_actions": ["UMOUpgradeSelection", "UMOCheckup", "BedLevel"],
"supported_actions": ["UMOCheckup", "UpgradeFirmware", "BedLevel", "UMOUpgradeSelection"] "supported_actions": ["UMOUpgradeSelection", "UMOCheckup", "BedLevel", "UpgradeFirmware"]
}, },
"overrides": { "overrides": {

View File

@ -0,0 +1,76 @@
{
"id": "ultimaker_original_dual",
"version": 2,
"name": "Ultimaker Original Dual Extrusion",
"inherits": "ultimaker",
"metadata": {
"visible": true,
"author": "Ultimaker",
"manufacturer": "Ultimaker",
"category": "Ultimaker",
"weight": 4,
"file_formats": "text/x-gcode",
"icon": "icon_ultimaker.png",
"platform": "ultimaker_platform.stl",
"has_materials": true,
"preferred_material": "*pla*",
"preferred_quality": "*normal*",
"machine_extruder_trains":
{
"0": "ultimaker_original_dual_left",
"1": "ultimaker_original_dual_right"
},
"first_start_actions": ["UMOUpgradeSelection", "UMOCheckup", "BedLevel"],
"supported_actions": ["UMOUpgradeSelection", "UMOCheckup", "BedLevel", "UpgradeFirmware"]
},
"overrides": {
"machine_width": {
"default_value": 205
},
"machine_height": {
"default_value": 200
},
"machine_depth": {
"default_value": 195
},
"machine_center_is_zero": {
"default_value": false
},
"machine_nozzle_size": {
"default_value": 0.4
},
"machine_nozzle_heat_up_speed": {
"default_value": 2
},
"machine_nozzle_cool_down_speed": {
"default_value": 2
},
"machine_head_with_fans_polygon":
{
"default_value": [
[ -75, 35 ],
[ -75, -18 ],
[ 18, 35 ],
[ 18, -18 ]
]
},
"gantry_height": {
"default_value": 55
},
"machine_use_extruder_offset_to_offset_coords": {
"default_value": true
},
"machine_gcode_flavor": {
"default_value": "RepRap (Marlin/Sprinter)"
},
"machine_start_gcode": {
"default_value": "G21 ;metric values\nG90 ;absolute positioning\nM82 ;set extruder to absolute mode\nM107 ;start with the fan off\nG28 X0 Y0 ;move X/Y to min endstops\nG28 Z0 ;move Z to min endstops\nG1 Z15.0 F9000 ;move the platform down 15mm\nG92 E0 ;zero the extruded length\nG1 F200 E6 ;extrude 6 mm of feed stock\nG92 E0 ;zero the extruded length again\nG1 F9000\n;Put printing message on LCD screen\nM117 Printing..."
},
"machine_end_gcode": {
"default_value": "M104 S0 ;extruder heater off\nM140 S0 ;heated bed heater off (if you have it)\nG91 ;relative positioning\nG1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\nG1 Z+0.5 E-5 X-20 Y-20 F9000 ;move Z up a bit and retract filament even more\nG28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\nM84 ;steppers off\nG90 ;absolute positioning"
},
"machine_extruder_count": { "default_value": 2 },
"print_sequence": {"enabled": false}
}
}

View File

@ -7,11 +7,13 @@
"author": "Ultimaker", "author": "Ultimaker",
"manufacturer": "Ultimaker", "manufacturer": "Ultimaker",
"category": "Ultimaker", "category": "Ultimaker",
"weight": 4,
"file_formats": "text/x-gcode", "file_formats": "text/x-gcode",
"icon": "icon_ultimaker.png", "icon": "icon_ultimaker.png",
"platform": "ultimaker2_platform.obj", "platform": "ultimaker2_platform.obj",
"platform_texture": "UltimakerPlusbackplate.png", "platform_texture": "UltimakerPlusbackplate.png",
"supported_actions":["UMOCheckup", "UpgradeFirmware", "BedLevel"] "first_start_actions": ["UMOCheckup", "BedLevel"],
"supported_actions": ["UMOCheckup", "BedLevel", "UpgradeFirmware"]
}, },
"overrides": { "overrides": {

View File

@ -0,0 +1,19 @@
{
"id": "ultimaker_original_dual_left",
"version": 2,
"name": "Left Extruder",
"inherits": "fdmextruder",
"metadata": {
"machine": "ultimaker_original_dual",
"position": "0"
},
"overrides": {
"extruder_nr": {
"default_value": 0,
"maximum_value": "1"
},
"machine_nozzle_offset_x": { "default_value": 0.0 },
"machine_nozzle_offset_y": { "default_value": 0.0 }
}
}

View File

@ -0,0 +1,19 @@
{
"id": "ultimaker_original_dual_right",
"version": 2,
"name": "Right Extruder",
"inherits": "fdmextruder",
"metadata": {
"machine": "ultimaker_original_dual",
"position": "1"
},
"overrides": {
"extruder_nr": {
"default_value": 1,
"maximum_value": "1"
},
"machine_nozzle_offset_x": { "default_value": 0.0 },
"machine_nozzle_offset_y": { "default_value": 21.6 }
}
}

View File

@ -11,7 +11,7 @@ Generic PLA profile. Serves as an example file, data in this file is not correct
</name> </name>
<GUID>60636bb4-518f-42e7-8237-fe77b194ebe0</GUID> <GUID>60636bb4-518f-42e7-8237-fe77b194ebe0</GUID>
<version>0</version> <version>0</version>
<color_code>#FF0000</color_code> <color_code>#8cb219</color_code>
</metadata> </metadata>
<properties> <properties>
<density>1.07</density> <density>1.07</density>

View File

@ -11,7 +11,7 @@ Generic PLA profile. Serves as an example file, data in this file is not correct
</name> </name>
<GUID>12f41353-1a33-415e-8b4f-a775a6c70cc6</GUID> <GUID>12f41353-1a33-415e-8b4f-a775a6c70cc6</GUID>
<version>0</version> <version>0</version>
<color_code>#0000FF</color_code> <color_code>#159499</color_code>
</metadata> </metadata>
<properties> <properties>
<density>0.94</density> <density>0.94</density>

View File

@ -11,7 +11,7 @@ Generic PLA profile. Serves as an example file, data in this file is not correct
</name> </name>
<GUID>506c9f0d-e3aa-4bd4-b2d2-23e2425b1aa9</GUID> <GUID>506c9f0d-e3aa-4bd4-b2d2-23e2425b1aa9</GUID>
<version>0</version> <version>0</version>
<color_code>#00FF00</color_code> <color_code>#ffc924</color_code>
</metadata> </metadata>
<properties> <properties>
<density>1.3</density> <density>1.3</density>

View File

@ -27,6 +27,7 @@ Item
property alias multiplyObject: multiplyObjectAction; property alias multiplyObject: multiplyObjectAction;
property alias selectAll: selectAllAction;
property alias deleteAll: deleteAllAction; property alias deleteAll: deleteAllAction;
property alias reloadAll: reloadAllAction; property alias reloadAll: reloadAllAction;
property alias resetAllTranslation: resetAllTranslationAction; property alias resetAllTranslation: resetAllTranslationAction;
@ -119,7 +120,7 @@ Item
Action Action
{ {
id: updateProfileAction; id: updateProfileAction;
enabled: Cura.MachineManager.isGlobalStackValid && Cura.MachineManager.hasUserSettings && !Cura.MachineManager.isReadOnly(Cura.MachineManager.activeQualityId) enabled: Cura.MachineManager.isActiveStackValid && Cura.MachineManager.hasUserSettings && !Cura.MachineManager.isReadOnly(Cura.MachineManager.activeQualityId)
text: catalog.i18nc("@action:inmenu menubar:profile","&Update profile with current settings"); text: catalog.i18nc("@action:inmenu menubar:profile","&Update profile with current settings");
onTriggered: Cura.MachineManager.updateQualityContainerFromUserContainer() onTriggered: Cura.MachineManager.updateQualityContainerFromUserContainer()
} }
@ -135,7 +136,7 @@ Item
Action Action
{ {
id: addProfileAction; id: addProfileAction;
enabled: Cura.MachineManager.isGlobalStackValid && Cura.MachineManager.hasUserSettings enabled: Cura.MachineManager.isActiveStackValid && Cura.MachineManager.hasUserSettings
text: catalog.i18nc("@action:inmenu menubar:profile","&Create profile from current settings..."); text: catalog.i18nc("@action:inmenu menubar:profile","&Create profile from current settings...");
} }
@ -182,7 +183,7 @@ Item
Action Action
{ {
id: deleteObjectAction; id: deleteObjectAction;
text: catalog.i18nc("@action:inmenu","Delete Object"); text: catalog.i18nc("@action:inmenu","Delete Model");
enabled: UM.Controller.toolsEnabled; enabled: UM.Controller.toolsEnabled;
iconName: "edit-delete"; iconName: "edit-delete";
} }
@ -190,13 +191,13 @@ Item
Action Action
{ {
id: centerObjectAction; id: centerObjectAction;
text: catalog.i18nc("@action:inmenu","Ce&nter Object on Platform"); text: catalog.i18nc("@action:inmenu","Ce&nter Model on Platform");
} }
Action Action
{ {
id: groupObjectsAction id: groupObjectsAction
text: catalog.i18nc("@action:inmenu menubar:edit","&Group Objects"); text: catalog.i18nc("@action:inmenu menubar:edit","&Group Models");
enabled: UM.Scene.numObjectsSelected > 1 ? true: false enabled: UM.Scene.numObjectsSelected > 1 ? true: false
iconName: "object-group" iconName: "object-group"
shortcut: "Ctrl+G"; shortcut: "Ctrl+G";
@ -206,7 +207,7 @@ Item
Action Action
{ {
id: unGroupObjectsAction id: unGroupObjectsAction
text: catalog.i18nc("@action:inmenu menubar:edit","Ungroup Objects"); text: catalog.i18nc("@action:inmenu menubar:edit","Ungroup Models");
enabled: UM.Scene.isGroupSelected enabled: UM.Scene.isGroupSelected
iconName: "object-ungroup" iconName: "object-ungroup"
shortcut: "Ctrl+Shift+G"; shortcut: "Ctrl+Shift+G";
@ -216,7 +217,7 @@ Item
Action Action
{ {
id: mergeObjectsAction id: mergeObjectsAction
text: catalog.i18nc("@action:inmenu menubar:edit","&Merge Objects"); text: catalog.i18nc("@action:inmenu menubar:edit","&Merge Models");
enabled: UM.Scene.numObjectsSelected > 1 ? true: false enabled: UM.Scene.numObjectsSelected > 1 ? true: false
iconName: "merge"; iconName: "merge";
shortcut: "Ctrl+Alt+G"; shortcut: "Ctrl+Alt+G";
@ -226,14 +227,24 @@ Item
Action Action
{ {
id: multiplyObjectAction; id: multiplyObjectAction;
text: catalog.i18nc("@action:inmenu","&Duplicate Object"); text: catalog.i18nc("@action:inmenu","&Duplicate Model");
iconName: "edit-duplicate" iconName: "edit-duplicate"
} }
Action
{
id: selectAllAction;
text: catalog.i18nc("@action:inmenu menubar:edit","&Select All Models");
enabled: UM.Controller.toolsEnabled;
iconName: "edit-select-all";
shortcut: "Ctrl+A";
onTriggered: Printer.selectAll();
}
Action Action
{ {
id: deleteAllAction; id: deleteAllAction;
text: catalog.i18nc("@action:inmenu menubar:edit","&Clear Build Platform"); text: catalog.i18nc("@action:inmenu menubar:edit","&Clear Build Plate");
enabled: UM.Controller.toolsEnabled; enabled: UM.Controller.toolsEnabled;
iconName: "edit-delete"; iconName: "edit-delete";
shortcut: "Ctrl+D"; shortcut: "Ctrl+D";
@ -243,7 +254,7 @@ Item
Action Action
{ {
id: reloadAllAction; id: reloadAllAction;
text: catalog.i18nc("@action:inmenu menubar:file","Re&load All Objects"); text: catalog.i18nc("@action:inmenu menubar:file","Re&load All Models");
iconName: "document-revert"; iconName: "document-revert";
onTriggered: Printer.reloadAll(); onTriggered: Printer.reloadAll();
} }
@ -251,14 +262,14 @@ Item
Action Action
{ {
id: resetAllTranslationAction; id: resetAllTranslationAction;
text: catalog.i18nc("@action:inmenu menubar:edit","Reset All Object Positions"); text: catalog.i18nc("@action:inmenu menubar:edit","Reset All Model Positions");
onTriggered: Printer.resetAllTranslation(); onTriggered: Printer.resetAllTranslation();
} }
Action Action
{ {
id: resetAllAction; id: resetAllAction;
text: catalog.i18nc("@action:inmenu menubar:edit","Reset All Object &Transformations"); text: catalog.i18nc("@action:inmenu menubar:edit","Reset All Model &Transformations");
onTriggered: Printer.resetAll(); onTriggered: Printer.resetAll();
} }

View File

@ -16,12 +16,24 @@ UM.Dialog
{ {
id: base id: base
title: catalog.i18nc("@title:window", "Add Printer") title: catalog.i18nc("@title:window", "Add Printer")
property string activeManufacturer: "Ultimaker"; property bool firstRun: false
property string preferredCategory: "Ultimaker"
property string activeCategory: preferredCategory
onVisibilityChanged:
{
// Reset selection and machine name
if (visible) {
activeCategory = preferredCategory;
machineList.currentIndex = 0;
machineName.text = getMachineName();
}
}
signal machineAdded(string id) signal machineAdded(string id)
function getMachineName() function getMachineName()
{ {
var name = machineList.model.getItem(machineList.currentIndex).name var name = machineList.model.getItem(machineList.currentIndex) != undefined ? machineList.model.getItem(machineList.currentIndex).name : ""
return name return name
} }
@ -36,6 +48,7 @@ UM.Dialog
right: parent.right; right: parent.right;
bottom: parent.bottom; bottom: parent.bottom;
} }
ListView ListView
{ {
id: machineList id: machineList
@ -44,8 +57,11 @@ UM.Dialog
{ {
id: machineDefinitionsModel id: machineDefinitionsModel
filter: { "visible": true } filter: { "visible": true }
sectionProperty: "category"
preferredSectionValue: preferredCategory
} }
section.property: "manufacturer"
section.property: "section"
section.delegate: Button section.delegate: Button
{ {
text: section text: section
@ -76,16 +92,25 @@ UM.Dialog
sourceSize.width: width sourceSize.width: width
sourceSize.height: width sourceSize.height: width
color: palette.windowText color: palette.windowText
source: base.activeManufacturer == section ? UM.Theme.getIcon("arrow_bottom") : UM.Theme.getIcon("arrow_right") source: base.activeCategory == section ? UM.Theme.getIcon("arrow_bottom") : UM.Theme.getIcon("arrow_right")
} }
} }
} }
onClicked: onClicked:
{ {
base.activeManufacturer = section; base.activeCategory = section;
machineList.currentIndex = machineList.model.find("manufacturer", section) if (machineList.model.getItem(machineList.currentIndex).section != section) {
machineName.text = getMachineName() // Find the first machine from this section
for(var i = 0; i < sortedMachineDefinitionsModel.count; i++) {
var item = sortedMachineDefinitionsModel.getItem(i);
if (item.section == section) {
machineList.currentIndex = i;
break;
}
}
}
machineName.text = getMachineName();
} }
} }
@ -114,7 +139,7 @@ UM.Dialog
states: State states: State
{ {
name: "collapsed"; name: "collapsed";
when: base.activeManufacturer != model.manufacturer; when: base.activeCategory != model.section;
PropertyChanges { target: machineButton; opacity: 0; height: 0; } PropertyChanges { target: machineButton; opacity: 0; height: 0; }
} }
@ -158,14 +183,18 @@ UM.Dialog
text: catalog.i18nc("@action:button", "Add Printer") text: catalog.i18nc("@action:button", "Add Printer")
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
anchors.right: parent.right anchors.right: parent.right
onClicked: onClicked: addMachine()
}
onAccepted: addMachine()
function addMachine()
{ {
base.visible = false base.visible = false
var item = machineList.model.getItem(machineList.currentIndex); var item = machineList.model.getItem(machineList.currentIndex);
Cura.MachineManager.addMachine(machineName.text, item.id) Cura.MachineManager.addMachine(machineName.text, item.id)
base.machineAdded(item.id) // Emit signal that the user added a machine. base.machineAdded(item.id) // Emit signal that the user added a machine.
} }
}
Item Item
{ {

View File

@ -1,32 +0,0 @@
// Copyright (c) 2015 Ultimaker B.V.
// Cura is released under the terms of the AGPLv3 or higher.
import QtQuick 2.2
import QtQuick.Controls 1.1
import QtQuick.Layouts 1.1
import QtQuick.Window 2.1
import UM 1.1 as UM
import Cura 1.0 as Cura
import "WizardPages"
UM.Wizard
{
id: base;
title: catalog.i18nc("@title:window", "Add Printer")
// This part is optional
// This part checks whether there is a printer -> if not: some of the functions (delete for example) are disabled
firstRun: false
Component.onCompleted: {
base.appendPage(Qt.resolvedUrl("WizardPages/AddMachine.qml"), catalog.i18nc("@title", "Add Printer"));
base.currentPage = 0;
}
Item {
UM.I18nCatalog { id: catalog; name: "cura"; }
}
}

View File

@ -54,10 +54,7 @@ UM.MainWindow
Keys.onPressed: { Keys.onPressed: {
if (event.key == Qt.Key_Backspace) if (event.key == Qt.Key_Backspace)
{ {
if(objectContextMenu.objectId != 0) Cura.Actions.deleteSelection.trigger()
{
Printer.deleteObject(objectContextMenu.objectId);
}
} }
} }
@ -121,6 +118,7 @@ UM.MainWindow
MenuItem { action: Cura.Actions.undo; } MenuItem { action: Cura.Actions.undo; }
MenuItem { action: Cura.Actions.redo; } MenuItem { action: Cura.Actions.redo; }
MenuSeparator { } MenuSeparator { }
MenuItem { action: Cura.Actions.selectAll; }
MenuItem { action: Cura.Actions.deleteSelection; } MenuItem { action: Cura.Actions.deleteSelection; }
MenuItem { action: Cura.Actions.deleteAll; } MenuItem { action: Cura.Actions.deleteAll; }
MenuItem { action: Cura.Actions.resetAllTranslation; } MenuItem { action: Cura.Actions.resetAllTranslation; }
@ -540,6 +538,7 @@ UM.MainWindow
MenuItem { action: Cura.Actions.deleteObject; } MenuItem { action: Cura.Actions.deleteObject; }
MenuItem { action: Cura.Actions.multiplyObject; } MenuItem { action: Cura.Actions.multiplyObject; }
MenuSeparator { } MenuSeparator { }
MenuItem { action: Cura.Actions.selectAll; }
MenuItem { action: Cura.Actions.deleteAll; } MenuItem { action: Cura.Actions.deleteAll; }
MenuItem { action: Cura.Actions.reloadAll; } MenuItem { action: Cura.Actions.reloadAll; }
MenuItem { action: Cura.Actions.resetAllTranslation; } MenuItem { action: Cura.Actions.resetAllTranslation; }
@ -592,6 +591,7 @@ UM.MainWindow
Menu Menu
{ {
id: contextMenu; id: contextMenu;
MenuItem { action: Cura.Actions.selectAll; }
MenuItem { action: Cura.Actions.deleteAll; } MenuItem { action: Cura.Actions.deleteAll; }
MenuItem { action: Cura.Actions.reloadAll; } MenuItem { action: Cura.Actions.reloadAll; }
MenuItem { action: Cura.Actions.resetAllTranslation; } MenuItem { action: Cura.Actions.resetAllTranslation; }
@ -677,6 +677,7 @@ UM.MainWindow
id: addMachineDialog id: addMachineDialog
onMachineAdded: onMachineAdded:
{ {
machineActionsWizard.firstRun = addMachineDialog.firstRun
machineActionsWizard.start(id) machineActionsWizard.start(id)
} }
} }

View File

@ -209,7 +209,7 @@ Rectangle {
lengths = ["0.00"]; lengths = ["0.00"];
weights = ["0"]; weights = ["0"];
} }
return catalog.i18nc("@label", "%1 m / %2 g").arg(lengths.join(" + ")).arg(weights.join(" + ")); return catalog.i18nc("@label", "%1 m / ~ %2 g").arg(lengths.join(" + ")).arg(weights.join(" + "));
} }
} }
} }

View File

@ -3,6 +3,10 @@ import QtQuick 2.2
Item Item
{ {
id: contentItem id: contentItem
// Point to the dialog containing the displayItem
property var dialog
// Connect the finished property change to completed signal. // Connect the finished property change to completed signal.
property var finished: manager.finished property var finished: manager.finished
onFinishedChanged: if(manager.finished) {completed()} onFinishedChanged: if(manager.finished) {completed()}

View File

@ -15,23 +15,57 @@ Rectangle
UM.I18nCatalog { id: catalog; name:"cura"} UM.I18nCatalog { id: catalog; name:"cura"}
property bool printerConnected: Cura.MachineManager.printerOutputDevices.length != 0 property bool printerConnected: Cura.MachineManager.printerOutputDevices.length != 0
property real progress: printerConnected ? Cura.MachineManager.printerOutputDevices[0].progress : 0; property bool printerAcceptsCommands: printerConnected && Cura.MachineManager.printerOutputDevices[0].acceptsCommands
property int backendState: UM.Backend.state; property real progress: printerConnected ? Cura.MachineManager.printerOutputDevices[0].progress : 0
property int backendState: UM.Backend.state
property bool showProgress: {
// determine if we need to show the progress bar + percentage
if(!printerConnected || !printerAcceptsCommands) {
return false;
}
switch(Cura.MachineManager.printerOutputDevices[0].jobState)
{
case "printing":
case "pre_print": // heating, etc.
case "paused":
return true;
case "wait_cleanup":
case "ready": // nut sure if this occurs, "" seems to be the ready state.
case "offline":
case "abort": // note sure if this jobState actually occurs in the wild
case "error": // after clicking abort you apparently get "error"
case "": // ready to print or getting ready
default:
return false;
}
}
property variant statusColor: property variant statusColor:
{ {
if(!printerConnected) if(!printerConnected || !printerAcceptsCommands)
return UM.Theme.getColor("status_offline") return UM.Theme.getColor("text");
else if(Cura.MachineManager.printerOutputDevices[0].jobState == "printing" || Cura.MachineManager.printerOutputDevices[0].jobState == "pre_print")
return UM.Theme.getColor("status_busy") switch(Cura.MachineManager.printerOutputDevices[0].jobState)
else if(Cura.MachineManager.printerOutputDevices[0].jobState == "ready" || Cura.MachineManager.printerOutputDevices[0].jobState == "") {
return UM.Theme.getColor("status_ready") case "printing":
else if(Cura.MachineManager.printerOutputDevices[0].jobState == "paused") case "pre_print":
return UM.Theme.getColor("status_paused") case "wait_cleanup":
else if (Cura.MachineManager.printerOutputDevices[0].jobState == "error") return UM.Theme.getColor("status_busy");
return UM.Theme.getColor("status_stopped") case "ready":
else case "":
return UM.Theme.getColor("text") return UM.Theme.getColor("status_ready");
case "paused":
return UM.Theme.getColor("status_paused");
case "error":
return UM.Theme.getColor("status_stopped");
case "offline":
return UM.Theme.getColor("status_offline");
default:
return UM.Theme.getColor("text");
}
} }
property bool activity: Printer.getPlatformActivity; property bool activity: Printer.getPlatformActivity;
@ -40,24 +74,28 @@ Rectangle
property string statusText: property string statusText:
{ {
if(!printerConnected) if(!printerConnected)
{ return catalog.i18nc("@label:MonitorStatus", "Not connected to a printer");
return catalog.i18nc("@label:", "Please check your printer connections") if(!printerAcceptsCommands)
} else if(Cura.MachineManager.printerOutputDevices[0].jobState == "printing") return catalog.i18nc("@label:MonitorStatus", "Printer does not accept commands");
{
return catalog.i18nc("@label:", "Printing...")
} else if(Cura.MachineManager.printerOutputDevices[0].jobState == "paused")
{
return catalog.i18nc("@label:", "Paused")
}
else if(Cura.MachineManager.printerOutputDevices[0].jobState == "pre_print")
{
return catalog.i18nc("@label:", "Preparing...")
}
else
{
return " "
}
var printerOutputDevice = Cura.MachineManager.printerOutputDevices[0]
switch(printerOutputDevice.jobState)
{
case "offline":
return catalog.i18nc("@label:MonitorStatus", "Lost connection with the printer");
case "printing":
return catalog.i18nc("@label:MonitorStatus", "Printing...");
case "paused":
return catalog.i18nc("@label:MonitorStatus", "Paused");
case "pre_print":
return catalog.i18nc("@label:MonitorStatus", "Preparing...");
case "wait_cleanup":
return catalog.i18nc("@label:MonitorStatus", "Please remove the print");
case "error":
return printerOutputDevice.errorText;
default:
return " ";
}
} }
Label Label
@ -70,7 +108,7 @@ Rectangle
color: base.statusColor color: base.statusColor
font: UM.Theme.getFont("large") font: UM.Theme.getFont("large")
text: statusText; text: statusText
} }
Label Label
@ -81,8 +119,8 @@ Rectangle
color: base.statusColor color: base.statusColor
font: UM.Theme.getFont("large") font: UM.Theme.getFont("large")
text: Math.round(progress) + "%"; text: Math.round(progress) + "%"
visible: printerConnected visible: showProgress
} }
Rectangle Rectangle
@ -96,6 +134,7 @@ Rectangle
anchors.leftMargin: UM.Theme.getSize("default_margin").width anchors.leftMargin: UM.Theme.getSize("default_margin").width
radius: UM.Theme.getSize("progressbar_radius").width radius: UM.Theme.getSize("progressbar_radius").width
color: UM.Theme.getColor("progressbar_background") color: UM.Theme.getColor("progressbar_background")
visible: showProgress
Rectangle Rectangle
{ {
@ -111,7 +150,8 @@ Rectangle
id: abortButton id: abortButton
visible: printerConnected visible: printerConnected
enabled: printerConnected && (Cura.MachineManager.printerOutputDevices[0].jobState == "paused" || Cura.MachineManager.printerOutputDevices[0].jobState == "printing") enabled: printerConnected && Cura.MachineManager.printerOutputDevices[0].acceptsCommands &&
(["paused", "printing", "pre_print"].indexOf(Cura.MachineManager.printerOutputDevices[0].jobState) >= 0)
height: UM.Theme.getSize("save_button_save_to_button").height height: UM.Theme.getSize("save_button_save_to_button").height
anchors.top: progressBar.bottom anchors.top: progressBar.bottom
@ -120,20 +160,35 @@ Rectangle
anchors.rightMargin: UM.Theme.getSize("default_margin").width anchors.rightMargin: UM.Theme.getSize("default_margin").width
text: catalog.i18nc("@label:", "Abort Print") text: catalog.i18nc("@label:", "Abort Print")
onClicked: { Cura.MachineManager.printerOutputDevices[0].setJobState("abort") } onClicked: Cura.MachineManager.printerOutputDevices[0].setJobState("abort")
style: ButtonStyle style: ButtonStyle
{ {
background: Rectangle background: Rectangle
{ {
border.width: UM.Theme.getSize("default_lining").width border.width: UM.Theme.getSize("default_lining").width
border.color: !control.enabled ? UM.Theme.getColor("action_button_disabled_border") : border.color:
control.pressed ? UM.Theme.getColor("action_button_active_border") : {
control.hovered ? UM.Theme.getColor("action_button_hovered_border") : UM.Theme.getColor("action_button_border") if(!control.enabled)
color: !control.enabled ? UM.Theme.getColor("action_button_disabled") : return UM.Theme.getColor("action_button_disabled_border");
control.pressed ? UM.Theme.getColor("action_button_active") : else if(control.pressed)
control.hovered ? UM.Theme.getColor("action_button_hovered") : UM.Theme.getColor("action_button") return UM.Theme.getColor("action_button_active_border");
else if(control.hovered)
return UM.Theme.getColor("action_button_hovered_border");
else
return UM.Theme.getColor("action_button_border");
}
color:
{
if(!control.enabled)
return UM.Theme.getColor("action_button_disabled");
else if(control.pressed)
return UM.Theme.getColor("action_button_active");
else if(control.hovered)
return UM.Theme.getColor("action_button_hovered");
else
return UM.Theme.getColor("action_button");
}
Behavior on color { ColorAnimation { duration: 50; } } Behavior on color { ColorAnimation { duration: 50; } }
implicitWidth: actualLabel.contentWidth + (UM.Theme.getSize("default_margin").width * 2) implicitWidth: actualLabel.contentWidth + (UM.Theme.getSize("default_margin").width * 2)
@ -142,9 +197,17 @@ Rectangle
{ {
id: actualLabel id: actualLabel
anchors.centerIn: parent anchors.centerIn: parent
color: !control.enabled ? UM.Theme.getColor("action_button_disabled_text") : color:
control.pressed ? UM.Theme.getColor("action_button_active_text") : {
control.hovered ? UM.Theme.getColor("action_button_hovered_text") : UM.Theme.getColor("action_button_text") if(!control.enabled)
return UM.Theme.getColor("action_button_disabled_text");
else if(control.pressed)
return UM.Theme.getColor("action_button_active_text");
else if(control.hovered)
return UM.Theme.getColor("action_button_hovered_text");
else
return UM.Theme.getColor("action_button_text");
}
font: UM.Theme.getFont("action_button") font: UM.Theme.getFont("action_button")
text: control.text; text: control.text;
} }
@ -155,10 +218,7 @@ Rectangle
Button Button
{ {
id: pauseButton id: pauseResumeButton
visible: printerConnected
enabled: printerConnected && (Cura.MachineManager.printerOutputDevices[0].jobState == "paused" || Cura.MachineManager.printerOutputDevices[0].jobState == "printing")
height: UM.Theme.getSize("save_button_save_to_button").height height: UM.Theme.getSize("save_button_save_to_button").height
anchors.top: progressBar.bottom anchors.top: progressBar.bottom
@ -166,20 +226,77 @@ Rectangle
anchors.right: abortButton.left anchors.right: abortButton.left
anchors.rightMargin: UM.Theme.getSize("default_margin").width anchors.rightMargin: UM.Theme.getSize("default_margin").width
text: printerConnected ? Cura.MachineManager.printerOutputDevices[0].jobState == "paused" ? catalog.i18nc("@label:", "Resume") : catalog.i18nc("@label:", "Pause") : "" property bool userClicked: false
onClicked: { Cura.MachineManager.printerOutputDevices[0].jobState == "paused" ? Cura.MachineManager.printerOutputDevices[0].setJobState("print") : Cura.MachineManager.printerOutputDevices[0].setJobState("pause") } property string lastJobState: ""
visible: printerConnected
enabled: (!userClicked) && printerConnected && Cura.MachineManager.printerOutputDevices[0].acceptsCommands &&
(["paused", "printing"].indexOf(Cura.MachineManager.printerOutputDevices[0].jobState) >= 0)
text: {
var result = "";
var jobState = Cura.MachineManager.printerOutputDevices[0].jobState;
if (!printerConnected) {
return "";
}
if (lastJobState !== jobState) {
// the userClicked message must disappear when an "automated" jobState comes by
userClicked = false;
lastJobState = jobState;
}
if (jobState == "paused")
{
if (userClicked) {
// User feedback for pretending we're already in "printing" mode.
result = catalog.i18nc("@label:", "Pause");
} else {
result = catalog.i18nc("@label:", "Resume");
}
} else {
if (userClicked) {
// User feedback for pretending we're already in "pause" mode.
result = catalog.i18nc("@label:", "Resume");
} else {
result = catalog.i18nc("@label:", "Pause");
}
}
return result;
}
onClicked: {
var newJobState = Cura.MachineManager.printerOutputDevices[0].jobState == "paused" ? "print" : "pause";
Cura.MachineManager.printerOutputDevices[0].setJobState(newJobState);
userClicked = true;
}
style: ButtonStyle style: ButtonStyle
{ {
background: Rectangle background: Rectangle
{ {
border.width: UM.Theme.getSize("default_lining").width border.width: UM.Theme.getSize("default_lining").width
border.color: !control.enabled ? UM.Theme.getColor("action_button_disabled_border") : border.color:
control.pressed ? UM.Theme.getColor("action_button_active_border") : {
control.hovered ? UM.Theme.getColor("action_button_hovered_border") : UM.Theme.getColor("action_button_border") if(!control.enabled)
color: !control.enabled ? UM.Theme.getColor("action_button_disabled") : return UM.Theme.getColor("action_button_disabled_border");
control.pressed ? UM.Theme.getColor("action_button_active") : else if(control.pressed)
control.hovered ? UM.Theme.getColor("action_button_hovered") : UM.Theme.getColor("action_button") return UM.Theme.getColor("action_button_active_border");
else if(control.hovered)
return UM.Theme.getColor("action_button_hovered_border");
else
return UM.Theme.getColor("action_button_border");
}
color:
{
if(!control.enabled)
return UM.Theme.getColor("action_button_disabled");
else if(control.pressed)
return UM.Theme.getColor("action_button_active");
else if(control.hovered)
return UM.Theme.getColor("action_button_hovered");
else
return UM.Theme.getColor("action_button");
}
Behavior on color { ColorAnimation { duration: 50; } } Behavior on color { ColorAnimation { duration: 50; } }
implicitWidth: actualLabel.contentWidth + (UM.Theme.getSize("default_margin").width * 2) implicitWidth: actualLabel.contentWidth + (UM.Theme.getSize("default_margin").width * 2)
@ -188,11 +305,19 @@ Rectangle
{ {
id: actualLabel id: actualLabel
anchors.centerIn: parent anchors.centerIn: parent
color: !control.enabled ? UM.Theme.getColor("action_button_disabled_text") : color:
control.pressed ? UM.Theme.getColor("action_button_active_text") : {
control.hovered ? UM.Theme.getColor("action_button_hovered_text") : UM.Theme.getColor("action_button_text") if(!control.enabled)
return UM.Theme.getColor("action_button_disabled_text");
else if(control.pressed)
return UM.Theme.getColor("action_button_active_text");
else if(control.hovered)
return UM.Theme.getColor("action_button_hovered_text");
else
return UM.Theme.getColor("action_button_text");
}
font: UM.Theme.getFont("action_button") font: UM.Theme.getFont("action_button")
text: control.text; text: control.text
} }
} }
label: Item { } label: Item { }

View File

@ -69,6 +69,7 @@ UM.PreferencesPage
Row Row
{ {
spacing: UM.Theme.getSize("default_margin").width
Label Label
{ {
id: languageLabel id: languageLabel
@ -167,7 +168,7 @@ UM.PreferencesPage
UM.TooltipArea { UM.TooltipArea {
width: childrenRect.width; width: childrenRect.width;
height: childrenRect.height; height: childrenRect.height;
text: catalog.i18nc("@info:tooltip","Moves the camera so the object is in the center of the view when an object is selected") text: catalog.i18nc("@info:tooltip","Moves the camera so the model is in the center of the view when an model is selected")
CheckBox CheckBox
{ {
@ -181,17 +182,18 @@ UM.PreferencesPage
UM.TooltipArea { UM.TooltipArea {
width: childrenRect.width width: childrenRect.width
height: childrenRect.height height: childrenRect.height
text: catalog.i18nc("@info:tooltip", "Should objects on the platform be moved so that they no longer intersect?") text: catalog.i18nc("@info:tooltip", "Should models on the platform be moved so that they no longer intersect?")
CheckBox CheckBox
{ {
id: pushFreeCheckbox id: pushFreeCheckbox
text: catalog.i18nc("@option:check", "Ensure objects are kept apart") text: catalog.i18nc("@option:check", "Ensure models are kept apart")
checked: boolCheck(UM.Preferences.getValue("physics/automatic_push_free")) checked: boolCheck(UM.Preferences.getValue("physics/automatic_push_free"))
onCheckedChanged: UM.Preferences.setValue("physics/automatic_push_free", checked) onCheckedChanged: UM.Preferences.setValue("physics/automatic_push_free", checked)
} }
} }
UM.TooltipArea { UM.TooltipArea {
width: childrenRect.width; width: childrenRect.width;
height: childrenRect.height; height: childrenRect.height;
@ -215,6 +217,19 @@ UM.PreferencesPage
} }
} }
} }
UM.TooltipArea {
width: childrenRect.width
height: childrenRect.height
text: catalog.i18nc("@info:tooltip", "Should only the top layers be displayed in layerview?")
CheckBox
{
id: topLayersOnlyCheckbox
text: catalog.i18nc("@option:check", "Only display top layer(s) in layer view")
checked: boolCheck(UM.Preferences.getValue("view/only_show_top_layers"))
onCheckedChanged: UM.Preferences.setValue("view/only_show_top_layers", checked)
}
}
Item Item
{ {
@ -232,12 +247,12 @@ UM.PreferencesPage
UM.TooltipArea { UM.TooltipArea {
width: childrenRect.width width: childrenRect.width
height: childrenRect.height height: childrenRect.height
text: catalog.i18nc("@info:tooltip","Should objects be scaled to the build volume if they are too large?") text: catalog.i18nc("@info:tooltip","Should models be scaled to the build volume if they are too large?")
CheckBox CheckBox
{ {
id: scaleToFitCheckbox id: scaleToFitCheckbox
text: catalog.i18nc("@option:check","Scale large objects") text: catalog.i18nc("@option:check","Scale large models")
checked: boolCheck(UM.Preferences.getValue("mesh/scale_to_fit")) checked: boolCheck(UM.Preferences.getValue("mesh/scale_to_fit"))
onCheckedChanged: UM.Preferences.setValue("mesh/scale_to_fit", checked) onCheckedChanged: UM.Preferences.setValue("mesh/scale_to_fit", checked)
} }
@ -246,12 +261,12 @@ UM.PreferencesPage
UM.TooltipArea { UM.TooltipArea {
width: childrenRect.width width: childrenRect.width
height: childrenRect.height height: childrenRect.height
text: catalog.i18nc("@info:tooltip","An object may appear extremely small if its unit is for example in meters rather than millimeters. Should these objects be scaled up?") text: catalog.i18nc("@info:tooltip","An model may appear extremely small if its unit is for example in meters rather than millimeters. Should these models be scaled up?")
CheckBox CheckBox
{ {
id: scaleTinyCheckbox id: scaleTinyCheckbox
text: catalog.i18nc("@option:check","Scale extremely small objects") text: catalog.i18nc("@option:check","Scale extremely small models")
checked: boolCheck(UM.Preferences.getValue("mesh/scale_tiny_meshes")) checked: boolCheck(UM.Preferences.getValue("mesh/scale_tiny_meshes"))
onCheckedChanged: UM.Preferences.setValue("mesh/scale_tiny_meshes", checked) onCheckedChanged: UM.Preferences.setValue("mesh/scale_tiny_meshes", checked)
} }

View File

@ -18,7 +18,10 @@ UM.ManagementPage
} }
activeId: Cura.MachineManager.activeMachineId activeId: Cura.MachineManager.activeMachineId
activeIndex: { activeIndex: activeMachineIndex()
function activeMachineIndex()
{
for(var i = 0; i < model.rowCount(); i++) { for(var i = 0; i < model.rowCount(); i++) {
if (model.getItem(i).id == Cura.MachineManager.activeMachineId) { if (model.getItem(i).id == Cura.MachineManager.activeMachineId) {
return i; return i;
@ -83,16 +86,17 @@ UM.ManagementPage
Repeater Repeater
{ {
id: machineActionRepeater id: machineActionRepeater
model: Cura.MachineActionManager.getSupportedActions(Cura.MachineManager.getDefinitionByMachineId(base.currentItem.id)) model: base.currentItem ? Cura.MachineActionManager.getSupportedActions(Cura.MachineManager.getDefinitionByMachineId(base.currentItem.id)) : null
Button Button
{ {
text: machineActionRepeater.model[index].label; text: machineActionRepeater.model[index].label
onClicked: onClicked:
{ {
actionDialog.content = machineActionRepeater.model[index].displayItem actionDialog.content = machineActionRepeater.model[index].displayItem;
machineActionRepeater.model[index].displayItem.reset() machineActionRepeater.model[index].displayItem.reset();
actionDialog.show() actionDialog.title = machineActionRepeater.model[index].label;
actionDialog.show();
} }
} }
} }
@ -106,6 +110,13 @@ UM.ManagementPage
{ {
contents = content; contents = content;
content.onCompleted.connect(hide) content.onCompleted.connect(hide)
content.dialog = actionDialog
}
rightButtons: Button
{
text: catalog.i18nc("@action:button", "Close")
iconName: "dialog-close"
onClicked: actionDialog.reject()
} }
} }
@ -118,8 +129,14 @@ UM.ManagementPage
spacing: UM.Theme.getSize("default_margin").height spacing: UM.Theme.getSize("default_margin").height
Label { text: catalog.i18nc("@label", "Type") } Label
Label { text: base.currentItem ? base.currentItem.metadata.definition_name : "" } {
text: catalog.i18nc("@label", "Type")
visible: base.currentItem && "definition_name" in base.currentItem.metadata
}
Label {
text: (base.currentItem && "definition_name" in base.currentItem.metadata) ? base.currentItem.metadata.definition_name : ""
}
} }
UM.I18nCatalog { id: catalog; name: "uranium"; } UM.I18nCatalog { id: catalog; name: "uranium"; }
@ -128,7 +145,16 @@ UM.ManagementPage
{ {
id: confirmDialog; id: confirmDialog;
object: base.currentItem && base.currentItem.name ? base.currentItem.name : ""; object: base.currentItem && base.currentItem.name ? base.currentItem.name : "";
onYes: Cura.MachineManager.removeMachine(base.currentItem.id); onYes:
{
Cura.MachineManager.removeMachine(base.currentItem.id);
if(!base.currentItem)
{
objectList.currentIndex = activeMachineIndex()
}
//Force updating currentItem and the details panel
objectList.onCurrentIndexChanged()
}
} }
UM.RenameDialog UM.RenameDialog
@ -138,11 +164,20 @@ UM.ManagementPage
onAccepted: onAccepted:
{ {
Cura.MachineManager.renameMachine(base.currentItem.id, newName.trim()); Cura.MachineManager.renameMachine(base.currentItem.id, newName.trim());
//Reselect current item to update details panel //Force updating currentItem and the details panel
var index = objectList.currentIndex objectList.onCurrentIndexChanged()
objectList.currentIndex = -1
objectList.currentIndex = index
} }
} }
Connections
{
target: Cura.MachineManager
onGlobalContainerChanged:
{
objectList.currentIndex = activeMachineIndex()
objectList.onCurrentIndexChanged()
}
}
} }
} }

View File

@ -67,6 +67,8 @@ UM.ManagementPage
enabled: base.currentItem != null && base.currentItem.id != Cura.MachineManager.activeMaterialId enabled: base.currentItem != null && base.currentItem.id != Cura.MachineManager.activeMaterialId
onClicked: Cura.MachineManager.setActiveMaterial(base.currentItem.id) onClicked: Cura.MachineManager.setActiveMaterial(base.currentItem.id)
}, },
/*
// disabled because it has a lot of issues
Button Button
{ {
text: catalog.i18nc("@action:button", "Duplicate"); text: catalog.i18nc("@action:button", "Duplicate");
@ -89,7 +91,7 @@ UM.ManagementPage
Cura.MachineManager.setActiveMaterial(material_id) Cura.MachineManager.setActiveMaterial(material_id)
} }
}, }, */
Button Button
{ {
text: catalog.i18nc("@action:button", "Remove"); text: catalog.i18nc("@action:button", "Remove");
@ -253,7 +255,7 @@ UM.ManagementPage
else if(result.status == "success") else if(result.status == "success")
{ {
messageDialog.icon = StandardIcon.Information messageDialog.icon = StandardIcon.Information
messageDialog.text = catalog.i18nc("@info:status", "Successfully exported material to <filename>%1</filename>").arg(fileUrl) messageDialog.text = catalog.i18nc("@info:status", "Successfully exported material to <filename>%1</filename>").arg(result.path)
messageDialog.open() messageDialog.open()
} }
CuraApplication.setDefaultPath("dialog_material_path", folder) CuraApplication.setDefaultPath("dialog_material_path", folder)

View File

@ -75,7 +75,7 @@ UM.ManagementPage
onClicked: onClicked:
{ {
var selectedContainer; var selectedContainer;
if (objectList.currentItem.id == Cura.MachineManager.activeQualityId) { if (base.currentItem.id == Cura.MachineManager.activeQualityId) {
selectedContainer = Cura.MachineManager.newQualityContainerFromQualityAndUser(); selectedContainer = Cura.MachineManager.newQualityContainerFromQualityAndUser();
} else { } else {
selectedContainer = Cura.MachineManager.duplicateContainer(base.currentItem.id); selectedContainer = Cura.MachineManager.duplicateContainer(base.currentItem.id);
@ -197,7 +197,9 @@ UM.ManagementPage
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
ListView { ListView {
model: Cura.ContainerSettingsModel{ containers: model: Cura.ContainerSettingsModel
{
containers:
{ {
if (!currentItem) { if (!currentItem) {
return [] return []

View File

@ -12,6 +12,18 @@ import Cura 1.0 as Cura
Column Column
{ {
id: printMonitor id: printMonitor
property var connectedPrinter: printerConnected ? Cura.MachineManager.printerOutputDevices[0] : null
Cura.ExtrudersModel { id: extrudersModel }
Label
{
text: printerConnected ? connectedPrinter.connectionText : catalog.i18nc("@label", "The printer is not connected.")
color: printerConnected && printerAcceptsCommands ? UM.Theme.getColor("setting_control_text") : UM.Theme.getColor("setting_control_disabled_text")
font: UM.Theme.getFont("default")
wrapMode: Text.WordWrap
width: base.width
}
Loader Loader
{ {
@ -24,8 +36,8 @@ Column
delegate: Loader delegate: Loader
{ {
sourceComponent: monitorItem sourceComponent: monitorItem
property string label: machineExtruderCount.properties.value > 1 ? catalog.i18nc("@label", "Hotend Temperature %1").arg(index + 1) : catalog.i18nc("@label", "Hotend Temperature") property string label: machineExtruderCount.properties.value > 1 ? extrudersModel.getItem(index).name : catalog.i18nc("@label", "Hotend")
property string value: printerConnected ? Math.round(Cura.MachineManager.printerOutputDevices[0].hotendTemperatures[index]) + "°C" : "" property string value: printerConnected ? Math.round(connectedPrinter.hotendTemperatures[index]) + "°C" : ""
} }
} }
Repeater Repeater
@ -34,8 +46,8 @@ Column
delegate: Loader delegate: Loader
{ {
sourceComponent: monitorItem sourceComponent: monitorItem
property string label: catalog.i18nc("@label", "Bed Temperature") property string label: catalog.i18nc("@label", "Build plate")
property string value: printerConnected ? Math.round(Cura.MachineManager.printerOutputDevices[0].bedTemperature) + "°C" : "" property string value: printerConnected ? Math.round(connectedPrinter.bedTemperature) + "°C" : ""
} }
} }
@ -48,19 +60,19 @@ Column
{ {
sourceComponent: monitorItem sourceComponent: monitorItem
property string label: catalog.i18nc("@label", "Job Name") property string label: catalog.i18nc("@label", "Job Name")
property string value: printerConnected ? Cura.MachineManager.printerOutputDevices[0].jobName : "" property string value: printerConnected ? connectedPrinter.jobName : ""
} }
Loader Loader
{ {
sourceComponent: monitorItem sourceComponent: monitorItem
property string label: catalog.i18nc("@label", "Printing Time") property string label: catalog.i18nc("@label", "Printing Time")
property string value: printerConnected ? getPrettyTime(Cura.MachineManager.printerOutputDevices[0].timeTotal) : "" property string value: printerConnected ? getPrettyTime(connectedPrinter.timeTotal) : ""
} }
Loader Loader
{ {
sourceComponent: monitorItem sourceComponent: monitorItem
property string label: catalog.i18nc("@label", "Estimated time left") property string label: catalog.i18nc("@label", "Estimated time left")
property string value: printerConnected ? getPrettyTime(Cura.MachineManager.printerOutputDevices[0].timeTotal - Cura.MachineManager.printerOutputDevices[0].timeElapsed) : "" property string value: printerConnected ? getPrettyTime(connectedPrinter.timeTotal - connectedPrinter.timeElapsed) : ""
} }
Component Component
@ -70,21 +82,24 @@ Column
Row Row
{ {
height: UM.Theme.getSize("setting_control").height height: UM.Theme.getSize("setting_control").height
width: base.width - 2 * UM.Theme.getSize("default_margin").width
Label Label
{ {
text: label width: parent.width * 0.4
color: printerConnected ? UM.Theme.getColor("setting_control_text") : UM.Theme.getColor("setting_control_disabled_text")
font: UM.Theme.getFont("default")
width: base.width * 0.4
elide: Text.ElideRight
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
text: label
color: printerConnected && printerAcceptsCommands ? UM.Theme.getColor("setting_control_text") : UM.Theme.getColor("setting_control_disabled_text")
font: UM.Theme.getFont("default")
elide: Text.ElideRight
} }
Label Label
{ {
text: value width: parent.width * 0.6
color: printerConnected ? UM.Theme.getColor("setting_control_text") : UM.Theme.getColor("setting_control_disabled_text")
font: UM.Theme.getFont("default")
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
text: value
color: printerConnected && printerAcceptsCommands ? UM.Theme.getColor("setting_control_text") : UM.Theme.getColor("setting_control_disabled_text")
font: UM.Theme.getFont("default")
elide: Text.ElideRight
} }
} }
} }

View File

@ -84,6 +84,27 @@ Rectangle {
anchors.topMargin: UM.Theme.getSize("default_margin").height anchors.topMargin: UM.Theme.getSize("default_margin").height
anchors.left: parent.left anchors.left: parent.left
Row {
id: additionalComponentsRow
anchors.top: parent.top
anchors.right: saveToButton.visible ? saveToButton.left : parent.right
anchors.rightMargin: UM.Theme.getSize("default_margin").width
spacing: UM.Theme.getSize("default_margin").width
}
Connections {
target: Printer
onAdditionalComponentsChanged:
{
if(areaId == "saveButton") {
for (var component in Printer.additionalComponents["saveButton"]) {
Printer.additionalComponents["saveButton"][component].parent = additionalComponentsRow
}
}
}
}
Button { Button {
id: saveToButton id: saveToButton
@ -102,8 +123,7 @@ Rectangle {
} }
style: ButtonStyle { style: ButtonStyle {
background: background: Rectangle
Rectangle
{ {
border.width: UM.Theme.getSize("default_lining").width border.width: UM.Theme.getSize("default_lining").width
border.color: !control.enabled ? UM.Theme.getColor("action_button_disabled_border") : border.color: !control.enabled ? UM.Theme.getColor("action_button_disabled_border") :

View File

@ -86,18 +86,18 @@ SettingItem
} }
} }
onActivated: { forceActiveFocus(); provider.setPropertyValue("value", definition.options[index].key) } onActivated: { forceActiveFocus(); propertyProvider.setPropertyValue("value", definition.options[index].key) }
onModelChanged: updateCurrentIndex(); onModelChanged: updateCurrentIndex();
Connections Connections
{ {
target: provider target: propertyProvider
onPropertiesChanged: control.updateCurrentIndex() onPropertiesChanged: control.updateCurrentIndex()
} }
function updateCurrentIndex() { function updateCurrentIndex() {
for(var i = 0; i < definition.options.length; ++i) { for(var i = 0; i < definition.options.length; ++i) {
if(definition.options[i].key == provider.properties.value) { if(definition.options[i].key == propertyProvider.properties.value) {
currentIndex = i; currentIndex = i;
return; return;
} }

View File

@ -19,7 +19,10 @@ SettingItem
model: Cura.ExtrudersModel model: Cura.ExtrudersModel
{ {
id: extruders_model id: extruders_model
onModelChanged: control.color = extruders_model.getItem(control.currentIndex).color
} }
property string color: extruders_model.getItem(control.currentIndex).color
textRole: "name" textRole: "name"
anchors.fill: parent anchors.fill: parent
@ -64,7 +67,7 @@ SettingItem
anchors.leftMargin: UM.Theme.getSize("default_lining").width anchors.leftMargin: UM.Theme.getSize("default_lining").width
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
color: extruders_model.getItem(control.currentIndex).colour color: control.color
border.width: UM.Theme.getSize("default_lining").width border.width: UM.Theme.getSize("default_lining").width
border.color: !enabled ? UM.Theme.getColor("setting_control_disabled_border") : UM.Theme.getColor("setting_control_border") border.color: !enabled ? UM.Theme.getColor("setting_control_disabled_border") : UM.Theme.getColor("setting_control_border")
} }
@ -105,13 +108,14 @@ SettingItem
onActivated: onActivated:
{ {
forceActiveFocus(); forceActiveFocus();
provider.setPropertyValue("value", extruders_model.getItem(index).index) propertyProvider.setPropertyValue("value", extruders_model.getItem(index).index);
control.color = extruders_model.getItem(index).color;
} }
onModelChanged: updateCurrentIndex(); onModelChanged: updateCurrentIndex();
Connections Connections
{ {
target: provider target: propertyProvider
onPropertiesChanged: control.updateCurrentIndex(); onPropertiesChanged: control.updateCurrentIndex();
} }
@ -119,9 +123,10 @@ SettingItem
{ {
for(var i = 0; i < extruders_model.rowCount(); ++i) for(var i = 0; i < extruders_model.rowCount(); ++i)
{ {
if(extruders_model.getItem(i).index == provider.properties.value) if(extruders_model.getItem(i).index == propertyProvider.properties.value)
{ {
currentIndex = i; control.currentIndex = i;
control.color = extruders_model.getItem(i).color;
return; return;
} }
} }

View File

@ -138,7 +138,7 @@ Item {
{ {
id: linkedSettingIcon; id: linkedSettingIcon;
visible: base.settablePerExtruder != "True" && base.showLinkedSettingIcon visible: Cura.MachineManager.activeStackId != Cura.MachineManager.activeMachineId && base.settablePerExtruder != "True" && base.showLinkedSettingIcon
height: parent.height; height: parent.height;
width: height; width: height;

View File

@ -100,7 +100,7 @@ SettingItem
maximumLength: 10; maximumLength: 10;
validator: RegExpValidator { regExp: /[0-9.,-]{0,10}/ } validator: RegExpValidator { regExp: (definition.type == "int") ? /^-?[0-9]{0,10}/ : /^-?[0-9.,]{0,10}/ } // definition.type property from parent loader used to disallow fractional number entry
Binding Binding
{ {

View File

@ -125,7 +125,7 @@ ScrollView
{ {
id: provider id: provider
containerStackId: delegate.stackId containerStackId: Cura.MachineManager.activeMachineId
key: model.key ? model.key : "" key: model.key ? model.key : ""
watchedProperties: [ "value", "enabled", "state", "validationState", "settable_per_extruder" ] watchedProperties: [ "value", "enabled", "state", "validationState", "settable_per_extruder" ]
storeIndex: 0 storeIndex: 0

View File

@ -28,6 +28,7 @@ Rectangle
// Is there an output device for this printer? // Is there an output device for this printer?
property bool printerConnected: Cura.MachineManager.printerOutputDevices.length != 0 property bool printerConnected: Cura.MachineManager.printerOutputDevices.length != 0
property bool printerAcceptsCommands: printerConnected && Cura.MachineManager.printerOutputDevices[0].acceptsCommands
color: UM.Theme.getColor("sidebar"); color: UM.Theme.getColor("sidebar");
UM.I18nCatalog { id: catalog; name:"cura"} UM.I18nCatalog { id: catalog; name:"cura"}
@ -107,18 +108,29 @@ Rectangle
onClicked: monitoringPrint = true onClicked: monitoringPrint = true
iconSource: { iconSource: {
if(!printerConnected) if(!printerConnected)
return UM.Theme.getIcon("tab_monitor") return UM.Theme.getIcon("tab_monitor");
else if(Cura.MachineManager.printerOutputDevices[0].jobState == "printing" || Cura.MachineManager.printerOutputDevices[0].jobState == "pre_print") else if(!printerAcceptsCommands)
return UM.Theme.getIcon("tab_monitor_busy") return UM.Theme.getIcon("tab_monitor_unknown");
else if(Cura.MachineManager.printerOutputDevices[0].jobState == "ready" || Cura.MachineManager.printerOutputDevices[0].jobState == "")
switch(Cura.MachineManager.printerOutputDevices[0].jobState)
{
case "printing":
case "pre_print":
case "wait_cleanup":
return UM.Theme.getIcon("tab_monitor_busy");
case "ready":
case "":
return UM.Theme.getIcon("tab_monitor_connected") return UM.Theme.getIcon("tab_monitor_connected")
else if(Cura.MachineManager.printerOutputDevices[0].jobState == "paused") case "paused":
return UM.Theme.getIcon("tab_monitor_paused") return UM.Theme.getIcon("tab_monitor_paused")
else if (Cura.MachineManager.printerOutputDevices[0].jobState == "error") case "error":
return UM.Theme.getIcon("tab_monitor_stopped") return UM.Theme.getIcon("tab_monitor_stopped")
else case "offline":
return UM.Theme.getIcon("tab_monitor_offline")
default:
return UM.Theme.getIcon("tab_monitor") return UM.Theme.getIcon("tab_monitor")
} }
}
checkable: true checkable: true
checked: monitoringPrint checked: monitoringPrint
exclusiveGroup: sidebarHeaderBarGroup exclusiveGroup: sidebarHeaderBarGroup

View File

@ -138,7 +138,7 @@ Column
anchors.leftMargin: (parent.height - height) / 2 anchors.leftMargin: (parent.height - height) / 2
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
color: model.colour color: model.color
border.width: UM.Theme.getSize("default_lining").width border.width: UM.Theme.getSize("default_lining").width
border.color: UM.Theme.getColor("toggle_checked") border.color: UM.Theme.getColor("toggle_checked")
} }

View File

@ -216,7 +216,7 @@ Item
anchors.verticalCenter: brimCheckBox.verticalCenter anchors.verticalCenter: brimCheckBox.verticalCenter
width: parent.width / 100 * 35 - 3 * UM.Theme.getSize("default_margin").width width: parent.width / 100 * 35 - 3 * UM.Theme.getSize("default_margin").width
//: Bed adhesion label //: Bed adhesion label
text: catalog.i18nc("@label:listbox", "Bed Adhesion:"); text: catalog.i18nc("@label", "Helper Parts:");
font: UM.Theme.getFont("default"); font: UM.Theme.getFont("default");
color: UM.Theme.getColor("text"); color: UM.Theme.getColor("text");
} }
@ -264,7 +264,7 @@ Item
anchors.verticalCenter: supportCheckBox.verticalCenter anchors.verticalCenter: supportCheckBox.verticalCenter
width: parent.width / 100 * 35 - 3 * UM.Theme.getSize("default_margin").width width: parent.width / 100 * 35 - 3 * UM.Theme.getSize("default_margin").width
//: Support label //: Support label
text: catalog.i18nc("@label:listbox", "Support:"); text: "";
font: UM.Theme.getFont("default"); font: UM.Theme.getFont("default");
color: UM.Theme.getColor("text"); color: UM.Theme.getColor("text");
} }
@ -327,7 +327,8 @@ Item
supportEnabled.setPropertyValue("value", false); supportEnabled.setPropertyValue("value", false);
} else { } else {
supportEnabled.setPropertyValue("value", true); supportEnabled.setPropertyValue("value", true);
supportExtruderNr.setPropertyValue("value", index - 1); // Send the extruder nr as a string.
supportExtruderNr.setPropertyValue("value", String(index - 1));
} }
} }
MouseArea { MouseArea {
@ -372,7 +373,7 @@ Item
for(var extruderNumber = 0; extruderNumber < extruders.model.rowCount() ; extruderNumber++) { for(var extruderNumber = 0; extruderNumber < extruders.model.rowCount() ; extruderNumber++) {
extruderModel.append({ extruderModel.append({
text: catalog.i18nc("@label", "Print using %1").arg(extruders.model.getItem(extruderNumber).name), text: catalog.i18nc("@label", "Print using %1").arg(extruders.model.getItem(extruderNumber).name),
color: extruders.model.getItem(extruderNumber).colour color: extruders.model.getItem(extruderNumber).color
}) })
} }
} }

View File

@ -1,7 +1,7 @@
[shaders] [shaders]
vertex = vertex =
uniform highp mat4 u_viewProjectionMatrix;
uniform highp mat4 u_modelMatrix; uniform highp mat4 u_modelMatrix;
uniform highp mat4 u_viewProjectionMatrix;
uniform highp mat4 u_normalMatrix; uniform highp mat4 u_normalMatrix;
attribute highp vec4 a_vertex; attribute highp vec4 a_vertex;
@ -10,7 +10,6 @@ vertex =
varying highp vec3 v_vertex; varying highp vec3 v_vertex;
varying highp vec3 v_normal; varying highp vec3 v_normal;
varying highp vec2 v_uvs;
void main() void main()
{ {
@ -19,56 +18,47 @@ vertex =
v_vertex = world_space_vert.xyz; v_vertex = world_space_vert.xyz;
v_normal = (u_normalMatrix * normalize(a_normal)).xyz; v_normal = (u_normalMatrix * normalize(a_normal)).xyz;
v_uvs = a_uvs;
} }
fragment = fragment =
uniform mediump vec4 u_ambientColor; uniform mediump vec4 u_ambientColor;
uniform mediump vec4 u_diffuseColor; uniform mediump vec4 u_diffuseColor;
uniform highp vec3 u_lightPosition; uniform highp vec3 u_lightPosition;
uniform highp vec3 u_viewPosition;
uniform mediump float u_opacity; uniform mediump float u_opacity;
uniform sampler2D u_texture;
varying highp vec3 v_vertex; varying highp vec3 v_vertex;
varying highp vec3 v_normal; varying highp vec3 v_normal;
varying highp vec2 v_uvs;
void main() void main()
{ {
// Copied from platform.shader, removed texture mediump vec4 finalColor = vec4(0.0);
mediump vec4 final_color = vec4(0.0);
/* Ambient Component */ /* Ambient Component */
final_color += u_ambientColor; finalColor += u_ambientColor;
highp vec3 normal = normalize(v_normal); highp vec3 normal = normalize(v_normal);
highp vec3 light_dir = normalize(u_lightPosition - v_vertex); highp vec3 lightDir = normalize(u_lightPosition - v_vertex);
/* Diffuse Component */ /* Diffuse Component */
highp float n_dot_l = clamp(dot(normal, light_dir), 0.0, 1.0); highp float NdotL = clamp(abs(dot(normal, lightDir)), 0.0, 1.0);
final_color += (n_dot_l * u_diffuseColor); finalColor += (NdotL * u_diffuseColor);
final_color.a = u_opacity; gl_FragColor = finalColor;
gl_FragColor.a = u_opacity;
gl_FragColor = final_color;
} }
[defaults] [defaults]
u_ambientColor = [0.3, 0.3, 0.3, 1.0] u_ambientColor = [0.1, 0.1, 0.1, 1.0]
u_diffuseColor = [1.0, 1.0, 1.0, 1.0] u_diffuseColor = [0.4, 0.4, 0.4, 1.0]
u_opacity = 0.5 u_opacity = 0.5
u_texture = 0
[bindings] [bindings]
u_viewProjectionMatrix = view_projection_matrix
u_modelMatrix = model_matrix u_modelMatrix = model_matrix
u_viewProjectionMatrix = view_projection_matrix
u_normalMatrix = normal_matrix u_normalMatrix = normal_matrix
u_lightPosition = light_0_position u_lightPosition = light_0_position
u_viewPosition = camera_position
[attributes] [attributes]
a_vertex = vertex a_vertex = vertex
a_normal = normal a_normal = normal
a_uvs = uv0

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 760 KiB

View File

@ -1,13 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> <!-- Generator: Adobe Illustrator 16.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" <svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
width="512px" height="512px" viewBox="0 0 512 512" enable-background="new 0 0 512 512" xml:space="preserve"> viewBox="0 0 30 30" xml:space="preserve">
<path d="M240.168,503.311c-60.098,21.117-109.643-3.1-99.704-60.816c9.942-57.73,66.971-181.316,75.107-204.688 <path d="M13.628 29.557c-3.53 1.24-6.44-.182-5.857-3.572.584-3.39 3.934-10.65 4.412-12.023.477-1.373-.438-1.75-1.42-1.19-.566.326-1.408.98-2.13 1.617-.2-.404-.482-.865-.694-1.306 1.18-1.181 3.15-2.765 5.482-3.339 2.787-.688 7.446.413 5.444 5.739-1.43 3.797-2.441 6.417-3.078 8.372-.637 1.952.125 2.365 1.239 1.6.87-.594 1.798-1.403 2.478-2.03.315.513.413.676.726 1.264-1.185 1.2-4.27 4.042-6.598 4.868h-.004zM20.942 6.071c-1.601 1.363-3.974 1.333-5.302-.067-1.328-1.399-1.106-3.638.495-5.001 1.6-1.363 3.974-1.333 5.306.062 1.323 1.399 1.1 3.639-.501 5.006h.002z"/>
c8.129-23.371-7.46-29.777-24.172-20.267c-9.638,5.56-23.963,16.704-36.26,27.535c-3.41-6.866-8.206-14.712-11.807-22.226
c20.067-20.109,53.61-47.07,93.318-56.841c47.445-11.711,126.759,7.031,92.673,97.695c-24.338,64.636-41.549,109.239-52.396,142.524
c-10.84,33.233,2.13,40.266,21.092,27.25c14.825-10.123,30.618-23.892,42.195-34.568c5.357,8.733,7.03,11.505,12.356,21.516
c-20.175,20.447-72.704,68.808-112.33,82.886H240.168z"/>
<path d="M364.689,103.474c-27.258,23.203-67.66,22.7-90.263-1.134c-22.598-23.819-18.828-61.943,8.426-85.149
c27.254-23.2,67.665-22.697,90.328,1.065c22.531,23.805,18.745,61.939-8.524,85.218H364.689z"/>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 960 B

View File

@ -0,0 +1,97 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:sketch="http://www.bohemiancoding.com/sketch/ns"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="30"
height="30"
viewBox="0 0 30 30"
version="1.1"
id="svg2"
inkscape:version="0.91 r13725"
sodipodi:docname="tab_monitor_unavailable.svg">
<metadata
id="metadata15">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title>Fill 1 Copy 3</dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<sodipodi:namedview
pagecolor="#000000"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1920"
inkscape:window-height="1148"
id="namedview13"
showgrid="false"
showborder="true"
inkscape:zoom="12.987195"
inkscape:cx="14.346643"
inkscape:cy="15.151358"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="svg2"
inkscape:snap-global="true"
inkscape:object-nodes="false"
inkscape:snap-smooth-nodes="false"
inkscape:snap-midpoints="false"
inkscape:snap-intersection-paths="false"
inkscape:snap-bbox="true"
inkscape:snap-others="false"
inkscape:snap-nodes="false" />
<!-- Generator: Sketch 3.4.4 (17249) - http://www.bohemiancoding.com/sketch -->
<title
id="title4">Fill 1 Copy 3</title>
<desc
id="desc6">Created with Sketch.</desc>
<defs
id="defs8" />
<g
id="Page-1"
sketch:type="MSPage"
style="fill:none;fill-rule:evenodd;stroke:none;stroke-width:1"
transform="matrix(1.3157895,0,0,1.3157895,2.5,2.4999995)">
<g
id="HIG"
sketch:type="MSArtboardGroup"
transform="translate(-718,-2432)"
style="fill:#ffffff">
<path
d="m 718,2432 19,0 0,0.9048 -19,0 0,-0.9048 0,0 z m 0,18.0952 1.73776,0 1.7267,-0.9047 12.13477,0 1.69775,0.9047 1.70302,0 0,0.9048 -1.70166,0 -1.69911,-0.9048 -12.13593,0 -1.72554,0.8949 L 718,2451 l 0,-0.9048 0,0 z m 18.13636,-17.1904 0.86364,0 0,17.1904 -0.86364,0 0,-17.1904 0,0 z m -18.13636,0 0.86364,0 0,17.1904 -0.86364,0 0,-17.1904 0,0 z m 2.59091,1.8095 13.81818,0 0,12.6667 -13.81818,0 0,-12.6667 0,0 z m 0.86364,0.9047 12.0909,0 0,10.8572 -12.0909,0 0,-10.8572 0,0 z m 4.31818,0 3.45454,0 0,2.7143 -3.45454,0 0,-2.7143 0,0 z m -2.59091,9.9524 8.63636,0 0,0.9048 -8.63636,0 0,-0.9048 0,0 z m 3.45454,-7.2381 1.72728,0 0,0.9048 -1.72728,0 0,-0.9048 0,0 z"
id="Fill-1-Copy-3"
sketch:type="MSShapeGroup"
inkscape:connector-curvature="0" />
</g>
</g>
<circle
style="fill:#7f7f7f;fill-opacity:1"
id="path3337"
cx="22.5"
cy="7.5"
r="7.5" />
<g
style="font-style:normal;font-weight:normal;font-size:10.78604317px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
id="text3339">
<path
d="m 25.53769,5.6750499 q 0,0.5319289 -0.157999,0.9427255 -0.152732,0.4055299 -0.43713,0.7057274 -0.284397,0.3001975 -0.68466,0.5371955 -0.394997,0.236998 -0.895326,0.4318631 l 0,1.2007899 -1.853851,0 0,-1.7748518 q 0.37393,-0.1000658 0.674127,-0.2053983 0.305464,-0.1053324 0.637262,-0.3423305 0.31073,-0.2106649 0.484529,-0.4897959 0.179065,-0.279131 0.179065,-0.6319947 0,-0.5266622 -0.34233,-0.7478604 -0.337064,-0.2264648 -0.953259,-0.2264648 -0.379197,0 -0.85846,0.1632653 -0.473996,0.1632653 -0.868992,0.4213298 l -0.210665,0 0,-1.6063199 q 0.337064,-0.1421988 1.037524,-0.2949308 0.700461,-0.1579987 1.421988,-0.1579987 1.300856,0 2.064517,0.5740619 0.76366,0.5740618 0.76366,1.5009874 z M 23.51004,11.6 l -2.127715,0 0,-1.390388 2.127715,0 0,1.390388 z"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-family:Verdana;-inkscape-font-specification:'Verdana Bold';fill:#ffffff"
id="path4144" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.4 KiB