mirror of
https://github.com/Ultimaker/Cura.git
synced 2025-08-06 13:34:01 -06:00
Merge remote-tracking branch 'refs/remotes/Ultimaker/master'
This commit is contained in:
commit
35e1062eb9
90 changed files with 12299 additions and 872 deletions
|
@ -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_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)
|
||||
|
||||
# Macro needed to list all sub-directory of a directory.
|
||||
|
@ -76,7 +77,7 @@ if(NOT APPLE AND NOT WIN32)
|
|||
FILES_MATCHING PATTERN *.py)
|
||||
install(FILES ${CMAKE_BINARY_DIR}/CuraVersion.py
|
||||
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)
|
||||
install(FILES cura.sharedmimeinfo
|
||||
DESTINATION ${CMAKE_INSTALL_DATADIR}/mime/packages/
|
||||
|
|
|
@ -5,9 +5,9 @@ Name[de]=Cura
|
|||
GenericName=3D Printing 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.
|
||||
Exec=/usr/bin/cura %F
|
||||
TryExec=/usr/bin/cura
|
||||
Icon=/usr/share/cura/resources/images/cura-icon.png
|
||||
Exec=@CMAKE_INSTALL_FULL_BINDIR@/cura %F
|
||||
TryExec=@CMAKE_INSTALL_FULL_BINDIR@/cura
|
||||
Icon=@CMAKE_INSTALL_FULL_DATADIR@/cura/resources/images/cura-icon.png
|
||||
Terminal=false
|
||||
Type=Application
|
||||
MimeType=application/sla;application/vnd.ms-3mfdocument;application/prs.wavefront-obj;image/bmp;image/gif;image/jpeg;image/png
|
|
@ -208,7 +208,7 @@ class BuildVolume(SceneNode):
|
|||
"@info:status",
|
||||
"The build volume height has been reduced due to the value of the"
|
||||
" \"Print Sequence\" setting to prevent the gantry from colliding"
|
||||
" with printed objects."), lifetime=10).show()
|
||||
" with printed models."), lifetime=10).show()
|
||||
|
||||
def getRaftThickness(self):
|
||||
return self._raft_thickness
|
||||
|
@ -265,7 +265,7 @@ class BuildVolume(SceneNode):
|
|||
self._height = self._active_container_stack.getProperty("machine_height", "value")
|
||||
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()
|
||||
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"]
|
||||
_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"]
|
||||
|
|
|
@ -30,9 +30,10 @@ class ConvexHullDecorator(SceneNodeDecorator):
|
|||
|
||||
def setNode(self, node):
|
||||
previous_node = self._node
|
||||
# Disconnect from previous node signals
|
||||
if previous_node is not None and node is not previous_node:
|
||||
previous_node.transformationChanged.connect(self._onChanged)
|
||||
previous_node.parentChanged.connect(self._onChanged)
|
||||
previous_node.transformationChanged.disconnect(self._onChanged)
|
||||
previous_node.parentChanged.disconnect(self._onChanged)
|
||||
|
||||
super().setNode(node)
|
||||
|
||||
|
@ -286,5 +287,5 @@ class ConvexHullDecorator(SceneNodeDecorator):
|
|||
|
||||
_affected_settings = [
|
||||
"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"]
|
||||
|
|
|
@ -23,7 +23,7 @@ class ConvexHullNode(SceneNode):
|
|||
self._original_parent = parent
|
||||
|
||||
# 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.
|
||||
self._mesh_height = 0.1
|
||||
|
@ -77,7 +77,7 @@ class ConvexHullNode(SceneNode):
|
|||
convex_hull_head = self._node.callDecoration("getConvexHullHead")
|
||||
if convex_hull_head:
|
||||
convex_hull_head_builder = MeshBuilder()
|
||||
convex_hull_head_builder.addConvexPolygon(convex_hull_head.getPoints(), self._mesh_height-self._thickness)
|
||||
convex_hull_head_builder.addConvexPolygon(convex_hull_head.getPoints(), self._mesh_height - self._thickness)
|
||||
self._convex_hull_head_mesh = convex_hull_head_builder.build()
|
||||
|
||||
if not node:
|
||||
|
|
|
@ -28,6 +28,7 @@ from cura.SetParentOperation import SetParentOperation
|
|||
|
||||
from UM.Settings.SettingDefinition import SettingDefinition, DefinitionPropertyType
|
||||
from UM.Settings.ContainerRegistry import ContainerRegistry
|
||||
from UM.Settings.SettingFunction import SettingFunction
|
||||
|
||||
from UM.i18n import i18nCatalog
|
||||
|
||||
|
@ -57,21 +58,12 @@ import copy
|
|||
import urllib
|
||||
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:
|
||||
from cura.CuraVersion import CuraVersion, CuraBuildType
|
||||
except ImportError:
|
||||
CuraVersion = "master" # [CodeStyle: Reflecting imported value]
|
||||
CuraBuildType = ""
|
||||
|
||||
|
||||
class CuraApplication(QtApplication):
|
||||
class ResourceTypes:
|
||||
QmlFiles = Resources.UserType + 1
|
||||
|
@ -98,7 +90,10 @@ class CuraApplication(QtApplication):
|
|||
SettingDefinition.addSupportedProperty("settable_per_meshgroup", DefinitionPropertyType.Any, default = True)
|
||||
SettingDefinition.addSupportedProperty("settable_globally", DefinitionPropertyType.Any, default = True)
|
||||
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.
|
||||
Resources.addStorageType(self.ResourceTypes.QualityInstanceContainer, "quality")
|
||||
|
@ -128,6 +123,8 @@ class CuraApplication(QtApplication):
|
|||
self._machine_action_manager = MachineActionManager.MachineActionManager()
|
||||
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)
|
||||
|
||||
self.setWindowIcon(QIcon(Resources.getPath(Resources.Images, "cura-icon.png")))
|
||||
|
@ -201,7 +198,7 @@ class CuraApplication(QtApplication):
|
|||
"dialog_profile_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")
|
||||
|
||||
|
@ -233,13 +230,20 @@ class CuraApplication(QtApplication):
|
|||
support
|
||||
support_enable
|
||||
support_type
|
||||
support_roof_density
|
||||
support_interface_density
|
||||
platform_adhesion
|
||||
adhesion_type
|
||||
brim_width
|
||||
raft_airgap
|
||||
layer_0_z_overlap
|
||||
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
|
||||
blackmagic
|
||||
print_sequence
|
||||
|
@ -553,12 +557,18 @@ class CuraApplication(QtApplication):
|
|||
def deleteSelection(self):
|
||||
if not self.getController().getToolsEnabled():
|
||||
return
|
||||
|
||||
removed_group_nodes = []
|
||||
op = GroupedOperation()
|
||||
nodes = Selection.getAllSelectedObjects()
|
||||
for node in nodes:
|
||||
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()
|
||||
|
||||
pass
|
||||
|
@ -584,8 +594,7 @@ class CuraApplication(QtApplication):
|
|||
op.push()
|
||||
if group_node:
|
||||
if len(group_node.getChildren()) == 1 and group_node.callDecoration("isGroup"):
|
||||
group_node.getChildren()[0].translate(group_node.getPosition())
|
||||
group_node.getChildren()[0].setParent(group_node.getParent())
|
||||
op.addOperation(SetParentOperation(group_node.getChildren()[0], group_node.getParent()))
|
||||
op = RemoveSceneNodeOperation(group_node)
|
||||
op.push()
|
||||
|
||||
|
@ -626,7 +635,23 @@ class CuraApplication(QtApplication):
|
|||
if node:
|
||||
op = SetTransformOperation(node, Vector())
|
||||
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.
|
||||
@pyqtSlot()
|
||||
def deleteAll(self):
|
||||
|
@ -650,6 +675,7 @@ class CuraApplication(QtApplication):
|
|||
op.addOperation(RemoveSceneNodeOperation(node))
|
||||
|
||||
op.push()
|
||||
Selection.clear()
|
||||
|
||||
## Reset all translation on nodes with mesh data.
|
||||
@pyqtSlot()
|
||||
|
@ -876,4 +902,22 @@ class CuraApplication(QtApplication):
|
|||
self.getMainWindow().setMinimumSize(size)
|
||||
|
||||
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)
|
||||
|
|
|
@ -44,6 +44,7 @@ class MachineAction(QObject, PluginObject):
|
|||
# /sa _reset
|
||||
@pyqtSlot()
|
||||
def reset(self):
|
||||
self._component = None
|
||||
self._finished = False
|
||||
self._reset()
|
||||
|
||||
|
|
|
@ -57,9 +57,10 @@ class MachineActionManager(QObject):
|
|||
def addRequiredAction(self, definition_id, action_key):
|
||||
if action_key in self._machine_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:
|
||||
self._required_actions[definition_id] = {self._machine_actions[action_key]}
|
||||
self._required_actions[definition_id] = [self._machine_actions[action_key]]
|
||||
else:
|
||||
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):
|
||||
if action_key in self._machine_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:
|
||||
self._supported_actions[definition_id] = {self._machine_actions[action_key]}
|
||||
self._supported_actions[definition_id] = [self._machine_actions[action_key]]
|
||||
else:
|
||||
Logger.log("w", "Unable to add %s to %s, as the action is not recognised", action_key, definition_id)
|
||||
|
||||
|
|
|
@ -71,7 +71,6 @@ class PlatformPhysics:
|
|||
# If there is no convex hull for the node, start calculating it and continue.
|
||||
if not node.getDecorator(ConvexHullDecorator):
|
||||
node.addDecorator(ConvexHullDecorator())
|
||||
node.callDecoration("recomputeConvexHull")
|
||||
|
||||
if Preferences.getInstance().getValue("physics/automatic_push_free"):
|
||||
# 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.
|
||||
head_hull = node.callDecoration("getConvexHullHead")
|
||||
if head_hull:
|
||||
overlap = head_hull.intersectsPolygon(other_node.callDecoration("getConvexHull"))
|
||||
overlap = head_hull.intersectsPolygon(other_node.callDecoration("getConvexHullHead"))
|
||||
if not overlap:
|
||||
other_head_hull = other_node.callDecoration("getConvexHullHead")
|
||||
if other_head_hull:
|
||||
overlap = node.callDecoration("getConvexHull").intersectsPolygon(other_head_hull)
|
||||
overlap = node.callDecoration("getConvexHullHead").intersectsPolygon(other_head_hull)
|
||||
else:
|
||||
own_convex_hull = node.callDecoration("getConvexHull")
|
||||
other_convex_hull = other_node.callDecoration("getConvexHull")
|
||||
|
|
|
@ -31,10 +31,13 @@ class PrinterOutputDevice(QObject, OutputDevice):
|
|||
self._head_y = 0
|
||||
self._head_z = 0
|
||||
self._connection_state = ConnectionState.closed
|
||||
self._connection_text = ""
|
||||
self._time_elapsed = 0
|
||||
self._time_total = 0
|
||||
self._job_state = ""
|
||||
self._job_name = ""
|
||||
self._error_text = ""
|
||||
self._accepts_commands = True
|
||||
|
||||
def requestWrite(self, node, file_name = None, filter_by_machine = False):
|
||||
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)
|
||||
connectionStateChanged = pyqtSignal(str)
|
||||
|
||||
connectionTextChanged = pyqtSignal()
|
||||
|
||||
timeElapsedChanged = pyqtSignal()
|
||||
|
||||
timeTotalChanged = pyqtSignal()
|
||||
|
@ -77,6 +82,10 @@ class PrinterOutputDevice(QObject, OutputDevice):
|
|||
|
||||
jobNameChanged = pyqtSignal()
|
||||
|
||||
errorTextChanged = pyqtSignal()
|
||||
|
||||
acceptsCommandsChanged = pyqtSignal()
|
||||
|
||||
@pyqtProperty(str, notify = jobStateChanged)
|
||||
def jobState(self):
|
||||
return self._job_state
|
||||
|
@ -102,6 +111,26 @@ class PrinterOutputDevice(QObject, OutputDevice):
|
|||
self._job_name = name
|
||||
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)
|
||||
# This function is "final" (do not re-implement)
|
||||
# /sa _getBedTemperature implementation function
|
||||
|
@ -266,6 +295,16 @@ class PrinterOutputDevice(QObject, OutputDevice):
|
|||
self._connection_state = connection_state
|
||||
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
|
||||
def __del__(self):
|
||||
self.close()
|
||||
|
|
|
@ -244,6 +244,7 @@ class ContainerManager(QObject):
|
|||
if not type_name or entry["type"] == type_name:
|
||||
filters.append(filter_string)
|
||||
|
||||
filters.append("All Files (*)")
|
||||
return filters
|
||||
|
||||
## Export a container to a file
|
||||
|
@ -280,6 +281,9 @@ class ContainerManager(QObject):
|
|||
return { "status": "error", "message": "Container not found"}
|
||||
container = containers[0]
|
||||
|
||||
if UM.Platform.isOSX() and "." in file_url:
|
||||
file_url = file_url[:file_url.rfind(".")]
|
||||
|
||||
for suffix in mime_type.suffixes:
|
||||
if file_url.endswith(suffix):
|
||||
break
|
||||
|
@ -301,7 +305,7 @@ class ContainerManager(QObject):
|
|||
with UM.SaveFile(file_url, "w") as f:
|
||||
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
|
||||
#
|
||||
|
@ -371,11 +375,20 @@ class ContainerManager(QObject):
|
|||
"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:
|
||||
if suffix == mime_type.preferredSuffix:
|
||||
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
|
||||
|
||||
name_filter = "{0} ({1})".format(mime_type.comment, suffix_list)
|
||||
|
|
|
@ -41,7 +41,6 @@ class ContainerSettingsModel(ListModel):
|
|||
keys = keys + list(container.getAllKeys())
|
||||
|
||||
keys = list(set(keys)) # remove duplicate keys
|
||||
keys.sort()
|
||||
|
||||
for key in keys:
|
||||
definition = None
|
||||
|
@ -72,6 +71,7 @@ class ContainerSettingsModel(ListModel):
|
|||
"unit": definition.unit,
|
||||
"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.
|
||||
# Also makes sure the model updates when the containers have property changes
|
||||
|
|
|
@ -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.Logger
|
||||
import UM.Settings.ContainerRegistry #Finding containers by ID.
|
||||
import UM.Settings.SettingFunction
|
||||
|
||||
|
||||
## 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 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
|
||||
# manager.
|
||||
#
|
||||
|
@ -273,3 +283,54 @@ class ExtruderManager(QObject):
|
|||
global_stack = UM.Application.getInstance().getGlobalContainerStack()
|
||||
if global_stack and global_stack.getBottom():
|
||||
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
|
||||
|
|
|
@ -20,7 +20,7 @@ class ExtrudersModel(UM.Qt.ListModel.ListModel):
|
|||
NameRole = Qt.UserRole + 2
|
||||
|
||||
## 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.
|
||||
#
|
||||
|
@ -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
|
||||
# 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
|
||||
# changes in the data.
|
||||
|
@ -42,7 +42,7 @@ class ExtrudersModel(UM.Qt.ListModel.ListModel):
|
|||
|
||||
self.addRoleName(self.IdRole, "id")
|
||||
self.addRoleName(self.NameRole, "name")
|
||||
self.addRoleName(self.ColourRole, "colour")
|
||||
self.addRoleName(self.ColorRole, "color")
|
||||
self.addRoleName(self.IndexRole, "index")
|
||||
|
||||
self._add_global = False
|
||||
|
@ -104,11 +104,11 @@ class ExtrudersModel(UM.Qt.ListModel.ListModel):
|
|||
if global_container_stack:
|
||||
if self._add_global:
|
||||
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 = {
|
||||
"id": global_container_stack.getId(),
|
||||
"name": "Global",
|
||||
"colour": colour,
|
||||
"color": color,
|
||||
"index": -1
|
||||
}
|
||||
self.appendItem(item)
|
||||
|
@ -125,12 +125,12 @@ class ExtrudersModel(UM.Qt.ListModel.ListModel):
|
|||
position = int(position)
|
||||
except ValueError: #Not a proper int.
|
||||
position = -1
|
||||
default_colour = self.defaultColours[position] if position >= 0 and position < len(self.defaultColours) else self.defaultColours[0]
|
||||
colour = material.getMetaDataEntry("color_code", default = default_colour) if material else default_colour
|
||||
default_color = self.defaultColors[position] if position >= 0 and position < len(self.defaultColors) else self.defaultColors[0]
|
||||
color = material.getMetaDataEntry("color_code", default = default_color) if material else default_color
|
||||
item = { #Construct an item with only the relevant information.
|
||||
"id": extruder.getId(),
|
||||
"name": extruder_name,
|
||||
"colour": colour,
|
||||
"color": color,
|
||||
"index": position
|
||||
}
|
||||
self.appendItem(item)
|
||||
|
|
|
@ -26,7 +26,7 @@ class MachineManager(QObject):
|
|||
self._global_container_stack = None
|
||||
|
||||
Application.getInstance().globalContainerStackChanged.connect(self._onGlobalContainerChanged)
|
||||
self._global_stack_valid = None
|
||||
self._active_stack_valid = None
|
||||
self._onGlobalContainerChanged()
|
||||
|
||||
ExtruderManager.getInstance().activeExtruderChanged.connect(self._onActiveExtruderStackChanged)
|
||||
|
@ -74,7 +74,7 @@ class MachineManager(QObject):
|
|||
activeStackChanged = pyqtSignal()
|
||||
|
||||
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
|
||||
|
||||
|
@ -165,7 +165,7 @@ class MachineManager(QObject):
|
|||
# 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()
|
||||
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)
|
||||
|
||||
else:
|
||||
|
@ -269,25 +269,29 @@ class MachineManager(QObject):
|
|||
if property_name == "global_inherits_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
|
||||
stack_index = int(self._active_container_stack.getProperty(key, property_name))
|
||||
extruder_stacks = [stack for stack in ExtruderManager.getInstance().getMachineExtruders(self._global_container_stack.getId())]
|
||||
extruder_stacks = list(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 = extruder_stacks[stack_index].getProperty(key, "value")
|
||||
if self._global_container_stack.getProperty(key, "value") != new_value:
|
||||
self._global_container_stack.getTop().setProperty(key, "value", new_value)
|
||||
new_value = target_stack.getProperty(key, "value")
|
||||
if self._global_container_stack.getProperty(key, "value") != new_value:
|
||||
self._global_container_stack.getTop().setProperty(key, "value", new_value)
|
||||
|
||||
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)
|
||||
if changed_validation_state in (UM.Settings.ValidatorState.Exception, UM.Settings.ValidatorState.MaximumError, UM.Settings.ValidatorState.MinimumError):
|
||||
self._global_stack_valid = False
|
||||
self.globalValidationChanged.emit()
|
||||
self._active_stack_valid = False
|
||||
self.activeValidationChanged.emit()
|
||||
else:
|
||||
has_errors = self._checkStackForErrors(self._active_container_stack)
|
||||
if not has_errors:
|
||||
self._global_stack_valid = True
|
||||
self.globalValidationChanged.emit()
|
||||
self._active_stack_valid = True
|
||||
self.activeValidationChanged.emit()
|
||||
|
||||
def _onGlobalContainerChanged(self):
|
||||
if self._global_container_stack:
|
||||
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.containersChanged.connect(self._onInstanceContainersChanged)
|
||||
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.nameChanged.connect(self._onMaterialNameChanged)
|
||||
|
||||
|
@ -329,6 +331,8 @@ class MachineManager(QObject):
|
|||
self._active_container_stack.propertyChanged.connect(self._onGlobalPropertyChanged)
|
||||
else:
|
||||
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):
|
||||
container_type = container.getMetaDataEntry("type")
|
||||
|
@ -433,11 +437,11 @@ class MachineManager(QObject):
|
|||
return len(user_settings) != 0
|
||||
|
||||
## 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
|
||||
@pyqtProperty(bool, notify = globalValidationChanged)
|
||||
def isGlobalStackValid(self):
|
||||
return bool(self._global_stack_valid)
|
||||
@pyqtProperty(bool, notify = activeValidationChanged)
|
||||
def isActiveStackValid(self):
|
||||
return bool(self._active_stack_valid)
|
||||
|
||||
@pyqtProperty(str, notify = activeStackChanged)
|
||||
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.
|
||||
activate_new_machine = (self._global_container_stack and self._global_container_stack.getId() == machine_id)
|
||||
|
||||
stacks = UM.Settings.ContainerRegistry.getInstance().findContainerStacks(id = machine_id)
|
||||
if not stacks:
|
||||
return
|
||||
ExtruderManager.getInstance().removeMachineExtruders(stacks[0].getBottom().getId())
|
||||
ExtruderManager.getInstance().removeMachineExtruders(machine_id)
|
||||
|
||||
containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(type = "user", machine = machine_id)
|
||||
for container in containers:
|
||||
|
|
|
@ -10,6 +10,8 @@ from UM.Settings.InstanceContainer import InstanceContainer
|
|||
from UM.Settings.ContainerRegistry import ContainerRegistry
|
||||
import UM.Logger
|
||||
|
||||
import cura.Settings
|
||||
|
||||
from UM.Application import Application
|
||||
|
||||
## 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._instance = InstanceContainer(container_id = "SettingOverrideInstanceContainer")
|
||||
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)
|
||||
|
||||
|
@ -41,6 +47,10 @@ class SettingOverrideDecorator(SceneNodeDecorator):
|
|||
deep_copy = SettingOverrideDecorator()
|
||||
## Copy the instance
|
||||
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.
|
||||
deep_copy._stack.replaceContainer(0, deep_copy._instance)
|
||||
return deep_copy
|
||||
|
@ -61,7 +71,11 @@ class SettingOverrideDecorator(SceneNodeDecorator):
|
|||
if self._extruder_stack:
|
||||
extruder_stack = ContainerRegistry.getInstance().findContainerStacks(id = self._extruder_stack)
|
||||
if extruder_stack:
|
||||
old_extruder_stack_id = self._stack.getNextStack().getId()
|
||||
if self._stack.getNextStack():
|
||||
old_extruder_stack_id = self._stack.getNextStack().getId()
|
||||
else:
|
||||
old_extruder_stack_id = ""
|
||||
|
||||
self._stack.setNextStack(extruder_stack[0])
|
||||
if self._stack.getNextStack().getId() != old_extruder_stack_id: #Only reslice if the extruder changed.
|
||||
Application.getInstance().getBackend().forceSlice()
|
||||
|
@ -75,6 +89,7 @@ class SettingOverrideDecorator(SceneNodeDecorator):
|
|||
# \param extruder_stack_id The new extruder stack to print with.
|
||||
def setActiveExtruder(self, extruder_stack_id):
|
||||
self._extruder_stack = extruder_stack_id
|
||||
self._updateNextStack()
|
||||
self.activeExtruderChanged.emit()
|
||||
|
||||
def getStack(self):
|
||||
|
|
12
cura_app.py
12
cura_app.py
|
@ -5,6 +5,18 @@
|
|||
|
||||
import os
|
||||
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
|
||||
# It looks like setuptools creates a .pth file in
|
||||
|
|
|
@ -9,18 +9,20 @@ from UM.Math.Vector import Vector
|
|||
from UM.Scene.SceneNode import SceneNode
|
||||
from UM.Scene.GroupDecorator import GroupDecorator
|
||||
from UM.Math.Quaternion import Quaternion
|
||||
|
||||
from UM.Job import Job
|
||||
|
||||
import math
|
||||
import zipfile
|
||||
|
||||
import xml.etree.ElementTree as ET
|
||||
try:
|
||||
import xml.etree.cElementTree as ET
|
||||
except ImportError:
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
## Base implementation for reading 3MF files. Has no support for textures. Only loads meshes!
|
||||
class ThreeMFReader(MeshReader):
|
||||
def __init__(self):
|
||||
super(ThreeMFReader, self).__init__()
|
||||
super().__init__()
|
||||
self._supported_extensions = [".3mf"]
|
||||
|
||||
self._namespaces = {
|
||||
|
@ -116,4 +118,10 @@ class ThreeMFReader(MeshReader):
|
|||
except Exception as e:
|
||||
Logger.log("e", "exception occured in 3mf reader: %s", e)
|
||||
|
||||
return result
|
||||
try: # Selftest - There might be more functions that should fail
|
||||
boundingBox = result.getBoundingBox()
|
||||
boundingBox.isValid()
|
||||
except:
|
||||
return None
|
||||
|
||||
return result
|
||||
|
|
|
@ -68,6 +68,7 @@ class ChangeLog(Extension, QObject,):
|
|||
line = line.replace("[","")
|
||||
line = line.replace("]","")
|
||||
open_version = Version(line)
|
||||
open_header = ""
|
||||
self._change_logs[open_version] = collections.OrderedDict()
|
||||
elif line.startswith("*"):
|
||||
open_header = line.replace("*","")
|
||||
|
|
|
@ -13,6 +13,7 @@ message Slice
|
|||
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
|
||||
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
|
||||
|
@ -108,8 +109,14 @@ message 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 {
|
||||
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 {
|
||||
|
|
|
@ -16,6 +16,7 @@ from UM.Platform import Platform
|
|||
import cura.Settings
|
||||
|
||||
from cura.OneAtATimeIterator import OneAtATimeIterator
|
||||
from cura.Settings.ExtruderManager import ExtruderManager
|
||||
from . import ProcessSlicedLayersJob
|
||||
from . import ProcessGCodeJob
|
||||
from . import StartSliceJob
|
||||
|
@ -31,7 +32,6 @@ import Arcus
|
|||
from UM.i18n import i18nCatalog
|
||||
catalog = i18nCatalog("cura")
|
||||
|
||||
|
||||
class CuraEngineBackend(Backend):
|
||||
## Starts the back-end plug-in.
|
||||
#
|
||||
|
@ -39,13 +39,30 @@ class CuraEngineBackend(Backend):
|
|||
# with the back-end in general.
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
# 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.
|
||||
default_engine_location = os.path.join(Application.getInstallPrefix(), "bin", "CuraEngine")
|
||||
if hasattr(sys, "frozen"):
|
||||
default_engine_location = os.path.join(os.path.dirname(os.path.abspath(sys.executable)), "CuraEngine")
|
||||
# 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.
|
||||
executable_name = "CuraEngine"
|
||||
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)
|
||||
Preferences.getInstance().addPreference("backend/location", default_engine_location)
|
||||
|
||||
|
@ -59,7 +76,7 @@ class CuraEngineBackend(Backend):
|
|||
self._stored_layer_data = []
|
||||
self._stored_optimized_layer_data = []
|
||||
|
||||
#Triggers for when to (re)start slicing:
|
||||
# Triggers for when to (re)start slicing:
|
||||
self._global_container_stack = None
|
||||
Application.getInstance().globalContainerStackChanged.connect(self._onGlobalStackChanged)
|
||||
self._onGlobalStackChanged()
|
||||
|
@ -68,15 +85,15 @@ class CuraEngineBackend(Backend):
|
|||
cura.Settings.ExtruderManager.getInstance().activeExtruderChanged.connect(self._onActiveExtruderChanged)
|
||||
self._onActiveExtruderChanged()
|
||||
|
||||
#When you update a setting and other settings get changed through inheritance, many propertyChanged signals are fired.
|
||||
#This timer will group them up, and only slice for the last setting changed signal.
|
||||
#TODO: Properly group propertyChanged signals by whether they are triggered by the same user interaction.
|
||||
# When you update a setting and other settings get changed through inheritance, many propertyChanged signals are fired.
|
||||
# This timer will group them up, and only slice for the last setting changed signal.
|
||||
# TODO: Properly group propertyChanged signals by whether they are triggered by the same user interaction.
|
||||
self._change_timer = QTimer()
|
||||
self._change_timer.setInterval(500)
|
||||
self._change_timer.setSingleShot(True)
|
||||
self._change_timer.timeout.connect(self.slice)
|
||||
|
||||
#Listeners for receiving messages from the back-end.
|
||||
# Listeners for receiving messages from the back-end.
|
||||
self._message_handlers["cura.proto.Layer"] = self._onLayerMessage
|
||||
self._message_handlers["cura.proto.LayerOptimized"] = self._onOptimizedLayerMessage
|
||||
self._message_handlers["cura.proto.Progress"] = self._onProgressMessage
|
||||
|
@ -86,19 +103,19 @@ class CuraEngineBackend(Backend):
|
|||
self._message_handlers["cura.proto.SlicingFinished"] = self._onSlicingFinishedMessage
|
||||
|
||||
self._start_slice_job = None
|
||||
self._slicing = False #Are we currently slicing?
|
||||
self._restart = False #Back-end is currently restarting?
|
||||
self._enabled = True #Should we be slicing? Slicing might be paused when, for instance, the user is dragging the mesh around.
|
||||
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._slicing = False # Are we currently slicing?
|
||||
self._restart = False # Back-end is currently restarting?
|
||||
self._enabled = True # Should we be slicing? Slicing might be paused when, for instance, the user is dragging the mesh around.
|
||||
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._backend_log_max_lines = 200 # Maximal count of lines to buffer
|
||||
self._error_message = None #Pop-up message that shows errors.
|
||||
self._backend_log_max_lines = 20000 # Maximum number of lines to buffer
|
||||
self._error_message = None # Pop-up message that shows errors.
|
||||
|
||||
self.backendQuit.connect(self._onBackendQuit)
|
||||
self.backendConnected.connect(self._onBackendConnected)
|
||||
|
||||
#When a tool operation is in progress, don't slice. So we need to listen for tool operations.
|
||||
# When a tool operation is in progress, don't slice. So we need to listen for tool operations.
|
||||
Application.getInstance().getController().toolOperationStarted.connect(self._onToolOperationStarted)
|
||||
Application.getInstance().getController().toolOperationStopped.connect(self._onToolOperationStopped)
|
||||
|
||||
|
@ -117,9 +134,10 @@ class CuraEngineBackend(Backend):
|
|||
# \return list of commands and args / parameters.
|
||||
def getEngineCommand(self):
|
||||
json_path = Resources.getPath(Resources.DefinitionContainers, "fdmprinter.def.json")
|
||||
return [Preferences.getInstance().getValue("backend/location"), "connect", "127.0.0.1:{0}".format(self._port), "-j", json_path, "-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 material_amount The amount of material the print will use.
|
||||
printDurationMessage = Signal()
|
||||
|
@ -133,7 +151,7 @@ class CuraEngineBackend(Backend):
|
|||
## Perform a slice of the scene.
|
||||
def slice(self):
|
||||
self._slice_start_time = time()
|
||||
if not self._enabled or not self._global_container_stack: #We shouldn't be slicing.
|
||||
if not self._enabled or not self._global_container_stack: # We shouldn't be slicing.
|
||||
# try again in a short time
|
||||
self._change_timer.start()
|
||||
return
|
||||
|
@ -143,10 +161,10 @@ class CuraEngineBackend(Backend):
|
|||
self._stored_layer_data = []
|
||||
self._stored_optimized_layer_data = []
|
||||
|
||||
if self._slicing: #We were already slicing. Stop the old job.
|
||||
if self._slicing: # We were already slicing. Stop the old job.
|
||||
self._terminate()
|
||||
|
||||
if self._process_layers_job: #We were processing layers. Stop that, the layers are going to change soon.
|
||||
if self._process_layers_job: # We were processing layers. Stop that, the layers are going to change soon.
|
||||
self._process_layers_job.abort()
|
||||
self._process_layers_job = None
|
||||
|
||||
|
@ -177,13 +195,18 @@ class CuraEngineBackend(Backend):
|
|||
self.slicingCancelled.emit()
|
||||
self.processingProgress.emit(0)
|
||||
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:
|
||||
Logger.log("d", "Killing engine process")
|
||||
try:
|
||||
self._process.terminate()
|
||||
Logger.log("d", "Engine process is killed. Received return code %s", self._process.wait())
|
||||
self._process = None
|
||||
except Exception as e: # terminating a process that is already terminating causes an exception, silently ignore this.
|
||||
except Exception as e: # terminating a process that is already terminating causes an exception, silently ignore this.
|
||||
Logger.log("d", "Exception occurred while trying to kill the engine %s", str(e))
|
||||
|
||||
## Event handler to call when the job to initiate the slicing process is
|
||||
|
@ -204,7 +227,7 @@ class CuraEngineBackend(Backend):
|
|||
|
||||
if job.getResult() == StartSliceJob.StartJobResult.SettingError:
|
||||
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.backendStateChange.emit(BackendState.Error)
|
||||
else:
|
||||
|
@ -213,7 +236,7 @@ class CuraEngineBackend(Backend):
|
|||
|
||||
if job.getResult() == StartSliceJob.StartJobResult.NothingToSlice:
|
||||
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.backendStateChange.emit(BackendState.Error)
|
||||
else:
|
||||
|
@ -252,6 +275,9 @@ class CuraEngineBackend(Backend):
|
|||
return
|
||||
|
||||
super()._onSocketError(error)
|
||||
if error.getErrorCode() == Arcus.ErrorCode.Debug:
|
||||
return
|
||||
|
||||
self._terminate()
|
||||
|
||||
if error.getErrorCode() not in [Arcus.ErrorCode.BindFailedError, Arcus.ErrorCode.ConnectionResetError, Arcus.ErrorCode.Debug]:
|
||||
|
@ -262,7 +288,7 @@ class CuraEngineBackend(Backend):
|
|||
# \param instance The setting instance that has changed.
|
||||
# \param property The property of the setting instance that has changed.
|
||||
def _onSettingChanged(self, instance, property):
|
||||
if property == "value": #Only reslice if the value has changed.
|
||||
if property == "value": # Only reslice if the value has changed.
|
||||
self._onChanged()
|
||||
|
||||
## Called when a sliced layer data message is received from the engine.
|
||||
|
@ -313,7 +339,7 @@ class CuraEngineBackend(Backend):
|
|||
|
||||
## 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
|
||||
def _onPrintTimeMaterialEstimates(self, message):
|
||||
material_amounts = []
|
||||
|
@ -348,8 +374,8 @@ class CuraEngineBackend(Backend):
|
|||
#
|
||||
# \param tool The tool that the user is using.
|
||||
def _onToolOperationStarted(self, tool):
|
||||
self._terminate() # Do not continue slicing once a tool has started
|
||||
self._enabled = False # Do not reslice when a tool is doing it's 'thing'
|
||||
self._terminate() # Do not continue slicing once a tool has started
|
||||
self._enabled = False # Do not reslice when a tool is doing it's 'thing'
|
||||
|
||||
## Called when the user stops using some tool.
|
||||
#
|
||||
|
@ -357,13 +383,13 @@ class CuraEngineBackend(Backend):
|
|||
#
|
||||
# \param tool The tool that the user was using.
|
||||
def _onToolOperationStopped(self, tool):
|
||||
self._enabled = True # Tool stop, start listening for changes again.
|
||||
self._enabled = True # Tool stop, start listening for changes again.
|
||||
|
||||
## Called when the user changes the active view mode.
|
||||
def _onActiveViewChanged(self):
|
||||
if Application.getInstance().getController().getActiveView():
|
||||
view = Application.getInstance().getController().getActiveView()
|
||||
if view.getPluginId() == "LayerView": #If switching to layer view, we should process the layers if that hasn't been done yet.
|
||||
if view.getPluginId() == "LayerView": # If switching to layer view, we should process the layers if that hasn't been done yet.
|
||||
self._layer_view_active = True
|
||||
# There is data and we're not slicing at the moment
|
||||
# if we are slicing, there is no need to re-calculate the data as it will be invalid in a moment.
|
||||
|
@ -388,22 +414,35 @@ class CuraEngineBackend(Backend):
|
|||
if self._global_container_stack:
|
||||
self._global_container_stack.propertyChanged.disconnect(self._onSettingChanged)
|
||||
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()
|
||||
|
||||
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)
|
||||
extruders = list(ExtruderManager.getInstance().getMachineExtruders(self._global_container_stack.getId()))
|
||||
if extruders:
|
||||
for extruder in extruders:
|
||||
extruder.propertyChanged.connect(self._onSettingChanged)
|
||||
self._onActiveExtruderChanged()
|
||||
self._onChanged()
|
||||
|
||||
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:
|
||||
self._active_extruder_stack.propertyChanged.disconnect(self._onSettingChanged)
|
||||
self._active_extruder_stack.containersChanged.disconnect(self._onChanged)
|
||||
|
||||
self._active_extruder_stack = cura.Settings.ExtruderManager.getInstance().getActiveExtruderStack()
|
||||
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)
|
||||
|
||||
|
|
|
@ -56,10 +56,9 @@ class ProcessSlicedLayersJob(Job):
|
|||
|
||||
## Remove old layer data (if any)
|
||||
for node in DepthFirstIterator(self._scene.getRoot()):
|
||||
if type(node) is SceneNode and node.getMeshData():
|
||||
if node.callDecoration("getLayerData"):
|
||||
self._scene.getRoot().removeChild(node)
|
||||
Job.yieldThread()
|
||||
if node.callDecoration("getLayerData"):
|
||||
node.getParent().removeChild(node)
|
||||
break
|
||||
if self._abort_requested:
|
||||
if self._progress:
|
||||
self._progress.hide()
|
||||
|
@ -74,7 +73,7 @@ class ProcessSlicedLayersJob(Job):
|
|||
# instead simply offset all other layers so the lowest layer is always 0.
|
||||
min_layer_number = 0
|
||||
for layer in self._layers:
|
||||
if(layer.id < min_layer_number):
|
||||
if layer.id < min_layer_number:
|
||||
min_layer_number = layer.id
|
||||
|
||||
current_layer = 0
|
||||
|
@ -98,7 +97,7 @@ class ProcessSlicedLayersJob(Job):
|
|||
points = numpy.fromstring(polygon.points, dtype="f4") # Convert bytearray to numpy array
|
||||
if polygon.point_type == 0: # Point2D
|
||||
points = points.reshape((-1,2)) # We get a linear list of pairs that make up the points, so make numpy interpret them correctly.
|
||||
else: # Point3D
|
||||
else: # Point3D
|
||||
points = points.reshape((-1,3))
|
||||
|
||||
line_widths = numpy.fromstring(polygon.line_width, dtype="f4") # Convert bytearray to numpy array
|
||||
|
@ -108,15 +107,14 @@ class ProcessSlicedLayersJob(Job):
|
|||
# This uses manual array creation + copy rather than numpy.insert since this is
|
||||
# faster.
|
||||
new_points = numpy.empty((len(points), 3), numpy.float32)
|
||||
if polygon.point_type == 0: # Point2D
|
||||
new_points[:,0] = points[:,0]
|
||||
new_points[:,1] = layer.height/1000 # layer height value is in backend representation
|
||||
new_points[:,2] = -points[:,1]
|
||||
if polygon.point_type == 0: # Point2D
|
||||
new_points[:, 0] = points[:, 0]
|
||||
new_points[:, 1] = layer.height / 1000 # layer height value is in backend representation
|
||||
new_points[:, 2] = -points[:, 1]
|
||||
else: # Point3D
|
||||
new_points[:,0] = points[:,0]
|
||||
new_points[:,1] = points[:,2]
|
||||
new_points[:,2] = -points[:,1]
|
||||
|
||||
new_points[:, 0] = points[:, 0]
|
||||
new_points[:, 1] = points[:, 2]
|
||||
new_points[:, 2] = -points[:, 1]
|
||||
|
||||
this_poly = LayerPolygon.LayerPolygon(layer_data, extruder, line_types, new_points, line_widths)
|
||||
this_poly.buildCache()
|
||||
|
@ -185,4 +183,3 @@ class ProcessSlicedLayersJob(Job):
|
|||
else:
|
||||
if self._progress:
|
||||
self._progress.hide()
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@ from UM.Scene.SceneNode import SceneNode
|
|||
from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
|
||||
|
||||
from UM.Settings.Validator import ValidatorState
|
||||
from UM.Settings.SettingRelation import RelationType
|
||||
|
||||
from cura.OneAtATimeIterator import OneAtATimeIterator
|
||||
|
||||
|
@ -24,6 +25,7 @@ class StartJobResult(IntEnum):
|
|||
SettingError = 3
|
||||
NothingToSlice = 4
|
||||
|
||||
|
||||
## Formatter class that handles token expansion in start/end gcod
|
||||
class GcodeStartEndFormatter(Formatter):
|
||||
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)
|
||||
return "{" + str(key) + "}"
|
||||
|
||||
|
||||
## Job class that builds up the message of scene data to send to CuraEngine.
|
||||
class StartSliceJob(Job):
|
||||
def __init__(self, slice_message):
|
||||
|
@ -71,7 +74,7 @@ class StartSliceJob(Job):
|
|||
return
|
||||
|
||||
# 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)
|
||||
return
|
||||
|
||||
|
@ -123,11 +126,15 @@ class StartSliceJob(Job):
|
|||
if 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:
|
||||
self.setResult(StartJobResult.NothingToSlice)
|
||||
return
|
||||
|
||||
self._buildGlobalSettingsMessage(stack)
|
||||
self._buildGlobalInheritsStackMessage(stack)
|
||||
|
||||
for extruder_stack in cura.Settings.ExtruderManager.getInstance().getMachineExtruders(stack.getId()):
|
||||
self._buildExtruderMessage(extruder_stack)
|
||||
|
@ -171,6 +178,7 @@ class StartSliceJob(Job):
|
|||
Logger.logException("w", "Unable to do token replacement on start/end gcode")
|
||||
return str(value).encode("utf-8")
|
||||
|
||||
## Create extruder message from stack
|
||||
def _buildExtruderMessage(self, stack):
|
||||
message = self._slice_message.addRepeatedMessage("extruders")
|
||||
message.id = int(stack.getMetaDataEntry("position"))
|
||||
|
@ -209,11 +217,54 @@ class StartSliceJob(Job):
|
|||
else:
|
||||
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):
|
||||
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:
|
||||
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.name = key
|
||||
setting.value = str(stack.getProperty(key, "value")).encode("utf-8")
|
||||
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)
|
|
@ -9,6 +9,8 @@ import UM.Settings.ContainerRegistry
|
|||
from cura.CuraApplication import CuraApplication
|
||||
from cura.Settings.ExtruderManager import ExtruderManager
|
||||
|
||||
from UM.Settings.InstanceContainer import InstanceContainer
|
||||
|
||||
import re #For escaping characters in the settings.
|
||||
import json
|
||||
|
||||
|
@ -61,6 +63,20 @@ class GCodeWriter(MeshWriter):
|
|||
|
||||
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
|
||||
# g-code.
|
||||
#
|
||||
|
@ -74,38 +90,21 @@ class GCodeWriter(MeshWriter):
|
|||
prefix_length = len(prefix)
|
||||
|
||||
container_with_profile = stack.findContainer({"type": "quality"})
|
||||
machine_manager = CuraApplication.getInstance().getMachineManager()
|
||||
|
||||
# Duplicate the current quality profile and update it with any user settings.
|
||||
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()
|
||||
if not container_with_profile:
|
||||
Logger.log("e", "No valid quality profile found, not writing settings to GCode!")
|
||||
return ""
|
||||
|
||||
flat_global_container = self._createFlattenedContainerInstance(stack.getTop(),container_with_profile)
|
||||
serialized = flat_global_container.serialize()
|
||||
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"})
|
||||
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 = 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"))
|
||||
flat_extruder_quality = self._createFlattenedContainerInstance(extruder.getTop(), extruder_quality)
|
||||
|
||||
extruder_serialized = flat_extruder_quality.serialize()
|
||||
data.setdefault("extruder_quality", []).append(extruder_serialized)
|
||||
|
|
|
@ -11,6 +11,7 @@ from UM.Math.Color import Color
|
|||
from UM.Mesh.MeshBuilder import MeshBuilder
|
||||
from UM.Job import Job
|
||||
from UM.Preferences import Preferences
|
||||
from UM.Logger import Logger
|
||||
|
||||
from UM.View.RenderBatch import RenderBatch
|
||||
from UM.View.GL.OpenGL import OpenGL
|
||||
|
@ -34,7 +35,7 @@ class LayerView(View):
|
|||
self._shader = None
|
||||
self._selection_shader = None
|
||||
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._controller.getScene().getRoot().childrenChanged.connect(self._onSceneChanged)
|
||||
self._max_layers = 0
|
||||
|
@ -43,12 +44,14 @@ class LayerView(View):
|
|||
self._current_layer_jumps = None
|
||||
self._top_layers_job = None
|
||||
self._activity = False
|
||||
self._old_max_layers = 0
|
||||
|
||||
Preferences.getInstance().addPreference("view/top_layer_count", 5)
|
||||
Preferences.getInstance().addPreference("view/only_show_top_layers", False)
|
||||
Preferences.getInstance().preferenceChanged.connect(self._onPreferencesChanged)
|
||||
|
||||
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
|
||||
|
||||
def getActivity(self):
|
||||
|
@ -100,7 +103,7 @@ class LayerView(View):
|
|||
continue
|
||||
|
||||
# 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
|
||||
end = 0
|
||||
element_counts = layer_data.getElementCounts()
|
||||
|
@ -126,14 +129,12 @@ class LayerView(View):
|
|||
if self._current_layer_num > self._max_layers:
|
||||
self._current_layer_num = self._max_layers
|
||||
|
||||
self.resetLayerData()
|
||||
self._startUpdateTopLayers()
|
||||
|
||||
self.currentLayerNumChanged.emit()
|
||||
|
||||
def calculateMaxLayers(self):
|
||||
scene = self.getController().getScene()
|
||||
renderer = self.getRenderer() # TODO: @UnusedVariable
|
||||
self._activity = True
|
||||
|
||||
self._old_max_layers = self._max_layers
|
||||
|
@ -199,7 +200,7 @@ class LayerView(View):
|
|||
|
||||
if not job.getResult():
|
||||
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_jumps = job.getResult().get("jumps")
|
||||
self._controller.getScene().sceneChanged.emit(self._controller.getScene().getRoot())
|
||||
|
@ -207,14 +208,15 @@ class LayerView(View):
|
|||
self._top_layers_job = None
|
||||
|
||||
def _onPreferencesChanged(self, preference):
|
||||
if preference != "view/top_layer_count":
|
||||
if preference != "view/top_layer_count" and preference != "view/only_show_top_layers":
|
||||
return
|
||||
|
||||
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()
|
||||
|
||||
|
||||
class _CreateTopLayersJob(Job):
|
||||
def __init__(self, scene, layer_number, solid_layers):
|
||||
super().__init__()
|
||||
|
@ -242,20 +244,20 @@ class _CreateTopLayersJob(Job):
|
|||
|
||||
try:
|
||||
layer = layer_data.getLayer(layer_number).createMesh()
|
||||
except Exception as e:
|
||||
print(e)
|
||||
except Exception:
|
||||
Logger.logException("w", "An exception occurred while creating layer mesh.")
|
||||
return
|
||||
|
||||
if not layer or layer.getVertices() is None:
|
||||
continue
|
||||
|
||||
layer_mesh.addIndices(layer_mesh._vertex_count+layer.getIndices())
|
||||
layer_mesh.addIndices(layer_mesh.getVertexCount() + layer.getIndices())
|
||||
layer_mesh.addVertices(layer.getVertices())
|
||||
|
||||
# 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.
|
||||
brightness = numpy.ones((1,4), dtype=numpy.float32) * (2.0 - (i / self._solid_layers)) / 2.0
|
||||
brightness[0, 3] = 1.0;
|
||||
brightness = numpy.ones((1, 4), dtype=numpy.float32) * (2.0 - (i / self._solid_layers)) / 2.0
|
||||
brightness[0, 3] = 1.0
|
||||
layer_mesh.addColors(layer.getColors() * brightness)
|
||||
|
||||
if self._cancel:
|
||||
|
@ -271,7 +273,7 @@ class _CreateTopLayersJob(Job):
|
|||
if not jump_mesh or jump_mesh.getVertices() is None:
|
||||
jump_mesh = None
|
||||
|
||||
self.setResult({ "layers": layer_mesh.build(), "jumps": jump_mesh })
|
||||
self.setResult({"layers": layer_mesh.build(), "jumps": jump_mesh})
|
||||
|
||||
def cancel(self):
|
||||
self._cancel = True
|
||||
|
|
|
@ -13,7 +13,40 @@ import ".."
|
|||
Button {
|
||||
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 hideTooltip();
|
||||
|
|
|
@ -16,17 +16,17 @@ UM.TooltipArea
|
|||
width: childrenRect.width;
|
||||
height: childrenRect.height;
|
||||
|
||||
Button
|
||||
CheckBox
|
||||
{
|
||||
id: check
|
||||
|
||||
text: definition.label
|
||||
checked: addedSettingsModel.getVisible(model.key)
|
||||
|
||||
onClicked:
|
||||
{
|
||||
addedSettingsModel.setVisible(model.key, true);
|
||||
settingPickDialog.visible = false
|
||||
UM.ActiveTool.forceUpdate()
|
||||
addedSettingsModel.setVisible(model.key, checked);
|
||||
UM.ActiveTool.forceUpdate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 UM.Application import Application
|
||||
from UM.Settings.SettingInstance import SettingInstance
|
||||
from UM.Logger import Logger
|
||||
|
||||
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
|
||||
|
||||
## The per object setting visibility handler ensures that only setting defintions that have a matching instance Container
|
||||
# are returned as visible.
|
||||
## The per object setting visibility handler ensures that only setting
|
||||
# definitions that have a matching instance Container are returned as visible.
|
||||
class PerObjectSettingVisibilityHandler(UM.Settings.Models.SettingVisibilityHandler):
|
||||
def __init__(self, parent = None, *args, **kwargs):
|
||||
super().__init__(parent = parent, *args, **kwargs)
|
||||
|
@ -54,10 +58,23 @@ class PerObjectSettingVisibilityHandler(UM.Settings.Models.SettingVisibilityHand
|
|||
if not settings.getInstance(item):
|
||||
definition = self._stack.getSettingDefinition(item)
|
||||
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
|
||||
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:
|
||||
self.visibilityChanged.emit()
|
||||
|
|
|
@ -31,7 +31,7 @@ Item {
|
|||
spacing: UM.Theme.getSize("default_margin").width
|
||||
Label
|
||||
{
|
||||
text: catalog.i18nc("@label", "Print object with")
|
||||
text: catalog.i18nc("@label", "Print model with")
|
||||
anchors.verticalCenter: extruderSelector.verticalCenter
|
||||
|
||||
color: UM.Theme.getColor("setting_control_text")
|
||||
|
@ -47,7 +47,9 @@ Item {
|
|||
id: extruders_model
|
||||
onRowsInserted: 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
|
||||
textRole: "name"
|
||||
width: UM.Theme.getSize("setting_control").width
|
||||
|
@ -88,7 +90,7 @@ Item {
|
|||
anchors.leftMargin: UM.Theme.getSize("default_lining").width
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
color: extruders_model.getItem(extruderSelector.currentIndex).colour
|
||||
color: extruderSelector.color
|
||||
border.width: UM.Theme.getSize("default_lining").width
|
||||
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();
|
||||
|
||||
function updateCurrentIndex()
|
||||
|
@ -136,6 +142,7 @@ Item {
|
|||
if(extruders_model.getItem(i).id == UM.ActiveTool.properties.getValue("SelectedActiveExtruder"))
|
||||
{
|
||||
extruderSelector.currentIndex = i;
|
||||
extruderSelector.color = extruders_model.getItem(i).color;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -144,105 +151,111 @@ Item {
|
|||
}
|
||||
}
|
||||
|
||||
Repeater
|
||||
Column
|
||||
{
|
||||
id: contents
|
||||
height: childrenRect.height;
|
||||
spacing: UM.Theme.getSize("default_lining").height
|
||||
|
||||
model: UM.SettingDefinitionsModel
|
||||
Repeater
|
||||
{
|
||||
id: addedSettingsModel;
|
||||
containerId: Cura.MachineManager.activeDefinitionId
|
||||
expanded: [ "*" ]
|
||||
id: contents
|
||||
height: childrenRect.height;
|
||||
|
||||
visibilityHandler: Cura.PerObjectSettingVisibilityHandler
|
||||
model: UM.SettingDefinitionsModel
|
||||
{
|
||||
selectedObjectId: UM.ActiveTool.properties.getValue("SelectedObjectId")
|
||||
}
|
||||
}
|
||||
id: addedSettingsModel;
|
||||
containerId: Cura.MachineManager.activeDefinitionId
|
||||
expanded: [ "*" ]
|
||||
|
||||
delegate: Row
|
||||
{
|
||||
Loader
|
||||
{
|
||||
id: settingLoader
|
||||
width: UM.Theme.getSize("setting").width;
|
||||
height: UM.Theme.getSize("section").height;
|
||||
|
||||
property var definition: model
|
||||
property var settingDefinitionsModel: addedSettingsModel
|
||||
property var propertyProvider: provider
|
||||
|
||||
//Qt5.4.2 and earlier has a bug where this causes a crash: https://bugreports.qt.io/browse/QTBUG-35989
|
||||
//In addition, while it works for 5.5 and higher, the ordering of the actual combo box drop down changes,
|
||||
//causing nasty issues when selecting different options. So disable asynchronous loading of enum type completely.
|
||||
asynchronous: model.type != "enum" && model.type != "extruder"
|
||||
|
||||
onLoaded: {
|
||||
settingLoader.item.showRevertButton = false
|
||||
settingLoader.item.showInheritButton = false
|
||||
settingLoader.item.showLinkedSettingIcon = false
|
||||
settingLoader.item.doDepthIndentation = false
|
||||
settingLoader.item.doQualityUserSettingEmphasis = false
|
||||
}
|
||||
|
||||
sourceComponent:
|
||||
visibilityHandler: Cura.PerObjectSettingVisibilityHandler
|
||||
{
|
||||
switch(model.type)
|
||||
{
|
||||
case "int":
|
||||
return settingTextField
|
||||
case "float":
|
||||
return settingTextField
|
||||
case "enum":
|
||||
return settingComboBox
|
||||
case "extruder":
|
||||
return settingExtruder
|
||||
case "bool":
|
||||
return settingCheckBox
|
||||
case "str":
|
||||
return settingTextField
|
||||
case "category":
|
||||
return settingCategory
|
||||
default:
|
||||
return settingUnknown
|
||||
selectedObjectId: UM.ActiveTool.properties.getValue("SelectedObjectId")
|
||||
}
|
||||
}
|
||||
|
||||
delegate: Row
|
||||
{
|
||||
Loader
|
||||
{
|
||||
id: settingLoader
|
||||
width: UM.Theme.getSize("setting").width
|
||||
height: UM.Theme.getSize("section").height
|
||||
|
||||
property var definition: model
|
||||
property var settingDefinitionsModel: addedSettingsModel
|
||||
property var propertyProvider: provider
|
||||
|
||||
//Qt5.4.2 and earlier has a bug where this causes a crash: https://bugreports.qt.io/browse/QTBUG-35989
|
||||
//In addition, while it works for 5.5 and higher, the ordering of the actual combo box drop down changes,
|
||||
//causing nasty issues when selecting different options. So disable asynchronous loading of enum type completely.
|
||||
asynchronous: model.type != "enum" && model.type != "extruder"
|
||||
|
||||
onLoaded: {
|
||||
settingLoader.item.showRevertButton = false
|
||||
settingLoader.item.showInheritButton = false
|
||||
settingLoader.item.showLinkedSettingIcon = false
|
||||
settingLoader.item.doDepthIndentation = false
|
||||
settingLoader.item.doQualityUserSettingEmphasis = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Button
|
||||
{
|
||||
width: UM.Theme.getSize("setting").height;
|
||||
height: UM.Theme.getSize("setting").height;
|
||||
|
||||
onClicked: addedSettingsModel.setVisible(model.key, false);
|
||||
|
||||
style: ButtonStyle
|
||||
{
|
||||
background: Item
|
||||
sourceComponent:
|
||||
{
|
||||
UM.RecolorImage
|
||||
switch(model.type)
|
||||
{
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
width: parent.width/2
|
||||
height: parent.height/2
|
||||
sourceSize.width: width
|
||||
sourceSize.height: width
|
||||
color: control.hovered ? UM.Theme.getColor("setting_control_button_hover") : UM.Theme.getColor("setting_control_button")
|
||||
source: UM.Theme.getIcon("cross1")
|
||||
case "int":
|
||||
return settingTextField
|
||||
case "float":
|
||||
return settingTextField
|
||||
case "enum":
|
||||
return settingComboBox
|
||||
case "extruder":
|
||||
return settingExtruder
|
||||
case "bool":
|
||||
return settingCheckBox
|
||||
case "str":
|
||||
return settingTextField
|
||||
case "category":
|
||||
return settingCategory
|
||||
default:
|
||||
return settingUnknown
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
UM.SettingPropertyProvider
|
||||
{
|
||||
id: provider
|
||||
|
||||
containerStackId: UM.ActiveTool.properties.getValue("ContainerID")
|
||||
key: model.key
|
||||
watchedProperties: [ "value", "enabled", "validationState" ]
|
||||
storeIndex: 0
|
||||
Button
|
||||
{
|
||||
width: UM.Theme.getSize("setting").height / 2;
|
||||
height: UM.Theme.getSize("setting").height;
|
||||
|
||||
onClicked: addedSettingsModel.setVisible(model.key, false);
|
||||
|
||||
style: ButtonStyle
|
||||
{
|
||||
background: Item
|
||||
{
|
||||
UM.RecolorImage
|
||||
{
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: parent.width
|
||||
height: parent.height / 2
|
||||
sourceSize.width: width
|
||||
sourceSize.height: width
|
||||
color: control.hovered ? UM.Theme.getColor("setting_control_button_hover") : UM.Theme.getColor("setting_control_button")
|
||||
source: UM.Theme.getIcon("minus")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
UM.SettingPropertyProvider
|
||||
{
|
||||
id: provider
|
||||
|
||||
containerStackId: UM.ActiveTool.properties.getValue("ContainerID")
|
||||
key: model.key
|
||||
watchedProperties: [ "value", "enabled", "validationState" ]
|
||||
storeIndex: 0
|
||||
removeUnusedValue: false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -253,7 +266,7 @@ Item {
|
|||
height: UM.Theme.getSize("setting").height;
|
||||
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
|
||||
{
|
||||
|
@ -293,16 +306,28 @@ Item {
|
|||
UM.Dialog {
|
||||
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: ""
|
||||
|
||||
onVisibilityChanged:
|
||||
{
|
||||
// force updating the model to sync it with addedSettingsModel
|
||||
if(visible)
|
||||
{
|
||||
listview.model.forceUpdate()
|
||||
}
|
||||
}
|
||||
|
||||
TextField {
|
||||
id: filter;
|
||||
id: filter
|
||||
|
||||
anchors {
|
||||
top: parent.top;
|
||||
left: parent.left;
|
||||
right: parent.right;
|
||||
top: parent.top
|
||||
left: parent.left
|
||||
right: toggleShowAll.left
|
||||
rightMargin: UM.Theme.getSize("default_margin").width
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
id: scrollView
|
||||
|
@ -373,7 +415,7 @@ Item {
|
|||
|
||||
rightButtons: [
|
||||
Button {
|
||||
text: catalog.i18nc("@action:button", "Cancel");
|
||||
text: catalog.i18nc("@action:button", "Close");
|
||||
onClicked: {
|
||||
settingPickDialog.visible = false;
|
||||
}
|
||||
|
|
|
@ -11,15 +11,15 @@ i18n_catalog = i18nCatalog("cura")
|
|||
def getMetaData():
|
||||
return {
|
||||
"plugin": {
|
||||
"name": i18n_catalog.i18nc("@label", "Per Object Settings Tool"),
|
||||
"name": i18n_catalog.i18nc("@label", "Per Model Settings Tool"),
|
||||
"author": "Ultimaker",
|
||||
"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
|
||||
},
|
||||
"tool": {
|
||||
"name": i18n_catalog.i18nc("@label", "Per Object Settings"),
|
||||
"description": i18n_catalog.i18nc("@info:tooltip", "Configure Per Object Settings"),
|
||||
"name": i18n_catalog.i18nc("@label", "Per Model Settings"),
|
||||
"description": i18n_catalog.i18nc("@info:tooltip", "Configure Per Model Settings"),
|
||||
"icon": "setting_per_object",
|
||||
"tool_panel": "PerObjectSettingsPanel.qml",
|
||||
"weight": 3
|
||||
|
|
|
@ -11,11 +11,8 @@ from UM.i18n import i18nCatalog
|
|||
from UM.Logger import Logger
|
||||
from UM.Platform import Platform
|
||||
from UM.Qt.Duration import DurationFormat
|
||||
from UM.Job import Job
|
||||
|
||||
import collections
|
||||
import json
|
||||
import os.path
|
||||
import copy
|
||||
import platform
|
||||
import math
|
||||
import urllib.request
|
||||
|
@ -24,6 +21,36 @@ import ssl
|
|||
|
||||
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.
|
||||
# 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)
|
||||
binary_data = submitted_data.encode("utf-8")
|
||||
|
||||
# Submit data
|
||||
kwoptions = {"data" : binary_data,
|
||||
"timeout" : 1
|
||||
}
|
||||
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:
|
||||
Logger.logException("e", "An exception occurred while trying to send slice information")
|
||||
except:
|
||||
# Sending slice info non-blocking
|
||||
reportJob = SliceInfoJob(self.info_url, binary_data)
|
||||
reportJob.start()
|
||||
except Exception as e:
|
||||
# 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.
|
||||
pass
|
||||
Logger.log("e", "Exception raised while sending slice info: %s" %(repr(e))) # But we should be notified about these problems of course.
|
|
@ -3,10 +3,12 @@
|
|||
|
||||
from UM.View.View import View
|
||||
from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
|
||||
from UM.Scene.Selection import Selection
|
||||
from UM.Resources import Resources
|
||||
from UM.Application import Application
|
||||
from UM.Preferences import Preferences
|
||||
from UM.View.Renderer import Renderer
|
||||
from UM.Settings.Validator import ValidatorState
|
||||
|
||||
from UM.View.GL.OpenGL import OpenGL
|
||||
|
||||
|
@ -39,43 +41,51 @@ class SolidView(View):
|
|||
self._disabled_shader.setUniformValue("u_diffuseColor2", [0.68, 0.68, 0.68, 1.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"):
|
||||
angle = Application.getInstance().getGlobalContainerStack().getProperty("support_angle", "value")
|
||||
if angle is not None:
|
||||
angle = global_container_stack.getProperty("support_angle", "value")
|
||||
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)))
|
||||
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.
|
||||
else:
|
||||
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()):
|
||||
if not node.render(renderer):
|
||||
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 = {}
|
||||
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
|
||||
extruder_index = 0
|
||||
extruder_id = node.callDecoration("getActiveExtruder")
|
||||
if extruder_id:
|
||||
extruder_index = max(0, self._extruders_model.find("id", extruder_id))
|
||||
|
||||
extruder_color = self._extruders_model.getItem(extruder_index)["colour"]
|
||||
try:
|
||||
# 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])
|
||||
uniforms["diffuse_color"] = [
|
||||
int(extruder_color[1:3], 16) / 255,
|
||||
int(extruder_color[3:5], 16) / 255,
|
||||
int(extruder_color[5:7], 16) / 255,
|
||||
1.0
|
||||
]
|
||||
except ValueError:
|
||||
pass
|
||||
material_color = self._extruders_model.getItem(extruder_index)["color"]
|
||||
try:
|
||||
# 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])
|
||||
uniforms["diffuse_color"] = [
|
||||
int(material_color[1:3], 16) / 255,
|
||||
int(material_color[3:5], 16) / 255,
|
||||
int(material_color[5:7], 16) / 255,
|
||||
1.0
|
||||
]
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
if hasattr(node, "_outside_buildarea"):
|
||||
if node._outside_buildarea:
|
||||
|
@ -84,7 +94,7 @@ class SolidView(View):
|
|||
renderer.queueNode(node, shader = self._enabled_shader, uniforms = uniforms)
|
||||
else:
|
||||
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)
|
||||
|
||||
def endRendering(self):
|
||||
|
|
|
@ -32,16 +32,16 @@ UM.Dialog
|
|||
}
|
||||
|
||||
text: {
|
||||
if (manager.progress == 0)
|
||||
{
|
||||
//: Firmware update status label
|
||||
return catalog.i18nc("@label","Starting firmware update, this may take a while.")
|
||||
}
|
||||
else if (manager.progress > 99)
|
||||
if (manager.firmwareUpdateCompleteStatus)
|
||||
{
|
||||
//: Firmware update status label
|
||||
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
|
||||
{
|
||||
//: Firmware update status label
|
||||
|
@ -55,10 +55,10 @@ UM.Dialog
|
|||
ProgressBar
|
||||
{
|
||||
id: prog
|
||||
value: manager.progress
|
||||
value: manager.firmwareUpdateCompleteStatus ? 100 : manager.progress
|
||||
minimumValue: 0
|
||||
maximumValue: 100
|
||||
indeterminate: manager.progress < 100
|
||||
indeterminate: (manager.progress < 1) && (!manager.firmwareUpdateCompleteStatus)
|
||||
anchors
|
||||
{
|
||||
left: parent.left;
|
||||
|
@ -79,7 +79,7 @@ UM.Dialog
|
|||
Button
|
||||
{
|
||||
text: catalog.i18nc("@action:button","Close");
|
||||
enabled: manager.progress >= 100;
|
||||
enabled: manager.firmwareUpdateCompleteStatus;
|
||||
onClicked: base.visible = false;
|
||||
}
|
||||
]
|
||||
|
|
|
@ -21,12 +21,14 @@ catalog = i18nCatalog("cura")
|
|||
|
||||
|
||||
class USBPrinterOutputDevice(PrinterOutputDevice):
|
||||
|
||||
def __init__(self, serial_port):
|
||||
super().__init__(serial_port)
|
||||
self.setName(catalog.i18nc("@item:inmenu", "USB printing"))
|
||||
self.setShortDescription(catalog.i18nc("@action:button", "Print via USB"))
|
||||
self.setDescription(catalog.i18nc("@info:tooltip", "Print via USB"))
|
||||
self.setIconName("print")
|
||||
self.setConnectionText(catalog.i18nc("@info:status", "Connected via USB"))
|
||||
|
||||
self._serial = None
|
||||
self._serial_port = serial_port
|
||||
|
@ -85,12 +87,14 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
|
|||
self._updating_firmware = False
|
||||
|
||||
self._firmware_file_name = None
|
||||
self._firmware_update_finished = False
|
||||
|
||||
self._error_message = None
|
||||
|
||||
onError = pyqtSignal()
|
||||
|
||||
firmwareUpdateComplete = pyqtSignal()
|
||||
firmwareUpdateChange = pyqtSignal()
|
||||
|
||||
endstopStateChanged = pyqtSignal(str ,bool, arguments = ["key","state"])
|
||||
|
||||
|
@ -170,6 +174,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
|
|||
## Private function (threaded) that actually uploads the firmware.
|
||||
def _updateFirmware(self):
|
||||
self.setProgress(0, 100)
|
||||
self._firmware_update_finished = False
|
||||
|
||||
if self._connection_state != ConnectionState.closed:
|
||||
self.close()
|
||||
|
@ -177,10 +182,11 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
|
|||
|
||||
if len(hex_file) == 0:
|
||||
Logger.log("e", "Unable to read provided hex file. Could not update firmware")
|
||||
return
|
||||
self._updateFirmware_completed()
|
||||
return
|
||||
|
||||
programmer = stk500v2.Stk500v2()
|
||||
programmer.progressCallback = self.setProgress
|
||||
programmer.progress_callback = self.setProgress
|
||||
|
||||
try:
|
||||
programmer.connect(self._serial_port)
|
||||
|
@ -192,6 +198,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
|
|||
|
||||
if not programmer.isConnected():
|
||||
Logger.log("e", "Unable to connect with serial. Could not update firmware")
|
||||
self._updateFirmware_completed()
|
||||
return
|
||||
|
||||
self._updating_firmware = True
|
||||
|
@ -201,13 +208,21 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
|
|||
self._updating_firmware = False
|
||||
except Exception as e:
|
||||
Logger.log("e", "Exception while trying to update firmware %s" %e)
|
||||
self._updating_firmware = False
|
||||
self._updateFirmware_completed()
|
||||
return
|
||||
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()
|
||||
|
||||
return
|
||||
|
||||
## Upload new firmware to machine
|
||||
# \param filename full path of firmware file to be uploaded
|
||||
|
@ -216,6 +231,14 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
|
|||
self._firmware_file_name = file_name
|
||||
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()
|
||||
def startPollEndstop(self):
|
||||
if not self._poll_endstop:
|
||||
|
@ -251,7 +274,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
|
|||
# If the programmer connected, we know its an atmega based version.
|
||||
# Not all that useful, but it does give some debugging information.
|
||||
for baud_rate in self._getBaudrateList(): # Cycle all baud rates (auto detect)
|
||||
Logger.log("d","Attempting to connect to printer with serial %s on baud rate %s", self._serial_port, baud_rate)
|
||||
Logger.log("d", "Attempting to connect to printer with serial %s on baud rate %s", self._serial_port, baud_rate)
|
||||
if self._serial is None:
|
||||
try:
|
||||
self._serial = serial.Serial(str(self._serial_port), baud_rate, timeout = 3, writeTimeout = 10000)
|
||||
|
@ -260,7 +283,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
|
|||
continue
|
||||
else:
|
||||
if not self.setBaudRate(baud_rate):
|
||||
continue # Could not set the baud rate, go to the next
|
||||
continue # Could not set the baud rate, go to the next
|
||||
|
||||
time.sleep(1.5) # Ensure that we are not talking to the bootloader. 1.5 seconds seems to be the magic number
|
||||
sucesfull_responses = 0
|
||||
|
@ -270,11 +293,13 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
|
|||
while timeout_time > time.time():
|
||||
line = self._readline()
|
||||
if line is None:
|
||||
Logger.log("d", "No response from serial connection received.")
|
||||
# Something went wrong with reading, could be that close was called.
|
||||
self.setConnectionState(ConnectionState.closed)
|
||||
return
|
||||
|
||||
if b"T:" in line:
|
||||
Logger.log("d", "Correct response for auto-baudrate detection received.")
|
||||
self._serial.timeout = 0.5
|
||||
sucesfull_responses += 1
|
||||
if sucesfull_responses >= self._required_responses_auto_baud:
|
||||
|
@ -282,7 +307,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
|
|||
self.setConnectionState(ConnectionState.connected)
|
||||
self._listen_thread.start() # Start listening
|
||||
Logger.log("i", "Established printer connection on port %s" % self._serial_port)
|
||||
return
|
||||
return
|
||||
|
||||
self._sendCommand("M105") # Send M105 as long as we are listening, otherwise we end up in an undefined state
|
||||
|
||||
|
@ -310,7 +335,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
|
|||
|
||||
self._connect_thread = threading.Thread(target = self._connect)
|
||||
self._connect_thread.daemon = True
|
||||
|
||||
|
||||
self.setConnectionState(ConnectionState.closed)
|
||||
if self._serial is not None:
|
||||
try:
|
||||
|
@ -539,7 +564,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
|
|||
ret = self._serial.readline()
|
||||
except Exception as e:
|
||||
Logger.log("e", "Unexpected error while reading serial port. %s" % e)
|
||||
self._setErrorState("Printer has been disconnected")
|
||||
self._setErrorState("Printer has been disconnected")
|
||||
self.close()
|
||||
return None
|
||||
return ret
|
||||
|
|
|
@ -48,15 +48,24 @@ class USBPrinterOutputDeviceManager(QObject, OutputDevicePlugin, Extension):
|
|||
connectionStateChanged = pyqtSignal()
|
||||
|
||||
progressChanged = pyqtSignal()
|
||||
firmwareUpdateChange = pyqtSignal()
|
||||
|
||||
@pyqtProperty(float, notify = progressChanged)
|
||||
def progress(self):
|
||||
progress = 0
|
||||
for printer_name, device in self._usb_output_devices.items(): # TODO: @UnusedVariable "printer_name"
|
||||
progress += device.progress
|
||||
|
||||
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):
|
||||
self._check_updates = True
|
||||
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()
|
||||
return
|
||||
|
||||
for printer_connection in self._usb_output_devices:
|
||||
self._usb_output_devices[printer_connection].resetFirmwareUpdate()
|
||||
self.spawnFirmwareInterface("")
|
||||
for printer_connection in self._usb_output_devices:
|
||||
try:
|
||||
self._usb_output_devices[printer_connection].updateFirmware(Resources.getPath(CuraApplication.ResourceTypes.Firmware, self._getDefaultFirmwareName()))
|
||||
except FileNotFoundError:
|
||||
# Should only happen in dev environments where the resources/firmware folder is absent.
|
||||
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
|
||||
|
||||
@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()))
|
||||
except FileNotFoundError:
|
||||
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 True
|
||||
return False
|
||||
|
@ -131,12 +147,12 @@ class USBPrinterOutputDeviceManager(QObject, OutputDevicePlugin, Extension):
|
|||
Logger.log("e", "There is no global container stack. Can not update firmware.")
|
||||
self._firmware_view.close()
|
||||
return ""
|
||||
|
||||
|
||||
# The bottom of the containerstack is the machine definition
|
||||
machine_id = global_container_stack.getBottom().id
|
||||
|
||||
|
||||
machine_has_heated_bed = global_container_stack.getProperty("machine_heated_bed", "value")
|
||||
|
||||
|
||||
if platform.system() == "Linux":
|
||||
baudrate = 115200
|
||||
else:
|
||||
|
@ -150,13 +166,15 @@ class USBPrinterOutputDeviceManager(QObject, OutputDevicePlugin, Extension):
|
|||
"bq_hephestos_2" : "MarlinHephestos2.hex",
|
||||
"ultimaker_original" : "MarlinUltimaker-{baudrate}.hex",
|
||||
"ultimaker_original_plus" : "MarlinUltimaker-UMOP-{baudrate}.hex",
|
||||
"ultimaker_original_dual" : "MarlinUltimaker-{baudrate}-dual.hex",
|
||||
"ultimaker2" : "MarlinUltimaker2.hex",
|
||||
"ultimaker2_go" : "MarlinUltimaker2go.hex",
|
||||
"ultimaker2_plus" : "MarlinUltimaker2plus.hex",
|
||||
"ultimaker2_plus" : "MarlinUltimaker2plus.hex",
|
||||
"ultimaker2_extended" : "MarlinUltimaker2extended.hex",
|
||||
"ultimaker2_extended_plus" : "MarlinUltimaker2extended-plus.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
|
||||
hex_file = None
|
||||
|
@ -200,6 +218,7 @@ class USBPrinterOutputDeviceManager(QObject, OutputDevicePlugin, Extension):
|
|||
device.connectionStateChanged.connect(self._onConnectionStateChanged)
|
||||
device.connect()
|
||||
device.progressChanged.connect(self.progressChanged)
|
||||
device.firmwareUpdateChange.connect(self.firmwareUpdateChange)
|
||||
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.
|
||||
|
|
|
@ -21,7 +21,7 @@ class Stk500v2(ispBase.IspBase):
|
|||
self.seq = 1
|
||||
self.last_addr = -1
|
||||
self.progress_callback = None
|
||||
|
||||
|
||||
def connect(self, port = "COM22", speed = 115200):
|
||||
if self.serial is not None:
|
||||
self.close()
|
||||
|
@ -69,7 +69,7 @@ class Stk500v2(ispBase.IspBase):
|
|||
self.serial = None
|
||||
return ret
|
||||
return None
|
||||
|
||||
|
||||
def isConnected(self):
|
||||
return self.serial is not None
|
||||
|
||||
|
@ -79,7 +79,7 @@ class Stk500v2(ispBase.IspBase):
|
|||
def sendISP(self, data):
|
||||
recv = self.sendMessage([0x1D, 4, 4, 0, data[0], data[1], data[2], data[3]])
|
||||
return recv[2:6]
|
||||
|
||||
|
||||
def writeFlash(self, flash_data):
|
||||
#Set load addr to 0, in case we have more then 64k flash we need to enable the address extension
|
||||
page_size = self.chip["pageSize"] * 2
|
||||
|
@ -89,15 +89,15 @@ class Stk500v2(ispBase.IspBase):
|
|||
self.sendMessage([0x06, 0x80, 0x00, 0x00, 0x00])
|
||||
else:
|
||||
self.sendMessage([0x06, 0x00, 0x00, 0x00, 0x00])
|
||||
load_count = (len(flash_data) + page_size - 1) / page_size
|
||||
load_count = (len(flash_data) + page_size - 1) / page_size
|
||||
for i in range(0, int(load_count)):
|
||||
recv = self.sendMessage([0x13, page_size >> 8, page_size & 0xFF, 0xc1, 0x0a, 0x40, 0x4c, 0x20, 0x00, 0x00] + flash_data[(i * page_size):(i * page_size + page_size)])
|
||||
if self.progress_callback is not None:
|
||||
if self._has_checksum:
|
||||
self.progress_callback(i + 1, load_count)
|
||||
else:
|
||||
self.progress_callback(i + 1, load_count*2)
|
||||
|
||||
self.progress_callback(i + 1, load_count * 2)
|
||||
|
||||
def verifyFlash(self, flash_data):
|
||||
if self._has_checksum:
|
||||
self.sendMessage([0x06, 0x00, (len(flash_data) >> 17) & 0xFF, (len(flash_data) >> 9) & 0xFF, (len(flash_data) >> 1) & 0xFF])
|
||||
|
@ -121,7 +121,7 @@ class Stk500v2(ispBase.IspBase):
|
|||
for i in range(0, int(load_count)):
|
||||
recv = self.sendMessage([0x14, 0x01, 0x00, 0x20])[2:0x102]
|
||||
if self.progress_callback is not None:
|
||||
self.progress_callback(load_count + i + 1, load_count*2)
|
||||
self.progress_callback(load_count + i + 1, load_count * 2)
|
||||
for j in range(0, 0x100):
|
||||
if i * 0x100 + j < len(flash_data) and flash_data[i * 0x100 + j] != recv[j]:
|
||||
raise ispBase.IspError("Verify error at: 0x%x" % (i * 0x100 + j))
|
||||
|
@ -141,7 +141,7 @@ class Stk500v2(ispBase.IspBase):
|
|||
raise ispBase.IspError("Serial send timeout")
|
||||
self.seq = (self.seq + 1) & 0xFF
|
||||
return self.recvMessage()
|
||||
|
||||
|
||||
def recvMessage(self):
|
||||
state = "Start"
|
||||
checksum = 0
|
||||
|
|
|
@ -10,7 +10,7 @@ catalog = i18nCatalog("cura")
|
|||
|
||||
class BedLevelMachineAction(MachineAction):
|
||||
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._bed_level_position = 0
|
||||
|
||||
|
@ -18,6 +18,11 @@ class BedLevelMachineAction(MachineAction):
|
|||
pass
|
||||
|
||||
def _reset(self):
|
||||
self._bed_level_position = 0
|
||||
pass
|
||||
|
||||
@pyqtSlot()
|
||||
def startBedLeveling(self):
|
||||
self._bed_level_position = 0
|
||||
printer_output_devices = self._getPrinterOutputDevices()
|
||||
if printer_output_devices:
|
||||
|
@ -52,4 +57,5 @@ class BedLevelMachineAction(MachineAction):
|
|||
output_device.moveHead(0, 0, -3)
|
||||
self._bed_level_position += 1
|
||||
elif self._bed_level_position >= 3:
|
||||
output_device.sendCommand("M18") # Turn off all motors so the user can move the axes
|
||||
self.setFinished()
|
|
@ -24,7 +24,7 @@ Cura.MachineAction
|
|||
{
|
||||
id: pageTitle
|
||||
width: parent.width
|
||||
text: catalog.i18nc("@title", "Bed Leveling")
|
||||
text: catalog.i18nc("@title", "Build Plate Leveling")
|
||||
wrapMode: Text.WordWrap
|
||||
font.pointSize: 18;
|
||||
}
|
||||
|
@ -44,40 +44,40 @@ Cura.MachineAction
|
|||
anchors.topMargin: UM.Theme.getSize("default_margin").height
|
||||
width: parent.width
|
||||
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
|
||||
anchors.top: bedlevelingText.bottom
|
||||
anchors.topMargin: UM.Theme.getSize("default_margin").height
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
height: skipBedlevelingButton.height
|
||||
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
|
||||
width: childrenRect.width
|
||||
spacing: UM.Theme.getSize("default_margin").width
|
||||
|
||||
Button
|
||||
{
|
||||
id: bedlevelingButton
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
text: catalog.i18nc("@action:button","Move to Next Position");
|
||||
id: startBedLevelingButton
|
||||
text: catalog.i18nc("@action:button","Start Build Plate Leveling")
|
||||
onClicked:
|
||||
{
|
||||
manager.moveToNextLevelPosition()
|
||||
startBedLevelingButton.visible = false;
|
||||
bedlevelingButton.visible = true;
|
||||
checkupMachineAction.heatupHotendStarted = false;
|
||||
checkupMachineAction.heatupBedStarted = false;
|
||||
manager.startCheck();
|
||||
}
|
||||
}
|
||||
|
||||
Button
|
||||
{
|
||||
id: skipBedlevelingButton
|
||||
anchors.top: parent.width < bedLevelMachineAction.width ? parent.top : bedlevelingButton.bottom
|
||||
anchors.topMargin: parent.width < bedLevelMachineAction.width ? 0 : UM.Theme.getSize("default_margin").height/2
|
||||
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");
|
||||
id: bedlevelingButton
|
||||
text: catalog.i18nc("@action:button","Move to Next Position")
|
||||
visible: false
|
||||
onClicked:
|
||||
{
|
||||
manager.setFinished()
|
||||
manager.moveToNextLevelPosition();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -155,6 +155,7 @@ class UMOCheckupMachineAction(MachineAction):
|
|||
if output_devices:
|
||||
self._output_device = output_devices[0]
|
||||
try:
|
||||
self._output_device.sendCommand("M18") # Turn off all motors so the user can move the axes
|
||||
self._output_device.startPollEndstop()
|
||||
self._output_device.bedTemperatureChanged.connect(self.bedTemperatureChanged)
|
||||
self._output_device.hotendTemperaturesChanged.connect(self.hotendTemperatureChanged)
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
||||
Item
|
||||
Row
|
||||
{
|
||||
id: startStopButtons
|
||||
anchors.top: pageDescription.bottom
|
||||
anchors.topMargin: UM.Theme.getSize("default_margin").height
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
height: childrenRect.height
|
||||
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
|
||||
width: childrenRect.width
|
||||
spacing: UM.Theme.getSize("default_margin").width
|
||||
Button
|
||||
{
|
||||
id: startCheckButton
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
text: catalog.i18nc("@action:button","Start Printer Check");
|
||||
onClicked:
|
||||
{
|
||||
checkupMachineAction.heatupHotendStarted = false
|
||||
checkupMachineAction.heatupBedStarted = false
|
||||
manager.startCheck()
|
||||
checkupMachineAction.heatupHotendStarted = false;
|
||||
checkupMachineAction.heatupBedStarted = false;
|
||||
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
|
||||
|
@ -232,7 +220,7 @@ Cura.MachineAction
|
|||
anchors.left: parent.left
|
||||
anchors.top: nozzleTempLabel.bottom
|
||||
wrapMode: Text.WordWrap
|
||||
text: catalog.i18nc("@label","Bed temperature check:")
|
||||
text: catalog.i18nc("@label","Build plate temperature check:")
|
||||
visible: checkupMachineAction.usbConnected && manager.hasHeatedBed
|
||||
}
|
||||
|
||||
|
|
|
@ -12,6 +12,9 @@ class UMOUpgradeSelection(MachineAction):
|
|||
super().__init__("UMOUpgradeSelection", catalog.i18nc("@action", "Select upgrades"))
|
||||
self._qml_url = "UMOUpgradeSelectionMachineAction.qml"
|
||||
|
||||
def _reset(self):
|
||||
self.heatedBedChanged.emit()
|
||||
|
||||
heatedBedChanged = pyqtSignal()
|
||||
|
||||
@pyqtProperty(bool, notify = heatedBedChanged)
|
||||
|
|
|
@ -42,9 +42,9 @@ Cura.MachineAction
|
|||
anchors.top: pageDescription.bottom
|
||||
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
|
||||
onClicked: manager.hasHeatedBed ? manager.removeHeatedBed() : manager.addHeatedBed()
|
||||
onClicked: checked ? manager.addHeatedBed() : manager.removeHeatedBed()
|
||||
}
|
||||
|
||||
UM.I18nCatalog { id: catalog; name: "cura"; }
|
||||
|
|
|
@ -56,29 +56,21 @@ Cura.MachineAction
|
|||
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.");
|
||||
}
|
||||
Item
|
||||
Row
|
||||
{
|
||||
anchors.top: upgradeText2.bottom
|
||||
anchors.topMargin: UM.Theme.getSize("default_margin").height
|
||||
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
|
||||
{
|
||||
id: upgradeButton
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
text: catalog.i18nc("@action:button","Upgrade to Marlin Firmware");
|
||||
onClicked: Cura.USBPrinterManager.updateAllFirmware()
|
||||
}
|
||||
Button
|
||||
{
|
||||
id: skipUpgradeButton
|
||||
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()
|
||||
onClicked:
|
||||
{
|
||||
Cura.USBPrinterManager.updateAllFirmware()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
||||
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)
|
||||
active_profile = VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translateProfile(self._active_profile_name)
|
||||
active_material = VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translateProfile(self._active_material_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 = [
|
||||
self._name + "_current_settings",
|
||||
current_settings,
|
||||
active_profile,
|
||||
active_material,
|
||||
variant,
|
||||
|
@ -97,4 +105,4 @@ class MachineInstance:
|
|||
|
||||
output = io.StringIO()
|
||||
config.write(output)
|
||||
return self._filename, output.getvalue()
|
||||
return [self._filename], [output.getvalue()]
|
|
@ -77,4 +77,4 @@ class Preferences:
|
|||
#Output the result as a string.
|
||||
output = io.StringIO()
|
||||
self._config.write(output)
|
||||
return self._filename, output.getvalue()
|
||||
return [self._filename], [output.getvalue()]
|
|
@ -91,7 +91,7 @@ class Profile:
|
|||
translated_machine = VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translatePrinter(self._machine_type_id)
|
||||
config.set("general", "definition", translated_machine)
|
||||
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")
|
||||
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))
|
||||
else:
|
||||
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:
|
||||
VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translateSettings(self._settings)
|
||||
|
@ -128,6 +126,34 @@ class Profile:
|
|||
for item in disabled_settings_defaults[1:]:
|
||||
disabled_defaults_string += "," + str(item)
|
||||
|
||||
output = io.StringIO()
|
||||
config.write(output)
|
||||
return self._filename, output.getvalue()
|
||||
#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()
|
||||
config.write(output)
|
||||
outputs.append(output.getvalue())
|
||||
return filenames, outputs
|
|
@ -9,6 +9,29 @@ from . import MachineInstance # To upgrade machine instances.
|
|||
from . import Preferences #To upgrade preferences.
|
||||
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.
|
||||
_printer_translations = {
|
||||
"ultimaker2plus": "ultimaker2_plus"
|
||||
|
@ -34,7 +57,14 @@ _setting_name_translations = {
|
|||
"skirt_line_width": "skirt_brim_line_width",
|
||||
"skirt_minimal_length": "skirt_brim_minimal_length",
|
||||
"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
|
||||
|
@ -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.
|
||||
#
|
||||
# It converts the machine instances and profiles.
|
||||
|
@ -70,6 +118,13 @@ class VersionUpgrade21to22(VersionUpgrade):
|
|||
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.
|
||||
|
||||
## 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.
|
||||
#
|
||||
# \param serialised The serialised machine instance in version 1.
|
||||
|
@ -146,32 +201,11 @@ class VersionUpgrade21to22(VersionUpgrade):
|
|||
for key, value in settings.items():
|
||||
if key == "fill_perimeter_gaps": #Setting is removed.
|
||||
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.
|
||||
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]
|
||||
settings["retraction_hop_enabled"] = 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
|
||||
settings[_setting_name_translations[key]] = value
|
||||
return settings
|
||||
|
||||
## Translates a setting name for the change from Cura 2.1 to 2.2.
|
||||
|
@ -194,4 +228,18 @@ class VersionUpgrade21to22(VersionUpgrade):
|
|||
def translateVariant(variant, machine):
|
||||
if machine in _variant_translations and variant in _variant_translations[machine]:
|
||||
return _variant_translations[machine][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
|
|
@ -161,7 +161,7 @@
|
|||
},
|
||||
"platform_adhesion":
|
||||
{
|
||||
"label": "Platform Adhesion",
|
||||
"label": "Build Plate Adhesion",
|
||||
"type": "category",
|
||||
"icon": "category_adhesion",
|
||||
"description": "Adhesion",
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -54,13 +54,13 @@
|
|||
"speed_travel": { "default_value": 150 },
|
||||
"speed_layer_0": {
|
||||
"minimum_value": "0.1",
|
||||
"default": 15.0
|
||||
"default_value": 15.0
|
||||
},
|
||||
"infill_overlap": { "default": 10 },
|
||||
"cool_fan_enabled": { "default": false },
|
||||
"cool_fan_speed": { "default": 0 },
|
||||
"skirt_line_count": { "default": 3 },
|
||||
"skirt_gap": { "default": 4 },
|
||||
"skirt_brim_minimal_length": { "default": 200 }
|
||||
"infill_overlap": { "default_value": 10 },
|
||||
"cool_fan_enabled": { "default_value": false },
|
||||
"cool_fan_speed": { "default_value": 0 },
|
||||
"skirt_line_count": { "default_value": 3 },
|
||||
"skirt_gap": { "default_value": 4 },
|
||||
"skirt_brim_minimal_length": { "default_value": 200 }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
"visible": true,
|
||||
"author": "Calvindog717",
|
||||
"manufacturer": "PrintrBot",
|
||||
"category": "Other",
|
||||
"file_formats": "text/x-gcode"
|
||||
},
|
||||
|
||||
|
|
|
@ -7,5 +7,16 @@
|
|||
"author": "Ultimaker",
|
||||
"manufacturer": "Ultimaker",
|
||||
"visible": false
|
||||
},
|
||||
"overrides": {
|
||||
"material_print_temperature": {
|
||||
"minimum_value": "0"
|
||||
},
|
||||
"material_bed_temperature": {
|
||||
"minimum_value": "0"
|
||||
},
|
||||
"material_standby_temperature": {
|
||||
"minimum_value": "0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
"author": "Ultimaker",
|
||||
"manufacturer": "Ultimaker",
|
||||
"category": "Ultimaker",
|
||||
"weight": 3,
|
||||
"file_formats": "text/x-gcode",
|
||||
"icon": "icon_ultimaker2.png",
|
||||
"platform": "ultimaker2_platform.obj",
|
||||
|
@ -17,10 +18,12 @@
|
|||
},
|
||||
"overrides": {
|
||||
"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" : {
|
||||
"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": {
|
||||
"default_value": 223
|
||||
|
@ -57,7 +60,7 @@
|
|||
"default_value": 2
|
||||
},
|
||||
"gantry_height": {
|
||||
"default_value": 55
|
||||
"default_value": 48
|
||||
},
|
||||
"machine_use_extruder_offset_to_offset_coords": {
|
||||
"default_value": true
|
||||
|
@ -82,10 +85,10 @@
|
|||
"default_value": 45
|
||||
},
|
||||
"material_print_temperature": {
|
||||
"enabled": "False"
|
||||
"enabled": "not (material_flow_dependent_temperature) and machine_gcode_flavor != \"UltiGCode\""
|
||||
},
|
||||
"material_bed_temperature": {
|
||||
"enabled": "False"
|
||||
"enabled": "machine_heated_bed and machine_gcode_flavor != \"UltiGCode\""
|
||||
},
|
||||
"machine_max_feedrate_x": {
|
||||
"default_value": 300
|
||||
|
@ -103,22 +106,22 @@
|
|||
"default_value": 3000
|
||||
},
|
||||
"material_diameter": {
|
||||
"enabled": "False"
|
||||
"enabled": "machine_gcode_flavor != \"UltiGCode\""
|
||||
},
|
||||
"material_flow": {
|
||||
"enabled": "False"
|
||||
"enabled": "machine_gcode_flavor != \"UltiGCode\""
|
||||
},
|
||||
"retraction_amount": {
|
||||
"enabled": "False"
|
||||
"enabled": "retraction_enable and machine_gcode_flavor != \"UltiGCode\""
|
||||
},
|
||||
"retraction_speed": {
|
||||
"enabled": "False"
|
||||
"enabled": "retraction_enable and machine_gcode_flavor != \"UltiGCode\""
|
||||
},
|
||||
"retraction_retract_speed": {
|
||||
"enabled": "False"
|
||||
"enabled": "retraction_enable and machine_gcode_flavor != \"UltiGCode\""
|
||||
},
|
||||
"retraction_prime_speed": {
|
||||
"enabled": "False"
|
||||
"enabled": "retraction_enable and machine_gcode_flavor != \"UltiGCode\""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
"author": "Ultimaker",
|
||||
"manufacturer": "Ultimaker",
|
||||
"category": "Ultimaker",
|
||||
"weight": 3,
|
||||
"file_formats": "text/x-gcode",
|
||||
"icon": "icon_ultimaker2.png",
|
||||
"platform": "ultimaker2_platform.obj",
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
"author": "Ultimaker",
|
||||
"manufacturer": "Ultimaker",
|
||||
"category": "Ultimaker",
|
||||
"weight": 2,
|
||||
"file_formats": "text/x-gcode",
|
||||
"platform": "ultimaker2_platform.obj",
|
||||
"platform_texture": "Ultimaker2ExtendedPlusbackplate.png",
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
"author": "Ultimaker",
|
||||
"manufacturer": "Ultimaker",
|
||||
"category": "Ultimaker",
|
||||
"weight": 3,
|
||||
"file_formats": "text/x-gcode",
|
||||
"icon": "icon_ultimaker2.png",
|
||||
"platform": "ultimaker2go_platform.obj",
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
"author": "Ultimaker",
|
||||
"manufacturer": "Ultimaker",
|
||||
"category": "Ultimaker",
|
||||
"weight": 1,
|
||||
"file_formats": "text/x-gcode",
|
||||
"platform": "ultimaker2_platform.obj",
|
||||
"platform_texture": "Ultimaker2Plusbackplate.png",
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
"author": "Ultimaker",
|
||||
"manufacturer": "Ultimaker",
|
||||
"category": "Ultimaker",
|
||||
"weight": 4,
|
||||
"file_formats": "text/x-gcode",
|
||||
"icon": "icon_ultimaker.png",
|
||||
"platform": "ultimaker_platform.stl",
|
||||
|
@ -15,7 +16,7 @@
|
|||
"preferred_material": "*pla*",
|
||||
"preferred_quality": "*normal*",
|
||||
"first_start_actions": ["UMOUpgradeSelection", "UMOCheckup", "BedLevel"],
|
||||
"supported_actions": ["UMOCheckup", "UpgradeFirmware", "BedLevel", "UMOUpgradeSelection"]
|
||||
"supported_actions": ["UMOUpgradeSelection", "UMOCheckup", "BedLevel", "UpgradeFirmware"]
|
||||
},
|
||||
|
||||
"overrides": {
|
||||
|
|
76
resources/definitions/ultimaker_original_dual.def.json
Normal file
76
resources/definitions/ultimaker_original_dual.def.json
Normal 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}
|
||||
}
|
||||
}
|
|
@ -7,11 +7,13 @@
|
|||
"author": "Ultimaker",
|
||||
"manufacturer": "Ultimaker",
|
||||
"category": "Ultimaker",
|
||||
"weight": 4,
|
||||
"file_formats": "text/x-gcode",
|
||||
"icon": "icon_ultimaker.png",
|
||||
"platform": "ultimaker2_platform.obj",
|
||||
"platform_texture": "UltimakerPlusbackplate.png",
|
||||
"supported_actions":["UMOCheckup", "UpgradeFirmware", "BedLevel"]
|
||||
"first_start_actions": ["UMOCheckup", "BedLevel"],
|
||||
"supported_actions": ["UMOCheckup", "BedLevel", "UpgradeFirmware"]
|
||||
},
|
||||
|
||||
"overrides": {
|
||||
|
|
19
resources/extruders/ultimaker_original_dual_left.def.json
Normal file
19
resources/extruders/ultimaker_original_dual_left.def.json
Normal 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 }
|
||||
}
|
||||
}
|
19
resources/extruders/ultimaker_original_dual_right.def.json
Normal file
19
resources/extruders/ultimaker_original_dual_right.def.json
Normal 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 }
|
||||
}
|
||||
}
|
|
@ -11,7 +11,7 @@ Generic PLA profile. Serves as an example file, data in this file is not correct
|
|||
</name>
|
||||
<GUID>60636bb4-518f-42e7-8237-fe77b194ebe0</GUID>
|
||||
<version>0</version>
|
||||
<color_code>#FF0000</color_code>
|
||||
<color_code>#8cb219</color_code>
|
||||
</metadata>
|
||||
<properties>
|
||||
<density>1.07</density>
|
||||
|
|
|
@ -11,7 +11,7 @@ Generic PLA profile. Serves as an example file, data in this file is not correct
|
|||
</name>
|
||||
<GUID>12f41353-1a33-415e-8b4f-a775a6c70cc6</GUID>
|
||||
<version>0</version>
|
||||
<color_code>#0000FF</color_code>
|
||||
<color_code>#159499</color_code>
|
||||
</metadata>
|
||||
<properties>
|
||||
<density>0.94</density>
|
||||
|
|
|
@ -11,7 +11,7 @@ Generic PLA profile. Serves as an example file, data in this file is not correct
|
|||
</name>
|
||||
<GUID>506c9f0d-e3aa-4bd4-b2d2-23e2425b1aa9</GUID>
|
||||
<version>0</version>
|
||||
<color_code>#00FF00</color_code>
|
||||
<color_code>#ffc924</color_code>
|
||||
</metadata>
|
||||
<properties>
|
||||
<density>1.3</density>
|
||||
|
|
|
@ -27,6 +27,7 @@ Item
|
|||
|
||||
property alias multiplyObject: multiplyObjectAction;
|
||||
|
||||
property alias selectAll: selectAllAction;
|
||||
property alias deleteAll: deleteAllAction;
|
||||
property alias reloadAll: reloadAllAction;
|
||||
property alias resetAllTranslation: resetAllTranslationAction;
|
||||
|
@ -119,7 +120,7 @@ Item
|
|||
Action
|
||||
{
|
||||
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");
|
||||
onTriggered: Cura.MachineManager.updateQualityContainerFromUserContainer()
|
||||
}
|
||||
|
@ -135,7 +136,7 @@ Item
|
|||
Action
|
||||
{
|
||||
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...");
|
||||
}
|
||||
|
||||
|
@ -182,7 +183,7 @@ Item
|
|||
Action
|
||||
{
|
||||
id: deleteObjectAction;
|
||||
text: catalog.i18nc("@action:inmenu","Delete Object");
|
||||
text: catalog.i18nc("@action:inmenu","Delete Model");
|
||||
enabled: UM.Controller.toolsEnabled;
|
||||
iconName: "edit-delete";
|
||||
}
|
||||
|
@ -190,13 +191,13 @@ Item
|
|||
Action
|
||||
{
|
||||
id: centerObjectAction;
|
||||
text: catalog.i18nc("@action:inmenu","Ce&nter Object on Platform");
|
||||
text: catalog.i18nc("@action:inmenu","Ce&nter Model on Platform");
|
||||
}
|
||||
|
||||
Action
|
||||
{
|
||||
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
|
||||
iconName: "object-group"
|
||||
shortcut: "Ctrl+G";
|
||||
|
@ -206,7 +207,7 @@ Item
|
|||
Action
|
||||
{
|
||||
id: unGroupObjectsAction
|
||||
text: catalog.i18nc("@action:inmenu menubar:edit","Ungroup Objects");
|
||||
text: catalog.i18nc("@action:inmenu menubar:edit","Ungroup Models");
|
||||
enabled: UM.Scene.isGroupSelected
|
||||
iconName: "object-ungroup"
|
||||
shortcut: "Ctrl+Shift+G";
|
||||
|
@ -216,7 +217,7 @@ Item
|
|||
Action
|
||||
{
|
||||
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
|
||||
iconName: "merge";
|
||||
shortcut: "Ctrl+Alt+G";
|
||||
|
@ -226,14 +227,24 @@ Item
|
|||
Action
|
||||
{
|
||||
id: multiplyObjectAction;
|
||||
text: catalog.i18nc("@action:inmenu","&Duplicate Object");
|
||||
text: catalog.i18nc("@action:inmenu","&Duplicate Model");
|
||||
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
|
||||
{
|
||||
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;
|
||||
iconName: "edit-delete";
|
||||
shortcut: "Ctrl+D";
|
||||
|
@ -243,7 +254,7 @@ Item
|
|||
Action
|
||||
{
|
||||
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";
|
||||
onTriggered: Printer.reloadAll();
|
||||
}
|
||||
|
@ -251,14 +262,14 @@ Item
|
|||
Action
|
||||
{
|
||||
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();
|
||||
}
|
||||
|
||||
Action
|
||||
{
|
||||
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();
|
||||
}
|
||||
|
||||
|
|
|
@ -16,12 +16,24 @@ UM.Dialog
|
|||
{
|
||||
id: base
|
||||
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)
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -36,6 +48,7 @@ UM.Dialog
|
|||
right: parent.right;
|
||||
bottom: parent.bottom;
|
||||
}
|
||||
|
||||
ListView
|
||||
{
|
||||
id: machineList
|
||||
|
@ -43,9 +56,12 @@ UM.Dialog
|
|||
model: UM.DefinitionContainersModel
|
||||
{
|
||||
id: machineDefinitionsModel
|
||||
filter: {"visible":true}
|
||||
filter: { "visible": true }
|
||||
sectionProperty: "category"
|
||||
preferredSectionValue: preferredCategory
|
||||
}
|
||||
section.property: "manufacturer"
|
||||
|
||||
section.property: "section"
|
||||
section.delegate: Button
|
||||
{
|
||||
text: section
|
||||
|
@ -76,16 +92,25 @@ UM.Dialog
|
|||
sourceSize.width: width
|
||||
sourceSize.height: width
|
||||
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:
|
||||
{
|
||||
base.activeManufacturer = section;
|
||||
machineList.currentIndex = machineList.model.find("manufacturer", section)
|
||||
machineName.text = getMachineName()
|
||||
base.activeCategory = section;
|
||||
if (machineList.model.getItem(machineList.currentIndex).section != section) {
|
||||
// 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
|
||||
{
|
||||
name: "collapsed";
|
||||
when: base.activeManufacturer != model.manufacturer;
|
||||
when: base.activeCategory != model.section;
|
||||
|
||||
PropertyChanges { target: machineButton; opacity: 0; height: 0; }
|
||||
}
|
||||
|
@ -158,13 +183,17 @@ UM.Dialog
|
|||
text: catalog.i18nc("@action:button", "Add Printer")
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.right: parent.right
|
||||
onClicked:
|
||||
{
|
||||
base.visible = false
|
||||
var item = machineList.model.getItem(machineList.currentIndex);
|
||||
Cura.MachineManager.addMachine(machineName.text, item.id)
|
||||
base.machineAdded(item.id) // Emit signal that the user added a machine.
|
||||
}
|
||||
onClicked: addMachine()
|
||||
}
|
||||
|
||||
onAccepted: addMachine()
|
||||
|
||||
function addMachine()
|
||||
{
|
||||
base.visible = false
|
||||
var item = machineList.model.getItem(machineList.currentIndex);
|
||||
Cura.MachineManager.addMachine(machineName.text, item.id)
|
||||
base.machineAdded(item.id) // Emit signal that the user added a machine.
|
||||
}
|
||||
|
||||
Item
|
||||
|
|
|
@ -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"; }
|
||||
}
|
||||
}
|
|
@ -54,10 +54,7 @@ UM.MainWindow
|
|||
Keys.onPressed: {
|
||||
if (event.key == Qt.Key_Backspace)
|
||||
{
|
||||
if(objectContextMenu.objectId != 0)
|
||||
{
|
||||
Printer.deleteObject(objectContextMenu.objectId);
|
||||
}
|
||||
Cura.Actions.deleteSelection.trigger()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -121,6 +118,7 @@ UM.MainWindow
|
|||
MenuItem { action: Cura.Actions.undo; }
|
||||
MenuItem { action: Cura.Actions.redo; }
|
||||
MenuSeparator { }
|
||||
MenuItem { action: Cura.Actions.selectAll; }
|
||||
MenuItem { action: Cura.Actions.deleteSelection; }
|
||||
MenuItem { action: Cura.Actions.deleteAll; }
|
||||
MenuItem { action: Cura.Actions.resetAllTranslation; }
|
||||
|
@ -540,6 +538,7 @@ UM.MainWindow
|
|||
MenuItem { action: Cura.Actions.deleteObject; }
|
||||
MenuItem { action: Cura.Actions.multiplyObject; }
|
||||
MenuSeparator { }
|
||||
MenuItem { action: Cura.Actions.selectAll; }
|
||||
MenuItem { action: Cura.Actions.deleteAll; }
|
||||
MenuItem { action: Cura.Actions.reloadAll; }
|
||||
MenuItem { action: Cura.Actions.resetAllTranslation; }
|
||||
|
@ -592,6 +591,7 @@ UM.MainWindow
|
|||
Menu
|
||||
{
|
||||
id: contextMenu;
|
||||
MenuItem { action: Cura.Actions.selectAll; }
|
||||
MenuItem { action: Cura.Actions.deleteAll; }
|
||||
MenuItem { action: Cura.Actions.reloadAll; }
|
||||
MenuItem { action: Cura.Actions.resetAllTranslation; }
|
||||
|
@ -677,6 +677,7 @@ UM.MainWindow
|
|||
id: addMachineDialog
|
||||
onMachineAdded:
|
||||
{
|
||||
machineActionsWizard.firstRun = addMachineDialog.firstRun
|
||||
machineActionsWizard.start(id)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -209,7 +209,7 @@ Rectangle {
|
|||
lengths = ["0.00"];
|
||||
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(" + "));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,10 @@ import QtQuick 2.2
|
|||
Item
|
||||
{
|
||||
id: contentItem
|
||||
|
||||
// Point to the dialog containing the displayItem
|
||||
property var dialog
|
||||
|
||||
// Connect the finished property change to completed signal.
|
||||
property var finished: manager.finished
|
||||
onFinishedChanged: if(manager.finished) {completed()}
|
||||
|
|
|
@ -15,23 +15,57 @@ Rectangle
|
|||
UM.I18nCatalog { id: catalog; name:"cura"}
|
||||
|
||||
property bool printerConnected: Cura.MachineManager.printerOutputDevices.length != 0
|
||||
property real progress: printerConnected ? Cura.MachineManager.printerOutputDevices[0].progress : 0;
|
||||
property int backendState: UM.Backend.state;
|
||||
property bool printerAcceptsCommands: printerConnected && Cura.MachineManager.printerOutputDevices[0].acceptsCommands
|
||||
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:
|
||||
{
|
||||
if(!printerConnected)
|
||||
return UM.Theme.getColor("status_offline")
|
||||
else if(Cura.MachineManager.printerOutputDevices[0].jobState == "printing" || Cura.MachineManager.printerOutputDevices[0].jobState == "pre_print")
|
||||
return UM.Theme.getColor("status_busy")
|
||||
else if(Cura.MachineManager.printerOutputDevices[0].jobState == "ready" || Cura.MachineManager.printerOutputDevices[0].jobState == "")
|
||||
return UM.Theme.getColor("status_ready")
|
||||
else if(Cura.MachineManager.printerOutputDevices[0].jobState == "paused")
|
||||
return UM.Theme.getColor("status_paused")
|
||||
else if (Cura.MachineManager.printerOutputDevices[0].jobState == "error")
|
||||
return UM.Theme.getColor("status_stopped")
|
||||
else
|
||||
return UM.Theme.getColor("text")
|
||||
if(!printerConnected || !printerAcceptsCommands)
|
||||
return UM.Theme.getColor("text");
|
||||
|
||||
switch(Cura.MachineManager.printerOutputDevices[0].jobState)
|
||||
{
|
||||
case "printing":
|
||||
case "pre_print":
|
||||
case "wait_cleanup":
|
||||
return UM.Theme.getColor("status_busy");
|
||||
case "ready":
|
||||
case "":
|
||||
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;
|
||||
|
@ -40,24 +74,28 @@ Rectangle
|
|||
property string statusText:
|
||||
{
|
||||
if(!printerConnected)
|
||||
{
|
||||
return catalog.i18nc("@label:", "Please check your printer connections")
|
||||
} else if(Cura.MachineManager.printerOutputDevices[0].jobState == "printing")
|
||||
{
|
||||
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 " "
|
||||
}
|
||||
return catalog.i18nc("@label:MonitorStatus", "Not connected to a printer");
|
||||
if(!printerAcceptsCommands)
|
||||
return catalog.i18nc("@label:MonitorStatus", "Printer does not accept commands");
|
||||
|
||||
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
|
||||
|
@ -70,7 +108,7 @@ Rectangle
|
|||
|
||||
color: base.statusColor
|
||||
font: UM.Theme.getFont("large")
|
||||
text: statusText;
|
||||
text: statusText
|
||||
}
|
||||
|
||||
Label
|
||||
|
@ -81,8 +119,8 @@ Rectangle
|
|||
|
||||
color: base.statusColor
|
||||
font: UM.Theme.getFont("large")
|
||||
text: Math.round(progress) + "%";
|
||||
visible: printerConnected
|
||||
text: Math.round(progress) + "%"
|
||||
visible: showProgress
|
||||
}
|
||||
|
||||
Rectangle
|
||||
|
@ -96,6 +134,7 @@ Rectangle
|
|||
anchors.leftMargin: UM.Theme.getSize("default_margin").width
|
||||
radius: UM.Theme.getSize("progressbar_radius").width
|
||||
color: UM.Theme.getColor("progressbar_background")
|
||||
visible: showProgress
|
||||
|
||||
Rectangle
|
||||
{
|
||||
|
@ -111,7 +150,8 @@ Rectangle
|
|||
id: abortButton
|
||||
|
||||
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
|
||||
anchors.top: progressBar.bottom
|
||||
|
@ -120,20 +160,35 @@ Rectangle
|
|||
anchors.rightMargin: UM.Theme.getSize("default_margin").width
|
||||
|
||||
text: catalog.i18nc("@label:", "Abort Print")
|
||||
onClicked: { Cura.MachineManager.printerOutputDevices[0].setJobState("abort") }
|
||||
|
||||
onClicked: Cura.MachineManager.printerOutputDevices[0].setJobState("abort")
|
||||
|
||||
style: ButtonStyle
|
||||
{
|
||||
background: Rectangle
|
||||
{
|
||||
border.width: UM.Theme.getSize("default_lining").width
|
||||
border.color: !control.enabled ? UM.Theme.getColor("action_button_disabled_border") :
|
||||
control.pressed ? UM.Theme.getColor("action_button_active_border") :
|
||||
control.hovered ? UM.Theme.getColor("action_button_hovered_border") : UM.Theme.getColor("action_button_border")
|
||||
color: !control.enabled ? UM.Theme.getColor("action_button_disabled") :
|
||||
control.pressed ? UM.Theme.getColor("action_button_active") :
|
||||
control.hovered ? UM.Theme.getColor("action_button_hovered") : UM.Theme.getColor("action_button")
|
||||
border.color:
|
||||
{
|
||||
if(!control.enabled)
|
||||
return UM.Theme.getColor("action_button_disabled_border");
|
||||
else if(control.pressed)
|
||||
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; } }
|
||||
|
||||
implicitWidth: actualLabel.contentWidth + (UM.Theme.getSize("default_margin").width * 2)
|
||||
|
@ -142,9 +197,17 @@ Rectangle
|
|||
{
|
||||
id: actualLabel
|
||||
anchors.centerIn: parent
|
||||
color: !control.enabled ? UM.Theme.getColor("action_button_disabled_text") :
|
||||
control.pressed ? UM.Theme.getColor("action_button_active_text") :
|
||||
control.hovered ? UM.Theme.getColor("action_button_hovered_text") : UM.Theme.getColor("action_button_text")
|
||||
color:
|
||||
{
|
||||
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")
|
||||
text: control.text;
|
||||
}
|
||||
|
@ -155,10 +218,7 @@ Rectangle
|
|||
|
||||
Button
|
||||
{
|
||||
id: pauseButton
|
||||
|
||||
visible: printerConnected
|
||||
enabled: printerConnected && (Cura.MachineManager.printerOutputDevices[0].jobState == "paused" || Cura.MachineManager.printerOutputDevices[0].jobState == "printing")
|
||||
id: pauseResumeButton
|
||||
|
||||
height: UM.Theme.getSize("save_button_save_to_button").height
|
||||
anchors.top: progressBar.bottom
|
||||
|
@ -166,20 +226,77 @@ Rectangle
|
|||
anchors.right: abortButton.left
|
||||
anchors.rightMargin: UM.Theme.getSize("default_margin").width
|
||||
|
||||
text: printerConnected ? Cura.MachineManager.printerOutputDevices[0].jobState == "paused" ? catalog.i18nc("@label:", "Resume") : catalog.i18nc("@label:", "Pause") : ""
|
||||
onClicked: { Cura.MachineManager.printerOutputDevices[0].jobState == "paused" ? Cura.MachineManager.printerOutputDevices[0].setJobState("print") : Cura.MachineManager.printerOutputDevices[0].setJobState("pause") }
|
||||
property bool userClicked: false
|
||||
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
|
||||
{
|
||||
background: Rectangle
|
||||
{
|
||||
border.width: UM.Theme.getSize("default_lining").width
|
||||
border.color: !control.enabled ? UM.Theme.getColor("action_button_disabled_border") :
|
||||
control.pressed ? UM.Theme.getColor("action_button_active_border") :
|
||||
control.hovered ? UM.Theme.getColor("action_button_hovered_border") : UM.Theme.getColor("action_button_border")
|
||||
color: !control.enabled ? UM.Theme.getColor("action_button_disabled") :
|
||||
control.pressed ? UM.Theme.getColor("action_button_active") :
|
||||
control.hovered ? UM.Theme.getColor("action_button_hovered") : UM.Theme.getColor("action_button")
|
||||
border.color:
|
||||
{
|
||||
if(!control.enabled)
|
||||
return UM.Theme.getColor("action_button_disabled_border");
|
||||
else if(control.pressed)
|
||||
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; } }
|
||||
|
||||
implicitWidth: actualLabel.contentWidth + (UM.Theme.getSize("default_margin").width * 2)
|
||||
|
@ -188,11 +305,19 @@ Rectangle
|
|||
{
|
||||
id: actualLabel
|
||||
anchors.centerIn: parent
|
||||
color: !control.enabled ? UM.Theme.getColor("action_button_disabled_text") :
|
||||
control.pressed ? UM.Theme.getColor("action_button_active_text") :
|
||||
control.hovered ? UM.Theme.getColor("action_button_hovered_text") : UM.Theme.getColor("action_button_text")
|
||||
color:
|
||||
{
|
||||
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")
|
||||
text: control.text;
|
||||
text: control.text
|
||||
}
|
||||
}
|
||||
label: Item { }
|
||||
|
|
|
@ -69,6 +69,7 @@ UM.PreferencesPage
|
|||
|
||||
Row
|
||||
{
|
||||
spacing: UM.Theme.getSize("default_margin").width
|
||||
Label
|
||||
{
|
||||
id: languageLabel
|
||||
|
@ -167,7 +168,7 @@ UM.PreferencesPage
|
|||
UM.TooltipArea {
|
||||
width: childrenRect.width;
|
||||
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
|
||||
{
|
||||
|
@ -181,17 +182,18 @@ UM.PreferencesPage
|
|||
UM.TooltipArea {
|
||||
width: childrenRect.width
|
||||
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
|
||||
{
|
||||
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"))
|
||||
onCheckedChanged: UM.Preferences.setValue("physics/automatic_push_free", checked)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
UM.TooltipArea {
|
||||
width: childrenRect.width;
|
||||
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
|
||||
{
|
||||
|
@ -232,12 +247,12 @@ UM.PreferencesPage
|
|||
UM.TooltipArea {
|
||||
width: childrenRect.width
|
||||
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
|
||||
{
|
||||
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"))
|
||||
onCheckedChanged: UM.Preferences.setValue("mesh/scale_to_fit", checked)
|
||||
}
|
||||
|
@ -246,12 +261,12 @@ UM.PreferencesPage
|
|||
UM.TooltipArea {
|
||||
width: childrenRect.width
|
||||
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
|
||||
{
|
||||
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"))
|
||||
onCheckedChanged: UM.Preferences.setValue("mesh/scale_tiny_meshes", checked)
|
||||
}
|
||||
|
|
|
@ -18,7 +18,10 @@ UM.ManagementPage
|
|||
}
|
||||
|
||||
activeId: Cura.MachineManager.activeMachineId
|
||||
activeIndex: {
|
||||
activeIndex: activeMachineIndex()
|
||||
|
||||
function activeMachineIndex()
|
||||
{
|
||||
for(var i = 0; i < model.rowCount(); i++) {
|
||||
if (model.getItem(i).id == Cura.MachineManager.activeMachineId) {
|
||||
return i;
|
||||
|
@ -83,16 +86,17 @@ UM.ManagementPage
|
|||
Repeater
|
||||
{
|
||||
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
|
||||
{
|
||||
text: machineActionRepeater.model[index].label;
|
||||
text: machineActionRepeater.model[index].label
|
||||
onClicked:
|
||||
{
|
||||
actionDialog.content = machineActionRepeater.model[index].displayItem
|
||||
machineActionRepeater.model[index].displayItem.reset()
|
||||
actionDialog.show()
|
||||
actionDialog.content = machineActionRepeater.model[index].displayItem;
|
||||
machineActionRepeater.model[index].displayItem.reset();
|
||||
actionDialog.title = machineActionRepeater.model[index].label;
|
||||
actionDialog.show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -106,6 +110,13 @@ UM.ManagementPage
|
|||
{
|
||||
contents = content;
|
||||
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
|
||||
|
||||
Label { text: catalog.i18nc("@label", "Type") }
|
||||
Label { text: base.currentItem ? base.currentItem.metadata.definition_name : "" }
|
||||
Label
|
||||
{
|
||||
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"; }
|
||||
|
@ -128,7 +145,16 @@ UM.ManagementPage
|
|||
{
|
||||
id: confirmDialog;
|
||||
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
|
||||
|
@ -138,11 +164,20 @@ UM.ManagementPage
|
|||
onAccepted:
|
||||
{
|
||||
Cura.MachineManager.renameMachine(base.currentItem.id, newName.trim());
|
||||
//Reselect current item to update details panel
|
||||
var index = objectList.currentIndex
|
||||
objectList.currentIndex = -1
|
||||
objectList.currentIndex = index
|
||||
//Force updating currentItem and the details panel
|
||||
objectList.onCurrentIndexChanged()
|
||||
}
|
||||
}
|
||||
|
||||
Connections
|
||||
{
|
||||
target: Cura.MachineManager
|
||||
onGlobalContainerChanged:
|
||||
{
|
||||
objectList.currentIndex = activeMachineIndex()
|
||||
objectList.onCurrentIndexChanged()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -67,6 +67,8 @@ UM.ManagementPage
|
|||
enabled: base.currentItem != null && base.currentItem.id != Cura.MachineManager.activeMaterialId
|
||||
onClicked: Cura.MachineManager.setActiveMaterial(base.currentItem.id)
|
||||
},
|
||||
/*
|
||||
// disabled because it has a lot of issues
|
||||
Button
|
||||
{
|
||||
text: catalog.i18nc("@action:button", "Duplicate");
|
||||
|
@ -89,7 +91,7 @@ UM.ManagementPage
|
|||
|
||||
Cura.MachineManager.setActiveMaterial(material_id)
|
||||
}
|
||||
},
|
||||
}, */
|
||||
Button
|
||||
{
|
||||
text: catalog.i18nc("@action:button", "Remove");
|
||||
|
@ -253,7 +255,7 @@ UM.ManagementPage
|
|||
else if(result.status == "success")
|
||||
{
|
||||
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()
|
||||
}
|
||||
CuraApplication.setDefaultPath("dialog_material_path", folder)
|
||||
|
|
|
@ -75,7 +75,7 @@ UM.ManagementPage
|
|||
onClicked:
|
||||
{
|
||||
var selectedContainer;
|
||||
if (objectList.currentItem.id == Cura.MachineManager.activeQualityId) {
|
||||
if (base.currentItem.id == Cura.MachineManager.activeQualityId) {
|
||||
selectedContainer = Cura.MachineManager.newQualityContainerFromQualityAndUser();
|
||||
} else {
|
||||
selectedContainer = Cura.MachineManager.duplicateContainer(base.currentItem.id);
|
||||
|
@ -197,7 +197,9 @@ UM.ManagementPage
|
|||
anchors.bottom: parent.bottom
|
||||
|
||||
ListView {
|
||||
model: Cura.ContainerSettingsModel{ containers:
|
||||
model: Cura.ContainerSettingsModel
|
||||
{
|
||||
containers:
|
||||
{
|
||||
if (!currentItem) {
|
||||
return []
|
||||
|
|
|
@ -12,6 +12,18 @@ import Cura 1.0 as Cura
|
|||
Column
|
||||
{
|
||||
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
|
||||
{
|
||||
|
@ -24,8 +36,8 @@ Column
|
|||
delegate: Loader
|
||||
{
|
||||
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 value: printerConnected ? Math.round(Cura.MachineManager.printerOutputDevices[0].hotendTemperatures[index]) + "°C" : ""
|
||||
property string label: machineExtruderCount.properties.value > 1 ? extrudersModel.getItem(index).name : catalog.i18nc("@label", "Hotend")
|
||||
property string value: printerConnected ? Math.round(connectedPrinter.hotendTemperatures[index]) + "°C" : ""
|
||||
}
|
||||
}
|
||||
Repeater
|
||||
|
@ -34,8 +46,8 @@ Column
|
|||
delegate: Loader
|
||||
{
|
||||
sourceComponent: monitorItem
|
||||
property string label: catalog.i18nc("@label", "Bed Temperature")
|
||||
property string value: printerConnected ? Math.round(Cura.MachineManager.printerOutputDevices[0].bedTemperature) + "°C" : ""
|
||||
property string label: catalog.i18nc("@label", "Build plate")
|
||||
property string value: printerConnected ? Math.round(connectedPrinter.bedTemperature) + "°C" : ""
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -48,19 +60,19 @@ Column
|
|||
{
|
||||
sourceComponent: monitorItem
|
||||
property string label: catalog.i18nc("@label", "Job Name")
|
||||
property string value: printerConnected ? Cura.MachineManager.printerOutputDevices[0].jobName : ""
|
||||
property string value: printerConnected ? connectedPrinter.jobName : ""
|
||||
}
|
||||
Loader
|
||||
{
|
||||
sourceComponent: monitorItem
|
||||
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
|
||||
{
|
||||
sourceComponent: monitorItem
|
||||
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
|
||||
|
@ -70,21 +82,24 @@ Column
|
|||
Row
|
||||
{
|
||||
height: UM.Theme.getSize("setting_control").height
|
||||
width: base.width - 2 * UM.Theme.getSize("default_margin").width
|
||||
Label
|
||||
{
|
||||
text: label
|
||||
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
|
||||
width: parent.width * 0.4
|
||||
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
|
||||
{
|
||||
text: value
|
||||
color: printerConnected ? UM.Theme.getColor("setting_control_text") : UM.Theme.getColor("setting_control_disabled_text")
|
||||
font: UM.Theme.getFont("default")
|
||||
width: parent.width * 0.6
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -84,6 +84,27 @@ Rectangle {
|
|||
anchors.topMargin: UM.Theme.getSize("default_margin").height
|
||||
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 {
|
||||
id: saveToButton
|
||||
|
||||
|
@ -102,8 +123,7 @@ Rectangle {
|
|||
}
|
||||
|
||||
style: ButtonStyle {
|
||||
background:
|
||||
Rectangle
|
||||
background: Rectangle
|
||||
{
|
||||
border.width: UM.Theme.getSize("default_lining").width
|
||||
border.color: !control.enabled ? UM.Theme.getColor("action_button_disabled_border") :
|
||||
|
@ -126,7 +146,7 @@ Rectangle {
|
|||
text: control.text;
|
||||
}
|
||||
}
|
||||
label: Item { }
|
||||
label: Item { }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
||||
Connections
|
||||
{
|
||||
target: provider
|
||||
target: propertyProvider
|
||||
onPropertiesChanged: control.updateCurrentIndex()
|
||||
}
|
||||
|
||||
function updateCurrentIndex() {
|
||||
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;
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -19,7 +19,10 @@ SettingItem
|
|||
model: Cura.ExtrudersModel
|
||||
{
|
||||
id: extruders_model
|
||||
onModelChanged: control.color = extruders_model.getItem(control.currentIndex).color
|
||||
}
|
||||
property string color: extruders_model.getItem(control.currentIndex).color
|
||||
|
||||
textRole: "name"
|
||||
|
||||
anchors.fill: parent
|
||||
|
@ -64,7 +67,7 @@ SettingItem
|
|||
anchors.leftMargin: UM.Theme.getSize("default_lining").width
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
color: extruders_model.getItem(control.currentIndex).colour
|
||||
color: control.color
|
||||
border.width: UM.Theme.getSize("default_lining").width
|
||||
border.color: !enabled ? UM.Theme.getColor("setting_control_disabled_border") : UM.Theme.getColor("setting_control_border")
|
||||
}
|
||||
|
@ -105,13 +108,14 @@ SettingItem
|
|||
onActivated:
|
||||
{
|
||||
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();
|
||||
|
||||
Connections
|
||||
{
|
||||
target: provider
|
||||
target: propertyProvider
|
||||
onPropertiesChanged: control.updateCurrentIndex();
|
||||
}
|
||||
|
||||
|
@ -119,9 +123,10 @@ SettingItem
|
|||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -138,7 +138,7 @@ Item {
|
|||
{
|
||||
id: linkedSettingIcon;
|
||||
|
||||
visible: base.settablePerExtruder != "True" && base.showLinkedSettingIcon
|
||||
visible: Cura.MachineManager.activeStackId != Cura.MachineManager.activeMachineId && base.settablePerExtruder != "True" && base.showLinkedSettingIcon
|
||||
|
||||
height: parent.height;
|
||||
width: height;
|
||||
|
|
|
@ -100,7 +100,7 @@ SettingItem
|
|||
|
||||
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
|
||||
{
|
||||
|
|
|
@ -125,7 +125,7 @@ ScrollView
|
|||
{
|
||||
id: provider
|
||||
|
||||
containerStackId: delegate.stackId
|
||||
containerStackId: Cura.MachineManager.activeMachineId
|
||||
key: model.key ? model.key : ""
|
||||
watchedProperties: [ "value", "enabled", "state", "validationState", "settable_per_extruder" ]
|
||||
storeIndex: 0
|
||||
|
|
|
@ -28,6 +28,7 @@ Rectangle
|
|||
|
||||
// Is there an output device for this printer?
|
||||
property bool printerConnected: Cura.MachineManager.printerOutputDevices.length != 0
|
||||
property bool printerAcceptsCommands: printerConnected && Cura.MachineManager.printerOutputDevices[0].acceptsCommands
|
||||
|
||||
color: UM.Theme.getColor("sidebar");
|
||||
UM.I18nCatalog { id: catalog; name:"cura"}
|
||||
|
@ -107,17 +108,28 @@ Rectangle
|
|||
onClicked: monitoringPrint = true
|
||||
iconSource: {
|
||||
if(!printerConnected)
|
||||
return UM.Theme.getIcon("tab_monitor")
|
||||
else if(Cura.MachineManager.printerOutputDevices[0].jobState == "printing" || Cura.MachineManager.printerOutputDevices[0].jobState == "pre_print")
|
||||
return UM.Theme.getIcon("tab_monitor_busy")
|
||||
else if(Cura.MachineManager.printerOutputDevices[0].jobState == "ready" || Cura.MachineManager.printerOutputDevices[0].jobState == "")
|
||||
return UM.Theme.getIcon("tab_monitor_connected")
|
||||
else if(Cura.MachineManager.printerOutputDevices[0].jobState == "paused")
|
||||
return UM.Theme.getIcon("tab_monitor_paused")
|
||||
else if (Cura.MachineManager.printerOutputDevices[0].jobState == "error")
|
||||
return UM.Theme.getIcon("tab_monitor_stopped")
|
||||
else
|
||||
return UM.Theme.getIcon("tab_monitor")
|
||||
return UM.Theme.getIcon("tab_monitor");
|
||||
else if(!printerAcceptsCommands)
|
||||
return UM.Theme.getIcon("tab_monitor_unknown");
|
||||
|
||||
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")
|
||||
case "paused":
|
||||
return UM.Theme.getIcon("tab_monitor_paused")
|
||||
case "error":
|
||||
return UM.Theme.getIcon("tab_monitor_stopped")
|
||||
case "offline":
|
||||
return UM.Theme.getIcon("tab_monitor_offline")
|
||||
default:
|
||||
return UM.Theme.getIcon("tab_monitor")
|
||||
}
|
||||
}
|
||||
checkable: true
|
||||
checked: monitoringPrint
|
||||
|
|
|
@ -138,7 +138,7 @@ Column
|
|||
anchors.leftMargin: (parent.height - height) / 2
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
color: model.colour
|
||||
color: model.color
|
||||
border.width: UM.Theme.getSize("default_lining").width
|
||||
border.color: UM.Theme.getColor("toggle_checked")
|
||||
}
|
||||
|
|
|
@ -216,7 +216,7 @@ Item
|
|||
anchors.verticalCenter: brimCheckBox.verticalCenter
|
||||
width: parent.width / 100 * 35 - 3 * UM.Theme.getSize("default_margin").width
|
||||
//: Bed adhesion label
|
||||
text: catalog.i18nc("@label:listbox", "Bed Adhesion:");
|
||||
text: catalog.i18nc("@label", "Helper Parts:");
|
||||
font: UM.Theme.getFont("default");
|
||||
color: UM.Theme.getColor("text");
|
||||
}
|
||||
|
@ -264,7 +264,7 @@ Item
|
|||
anchors.verticalCenter: supportCheckBox.verticalCenter
|
||||
width: parent.width / 100 * 35 - 3 * UM.Theme.getSize("default_margin").width
|
||||
//: Support label
|
||||
text: catalog.i18nc("@label:listbox", "Support:");
|
||||
text: "";
|
||||
font: UM.Theme.getFont("default");
|
||||
color: UM.Theme.getColor("text");
|
||||
}
|
||||
|
@ -327,7 +327,8 @@ Item
|
|||
supportEnabled.setPropertyValue("value", false);
|
||||
} else {
|
||||
supportEnabled.setPropertyValue("value", true);
|
||||
supportExtruderNr.setPropertyValue("value", index - 1);
|
||||
// Send the extruder nr as a string.
|
||||
supportExtruderNr.setPropertyValue("value", String(index - 1));
|
||||
}
|
||||
}
|
||||
MouseArea {
|
||||
|
@ -372,7 +373,7 @@ Item
|
|||
for(var extruderNumber = 0; extruderNumber < extruders.model.rowCount() ; extruderNumber++) {
|
||||
extruderModel.append({
|
||||
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
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
[shaders]
|
||||
vertex =
|
||||
uniform highp mat4 u_viewProjectionMatrix;
|
||||
uniform highp mat4 u_modelMatrix;
|
||||
uniform highp mat4 u_viewProjectionMatrix;
|
||||
uniform highp mat4 u_normalMatrix;
|
||||
|
||||
attribute highp vec4 a_vertex;
|
||||
|
@ -10,7 +10,6 @@ vertex =
|
|||
|
||||
varying highp vec3 v_vertex;
|
||||
varying highp vec3 v_normal;
|
||||
varying highp vec2 v_uvs;
|
||||
|
||||
void main()
|
||||
{
|
||||
|
@ -19,56 +18,47 @@ vertex =
|
|||
|
||||
v_vertex = world_space_vert.xyz;
|
||||
v_normal = (u_normalMatrix * normalize(a_normal)).xyz;
|
||||
|
||||
v_uvs = a_uvs;
|
||||
}
|
||||
|
||||
fragment =
|
||||
uniform mediump vec4 u_ambientColor;
|
||||
uniform mediump vec4 u_diffuseColor;
|
||||
uniform highp vec3 u_lightPosition;
|
||||
uniform highp vec3 u_viewPosition;
|
||||
|
||||
uniform mediump float u_opacity;
|
||||
uniform sampler2D u_texture;
|
||||
|
||||
varying highp vec3 v_vertex;
|
||||
varying highp vec3 v_normal;
|
||||
varying highp vec2 v_uvs;
|
||||
|
||||
void main()
|
||||
{
|
||||
// Copied from platform.shader, removed texture
|
||||
mediump vec4 final_color = vec4(0.0);
|
||||
mediump vec4 finalColor = vec4(0.0);
|
||||
|
||||
/* Ambient Component */
|
||||
final_color += u_ambientColor;
|
||||
finalColor += u_ambientColor;
|
||||
|
||||
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 */
|
||||
highp float n_dot_l = clamp(dot(normal, light_dir), 0.0, 1.0);
|
||||
final_color += (n_dot_l * u_diffuseColor);
|
||||
highp float NdotL = clamp(abs(dot(normal, lightDir)), 0.0, 1.0);
|
||||
finalColor += (NdotL * u_diffuseColor);
|
||||
|
||||
final_color.a = u_opacity;
|
||||
|
||||
gl_FragColor = final_color;
|
||||
gl_FragColor = finalColor;
|
||||
gl_FragColor.a = u_opacity;
|
||||
}
|
||||
|
||||
[defaults]
|
||||
u_ambientColor = [0.3, 0.3, 0.3, 1.0]
|
||||
u_diffuseColor = [1.0, 1.0, 1.0, 1.0]
|
||||
u_ambientColor = [0.1, 0.1, 0.1, 1.0]
|
||||
u_diffuseColor = [0.4, 0.4, 0.4, 1.0]
|
||||
u_opacity = 0.5
|
||||
u_texture = 0
|
||||
|
||||
[bindings]
|
||||
u_viewProjectionMatrix = view_projection_matrix
|
||||
u_modelMatrix = model_matrix
|
||||
u_viewProjectionMatrix = view_projection_matrix
|
||||
u_normalMatrix = normal_matrix
|
||||
u_lightPosition = light_0_position
|
||||
u_viewPosition = camera_position
|
||||
|
||||
[attributes]
|
||||
a_vertex = vertex
|
||||
a_normal = normal
|
||||
a_uvs = uv0
|
||||
|
|
10260
resources/themes/cura/icons/minus.svg
Normal file
10260
resources/themes/cura/icons/minus.svg
Normal file
File diff suppressed because it is too large
Load diff
After Width: | Height: | Size: 760 KiB |
|
@ -1,13 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 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">
|
||||
<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"
|
||||
width="512px" height="512px" viewBox="0 0 512 512" enable-background="new 0 0 512 512" 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
|
||||
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 version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
viewBox="0 0 30 30" xml:space="preserve">
|
||||
<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"/>
|
||||
</svg>
|
||||
|
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 960 B |
97
resources/themes/cura/icons/tab_monitor_unknown.svg
Normal file
97
resources/themes/cura/icons/tab_monitor_unknown.svg
Normal 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 |
Loading…
Add table
Add a link
Reference in a new issue