mirror of
https://github.com/Ultimaker/Cura.git
synced 2025-07-12 09:17:50 -06:00
Merge branch '3.1'
This commit is contained in:
commit
7d1db1b165
26 changed files with 565 additions and 64 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -44,6 +44,7 @@ plugins/cura-god-mode-plugin
|
||||||
plugins/cura-big-flame-graph
|
plugins/cura-big-flame-graph
|
||||||
plugins/cura-siemensnx-plugin
|
plugins/cura-siemensnx-plugin
|
||||||
plugins/CuraVariSlicePlugin
|
plugins/CuraVariSlicePlugin
|
||||||
|
plugins/CuraLiveScriptingPlugin
|
||||||
|
|
||||||
#Build stuff
|
#Build stuff
|
||||||
CMakeCache.txt
|
CMakeCache.txt
|
||||||
|
|
|
@ -20,6 +20,8 @@ For crashes and similar issues, please attach the following information:
|
||||||
|
|
||||||
If the Cura user interface still starts, you can also reach this directory from the application menu in Help -> Show settings folder
|
If the Cura user interface still starts, you can also reach this directory from the application menu in Help -> Show settings folder
|
||||||
|
|
||||||
|
For additional support, you could also ask in the #cura channel on FreeNode IRC. For help with development, there is also the #cura-dev channel.
|
||||||
|
|
||||||
Dependencies
|
Dependencies
|
||||||
------------
|
------------
|
||||||
|
|
||||||
|
|
|
@ -70,6 +70,7 @@ class PrintInformation(QObject):
|
||||||
|
|
||||||
Application.getInstance().globalContainerStackChanged.connect(self._updateJobName)
|
Application.getInstance().globalContainerStackChanged.connect(self._updateJobName)
|
||||||
Application.getInstance().fileLoaded.connect(self.setBaseName)
|
Application.getInstance().fileLoaded.connect(self.setBaseName)
|
||||||
|
Application.getInstance().workspaceLoaded.connect(self.setProjectName)
|
||||||
Preferences.getInstance().preferenceChanged.connect(self._onPreferencesChanged)
|
Preferences.getInstance().preferenceChanged.connect(self._onPreferencesChanged)
|
||||||
|
|
||||||
self._active_material_container = None
|
self._active_material_container = None
|
||||||
|
@ -283,7 +284,11 @@ class PrintInformation(QObject):
|
||||||
return self._base_name
|
return self._base_name
|
||||||
|
|
||||||
@pyqtSlot(str)
|
@pyqtSlot(str)
|
||||||
def setBaseName(self, base_name):
|
def setProjectName(self, name):
|
||||||
|
self.setBaseName(name, is_project_file = True)
|
||||||
|
|
||||||
|
@pyqtSlot(str)
|
||||||
|
def setBaseName(self, base_name, is_project_file = False):
|
||||||
# Ensure that we don't use entire path but only filename
|
# Ensure that we don't use entire path but only filename
|
||||||
name = os.path.basename(base_name)
|
name = os.path.basename(base_name)
|
||||||
|
|
||||||
|
@ -291,15 +296,17 @@ class PrintInformation(QObject):
|
||||||
# extension. This cuts the extension off if necessary.
|
# extension. This cuts the extension off if necessary.
|
||||||
name = os.path.splitext(name)[0]
|
name = os.path.splitext(name)[0]
|
||||||
|
|
||||||
|
# if this is a profile file, always update the job name
|
||||||
# name is "" when I first had some meshes and afterwards I deleted them so the naming should start again
|
# name is "" when I first had some meshes and afterwards I deleted them so the naming should start again
|
||||||
is_empty = name == ""
|
is_empty = name == ""
|
||||||
if is_empty or (self._base_name == "" and self._base_name != name):
|
if is_project_file or (is_empty or (self._base_name == "" and self._base_name != name)):
|
||||||
# remove ".curaproject" suffix from (imported) the file name
|
# remove ".curaproject" suffix from (imported) the file name
|
||||||
if name.endswith(".curaproject"):
|
if name.endswith(".curaproject"):
|
||||||
name = name[:name.rfind(".curaproject")]
|
name = name[:name.rfind(".curaproject")]
|
||||||
self._base_name = name
|
self._base_name = name
|
||||||
self._updateJobName()
|
self._updateJobName()
|
||||||
|
|
||||||
|
|
||||||
## Created an acronymn-like abbreviated machine name from the currently active machine name
|
## Created an acronymn-like abbreviated machine name from the currently active machine name
|
||||||
# Called each time the global stack is switched
|
# Called each time the global stack is switched
|
||||||
def _setAbbreviatedMachineName(self):
|
def _setAbbreviatedMachineName(self):
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
import os
|
import os
|
||||||
import os.path
|
import os.path
|
||||||
import re
|
import re
|
||||||
|
import configparser
|
||||||
|
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
|
@ -19,6 +20,7 @@ from UM.Message import Message
|
||||||
from UM.Platform import Platform
|
from UM.Platform import Platform
|
||||||
from UM.PluginRegistry import PluginRegistry # For getting the possible profile writers to write with.
|
from UM.PluginRegistry import PluginRegistry # For getting the possible profile writers to write with.
|
||||||
from UM.Util import parseBool
|
from UM.Util import parseBool
|
||||||
|
from UM.Resources import Resources
|
||||||
|
|
||||||
from . import ExtruderStack
|
from . import ExtruderStack
|
||||||
from . import GlobalStack
|
from . import GlobalStack
|
||||||
|
@ -409,7 +411,7 @@ class CuraContainerRegistry(ContainerRegistry):
|
||||||
extruder_stack = None
|
extruder_stack = None
|
||||||
|
|
||||||
# if extruders are defined in the machine definition use those instead
|
# if extruders are defined in the machine definition use those instead
|
||||||
if machine.extruders and len(machine.extruders) > 0:
|
if machine.extruders and "0" in machine.extruders:
|
||||||
new_extruder_id = machine.extruders["0"].getId()
|
new_extruder_id = machine.extruders["0"].getId()
|
||||||
extruder_stack = machine.extruders["0"]
|
extruder_stack = machine.extruders["0"]
|
||||||
|
|
||||||
|
@ -444,20 +446,82 @@ class CuraContainerRegistry(ContainerRegistry):
|
||||||
self.addContainer(user_container)
|
self.addContainer(user_container)
|
||||||
|
|
||||||
variant_id = "default"
|
variant_id = "default"
|
||||||
if machine.variant.getId() != "empty_variant":
|
if machine.variant.getId() not in ("empty", "empty_variant"):
|
||||||
variant_id = machine.variant.getId()
|
variant_id = machine.variant.getId()
|
||||||
|
else:
|
||||||
|
variant_id = "empty_variant"
|
||||||
extruder_stack.setVariantById(variant_id)
|
extruder_stack.setVariantById(variant_id)
|
||||||
extruder_stack.setMaterialById("default")
|
|
||||||
extruder_stack.setQualityById("default")
|
material_id = "default"
|
||||||
quality_changes_id = "default"
|
if machine.material.getId() not in ("empty", "empty_material"):
|
||||||
if machine.qualityChanges.getId() != "empty_quality_changes":
|
material_id = machine.material.getId()
|
||||||
|
else:
|
||||||
|
material_id = "empty_material"
|
||||||
|
extruder_stack.setMaterialById(material_id)
|
||||||
|
|
||||||
|
quality_id = "default"
|
||||||
|
if machine.quality.getId() not in ("empty", "empty_quality"):
|
||||||
|
quality_id = machine.quality.getId()
|
||||||
|
else:
|
||||||
|
quality_id = "empty_quality"
|
||||||
|
extruder_stack.setQualityById(quality_id)
|
||||||
|
|
||||||
|
if machine.qualityChanges.getId() not in ("empty", "empty_quality_changes"):
|
||||||
extruder_quality_changes_container = self.findInstanceContainers(name = machine.qualityChanges.getName(), extruder = extruder_id)
|
extruder_quality_changes_container = self.findInstanceContainers(name = machine.qualityChanges.getName(), extruder = extruder_id)
|
||||||
if extruder_quality_changes_container:
|
if extruder_quality_changes_container:
|
||||||
quality_changes_id = extruder_quality_changes_container[0].getId()
|
extruder_quality_changes_container = extruder_quality_changes_container[0]
|
||||||
extruder_stack.setQualityChangesById(quality_changes_id)
|
quality_changes_id = extruder_quality_changes_container.getId()
|
||||||
|
extruder_stack.setQualityChangesById(quality_changes_id)
|
||||||
|
else:
|
||||||
|
# Some extruder quality_changes containers can be created at runtime as files in the qualities
|
||||||
|
# folder. Those files won't be loaded in the registry immediately. So we also need to search
|
||||||
|
# the folder to see if the quality_changes exists.
|
||||||
|
extruder_quality_changes_container = self._findQualityChangesContainerInCuraFolder(machine.qualityChanges.getName())
|
||||||
|
if extruder_quality_changes_container:
|
||||||
|
quality_changes_id = extruder_quality_changes_container.getId()
|
||||||
|
extruder_stack.setQualityChangesById(quality_changes_id)
|
||||||
|
|
||||||
|
if not extruder_quality_changes_container:
|
||||||
|
Logger.log("w", "Could not find quality_changes named [%s] for extruder [%s]",
|
||||||
|
machine.qualityChanges.getName(), extruder_stack.getId())
|
||||||
|
|
||||||
self.addContainer(extruder_stack)
|
self.addContainer(extruder_stack)
|
||||||
|
|
||||||
|
return extruder_stack
|
||||||
|
|
||||||
|
def _findQualityChangesContainerInCuraFolder(self, name):
|
||||||
|
quality_changes_dir = Resources.getPath(CuraApplication.ResourceTypes.QualityInstanceContainer)
|
||||||
|
|
||||||
|
instance_container = None
|
||||||
|
|
||||||
|
for item in os.listdir(quality_changes_dir):
|
||||||
|
file_path = os.path.join(quality_changes_dir, item)
|
||||||
|
if not os.path.isfile(file_path):
|
||||||
|
continue
|
||||||
|
|
||||||
|
parser = configparser.ConfigParser()
|
||||||
|
try:
|
||||||
|
parser.read([file_path])
|
||||||
|
except:
|
||||||
|
# skip, it is not a valid stack file
|
||||||
|
continue
|
||||||
|
|
||||||
|
if not parser.has_option("general", "name"):
|
||||||
|
continue
|
||||||
|
|
||||||
|
if parser["general"]["name"] == name:
|
||||||
|
# load the container
|
||||||
|
container_id = os.path.basename(file_path).replace(".inst.cfg", "")
|
||||||
|
|
||||||
|
instance_container = InstanceContainer(container_id)
|
||||||
|
with open(file_path, "r") as f:
|
||||||
|
serialized = f.read()
|
||||||
|
instance_container.deserialize(serialized, file_path)
|
||||||
|
self.addContainer(instance_container)
|
||||||
|
break
|
||||||
|
|
||||||
|
return instance_container
|
||||||
|
|
||||||
# Fix the extruders that were upgraded to ExtruderStack instances during addContainer.
|
# Fix the extruders that were upgraded to ExtruderStack instances during addContainer.
|
||||||
# The stacks are now responsible for setting the next stack on deserialize. However,
|
# The stacks are now responsible for setting the next stack on deserialize. However,
|
||||||
# due to problems with loading order, some stacks may not have the proper next stack
|
# due to problems with loading order, some stacks may not have the proper next stack
|
||||||
|
|
|
@ -41,9 +41,20 @@ class CuraContainerStack(ContainerStack):
|
||||||
def __init__(self, container_id: str, *args, **kwargs):
|
def __init__(self, container_id: str, *args, **kwargs):
|
||||||
super().__init__(container_id, *args, **kwargs)
|
super().__init__(container_id, *args, **kwargs)
|
||||||
|
|
||||||
self._empty_instance_container = ContainerRegistry.getInstance().getEmptyInstanceContainer()
|
self._container_registry = ContainerRegistry.getInstance()
|
||||||
|
|
||||||
|
self._empty_instance_container = self._container_registry.getEmptyInstanceContainer()
|
||||||
|
|
||||||
|
self._empty_quality_changes = self._container_registry.findInstanceContainers(id = "empty_quality_changes")[0]
|
||||||
|
self._empty_quality = self._container_registry.findInstanceContainers(id = "empty_quality")[0]
|
||||||
|
self._empty_material = self._container_registry.findInstanceContainers(id = "empty_material")[0]
|
||||||
|
self._empty_variant = self._container_registry.findInstanceContainers(id = "empty_variant")[0]
|
||||||
|
|
||||||
self._containers = [self._empty_instance_container for i in range(len(_ContainerIndexes.IndexTypeMap))]
|
self._containers = [self._empty_instance_container for i in range(len(_ContainerIndexes.IndexTypeMap))]
|
||||||
|
self._containers[_ContainerIndexes.QualityChanges] = self._empty_quality_changes
|
||||||
|
self._containers[_ContainerIndexes.Quality] = self._empty_quality
|
||||||
|
self._containers[_ContainerIndexes.Material] = self._empty_material
|
||||||
|
self._containers[_ContainerIndexes.Variant] = self._empty_variant
|
||||||
|
|
||||||
self.containersChanged.connect(self._onContainersChanged)
|
self.containersChanged.connect(self._onContainersChanged)
|
||||||
|
|
||||||
|
@ -110,7 +121,7 @@ class CuraContainerStack(ContainerStack):
|
||||||
#
|
#
|
||||||
# \throws Exceptions.InvalidContainerError Raised when no container could be found with the specified ID.
|
# \throws Exceptions.InvalidContainerError Raised when no container could be found with the specified ID.
|
||||||
def setQualityById(self, new_quality_id: str) -> None:
|
def setQualityById(self, new_quality_id: str) -> None:
|
||||||
quality = self._empty_instance_container
|
quality = self._empty_quality
|
||||||
if new_quality_id == "default":
|
if new_quality_id == "default":
|
||||||
new_quality = self.findDefaultQuality()
|
new_quality = self.findDefaultQuality()
|
||||||
if new_quality:
|
if new_quality:
|
||||||
|
@ -148,7 +159,7 @@ class CuraContainerStack(ContainerStack):
|
||||||
#
|
#
|
||||||
# \throws Exceptions.InvalidContainerError Raised when no container could be found with the specified ID.
|
# \throws Exceptions.InvalidContainerError Raised when no container could be found with the specified ID.
|
||||||
def setMaterialById(self, new_material_id: str) -> None:
|
def setMaterialById(self, new_material_id: str) -> None:
|
||||||
material = self._empty_instance_container
|
material = self._empty_material
|
||||||
if new_material_id == "default":
|
if new_material_id == "default":
|
||||||
new_material = self.findDefaultMaterial()
|
new_material = self.findDefaultMaterial()
|
||||||
if new_material:
|
if new_material:
|
||||||
|
@ -186,7 +197,7 @@ class CuraContainerStack(ContainerStack):
|
||||||
#
|
#
|
||||||
# \throws Exceptions.InvalidContainerError Raised when no container could be found with the specified ID.
|
# \throws Exceptions.InvalidContainerError Raised when no container could be found with the specified ID.
|
||||||
def setVariantById(self, new_variant_id: str) -> None:
|
def setVariantById(self, new_variant_id: str) -> None:
|
||||||
variant = self._empty_instance_container
|
variant = self._empty_variant
|
||||||
if new_variant_id == "default":
|
if new_variant_id == "default":
|
||||||
new_variant = self.findDefaultVariant()
|
new_variant = self.findDefaultVariant()
|
||||||
if new_variant:
|
if new_variant:
|
||||||
|
@ -348,8 +359,8 @@ class CuraContainerStack(ContainerStack):
|
||||||
#
|
#
|
||||||
# \throws InvalidContainerStackError Raised when no definition can be found for the stack.
|
# \throws InvalidContainerStackError Raised when no definition can be found for the stack.
|
||||||
@override(ContainerStack)
|
@override(ContainerStack)
|
||||||
def deserialize(self, contents: str) -> None:
|
def deserialize(self, contents: str, file_name: Optional[str] = None) -> None:
|
||||||
super().deserialize(contents)
|
super().deserialize(contents, file_name)
|
||||||
|
|
||||||
new_containers = self._containers.copy()
|
new_containers = self._containers.copy()
|
||||||
while len(new_containers) < len(_ContainerIndexes.IndexTypeMap):
|
while len(new_containers) < len(_ContainerIndexes.IndexTypeMap):
|
||||||
|
@ -456,7 +467,7 @@ class CuraContainerStack(ContainerStack):
|
||||||
else:
|
else:
|
||||||
search_criteria["definition"] = "fdmprinter"
|
search_criteria["definition"] = "fdmprinter"
|
||||||
|
|
||||||
if self.material != self._empty_instance_container:
|
if self.material != self._empty_material:
|
||||||
search_criteria["name"] = self.material.name
|
search_criteria["name"] = self.material.name
|
||||||
else:
|
else:
|
||||||
preferred_material = definition.getMetaDataEntry("preferred_material")
|
preferred_material = definition.getMetaDataEntry("preferred_material")
|
||||||
|
@ -503,7 +514,7 @@ class CuraContainerStack(ContainerStack):
|
||||||
else:
|
else:
|
||||||
search_criteria["definition"] = "fdmprinter"
|
search_criteria["definition"] = "fdmprinter"
|
||||||
|
|
||||||
if self.quality != self._empty_instance_container:
|
if self.quality != self._empty_quality:
|
||||||
search_criteria["name"] = self.quality.name
|
search_criteria["name"] = self.quality.name
|
||||||
else:
|
else:
|
||||||
preferred_quality = definition.getMetaDataEntry("preferred_quality")
|
preferred_quality = definition.getMetaDataEntry("preferred_quality")
|
||||||
|
|
|
@ -92,8 +92,8 @@ class ExtruderStack(CuraContainerStack):
|
||||||
return self.getNextStack()._getMachineDefinition()
|
return self.getNextStack()._getMachineDefinition()
|
||||||
|
|
||||||
@override(CuraContainerStack)
|
@override(CuraContainerStack)
|
||||||
def deserialize(self, contents: str) -> None:
|
def deserialize(self, contents: str, file_name: Optional[str] = None) -> None:
|
||||||
super().deserialize(contents)
|
super().deserialize(contents, file_name)
|
||||||
stacks = ContainerRegistry.getInstance().findContainerStacks(id=self.getMetaDataEntry("machine", ""))
|
stacks = ContainerRegistry.getInstance().findContainerStacks(id=self.getMetaDataEntry("machine", ""))
|
||||||
if stacks:
|
if stacks:
|
||||||
self.setNextStack(stacks[0])
|
self.setNextStack(stacks[0])
|
||||||
|
|
|
@ -621,9 +621,16 @@ class MachineManager(QObject):
|
||||||
def activeQualityId(self) -> str:
|
def activeQualityId(self) -> str:
|
||||||
if self._active_container_stack:
|
if self._active_container_stack:
|
||||||
quality = self._active_container_stack.quality
|
quality = self._active_container_stack.quality
|
||||||
|
if isinstance(quality, type(self._empty_quality_container)):
|
||||||
|
return ""
|
||||||
quality_changes = self._active_container_stack.qualityChanges
|
quality_changes = self._active_container_stack.qualityChanges
|
||||||
if quality and quality_changes and isinstance(quality_changes, type(self._empty_quality_changes_container)) and not isinstance(quality, type(self._empty_quality_container)):
|
if quality and quality_changes:
|
||||||
return quality.getId()
|
if isinstance(quality_changes, type(self._empty_quality_changes_container)):
|
||||||
|
# It's a built-in profile
|
||||||
|
return quality.getId()
|
||||||
|
else:
|
||||||
|
# Custom profile
|
||||||
|
return quality_changes.getId()
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
@pyqtProperty(str, notify=activeQualityChanged)
|
@pyqtProperty(str, notify=activeQualityChanged)
|
||||||
|
|
|
@ -18,4 +18,8 @@ class MaterialsModel(InstanceContainersModel):
|
||||||
# \param container The container whose metadata was changed.
|
# \param container The container whose metadata was changed.
|
||||||
def _onContainerMetaDataChanged(self, container):
|
def _onContainerMetaDataChanged(self, container):
|
||||||
if container.getMetaDataEntry("type") == "material": #Only need to update if a material was changed.
|
if container.getMetaDataEntry("type") == "material": #Only need to update if a material was changed.
|
||||||
self._update()
|
self._update()
|
||||||
|
|
||||||
|
def _onContainerChanged(self, container):
|
||||||
|
if container.getMetaDataEntry("type", "") == "material":
|
||||||
|
super()._onContainerChanged(container)
|
||||||
|
|
|
@ -152,7 +152,8 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
|
||||||
|
|
||||||
if not definitions:
|
if not definitions:
|
||||||
definition_container = DefinitionContainer(container_id)
|
definition_container = DefinitionContainer(container_id)
|
||||||
definition_container.deserialize(archive.open(each_definition_container_file).read().decode("utf-8"))
|
definition_container.deserialize(archive.open(each_definition_container_file).read().decode("utf-8"),
|
||||||
|
file_name = each_definition_container_file)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
definition_container = definitions[0]
|
definition_container = definitions[0]
|
||||||
|
@ -208,7 +209,8 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
|
||||||
instance_container = InstanceContainer(container_id)
|
instance_container = InstanceContainer(container_id)
|
||||||
|
|
||||||
# Deserialize InstanceContainer by converting read data from bytes to string
|
# Deserialize InstanceContainer by converting read data from bytes to string
|
||||||
instance_container.deserialize(archive.open(each_instance_container_file).read().decode("utf-8"))
|
instance_container.deserialize(archive.open(each_instance_container_file).read().decode("utf-8"),
|
||||||
|
file_name = each_instance_container_file)
|
||||||
instance_container_list.append(instance_container)
|
instance_container_list.append(instance_container)
|
||||||
|
|
||||||
container_type = instance_container.getMetaDataEntry("type")
|
container_type = instance_container.getMetaDataEntry("type")
|
||||||
|
@ -378,7 +380,7 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
|
||||||
return WorkspaceReader.PreReadResult.accepted
|
return WorkspaceReader.PreReadResult.accepted
|
||||||
|
|
||||||
## Overrides an ExtruderStack in the given GlobalStack and returns the new ExtruderStack.
|
## Overrides an ExtruderStack in the given GlobalStack and returns the new ExtruderStack.
|
||||||
def _overrideExtruderStack(self, global_stack, extruder_file_content):
|
def _overrideExtruderStack(self, global_stack, extruder_file_content, extruder_stack_file):
|
||||||
# Get extruder position first
|
# Get extruder position first
|
||||||
extruder_config = configparser.ConfigParser()
|
extruder_config = configparser.ConfigParser()
|
||||||
extruder_config.read_string(extruder_file_content)
|
extruder_config.read_string(extruder_file_content)
|
||||||
|
@ -394,7 +396,7 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# Override the given extruder stack
|
# Override the given extruder stack
|
||||||
extruder_stack.deserialize(extruder_file_content)
|
extruder_stack.deserialize(extruder_file_content, file_name = extruder_stack_file)
|
||||||
|
|
||||||
# return the new ExtruderStack
|
# return the new ExtruderStack
|
||||||
return extruder_stack
|
return extruder_stack
|
||||||
|
@ -484,7 +486,8 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
|
||||||
definitions = self._container_registry.findDefinitionContainers(id = container_id)
|
definitions = self._container_registry.findDefinitionContainers(id = container_id)
|
||||||
if not definitions:
|
if not definitions:
|
||||||
definition_container = DefinitionContainer(container_id)
|
definition_container = DefinitionContainer(container_id)
|
||||||
definition_container.deserialize(archive.open(definition_container_file).read().decode("utf-8"))
|
definition_container.deserialize(archive.open(definition_container_file).read().decode("utf-8"),
|
||||||
|
file_name = definition_container_file)
|
||||||
self._container_registry.addContainer(definition_container)
|
self._container_registry.addContainer(definition_container)
|
||||||
Job.yieldThread()
|
Job.yieldThread()
|
||||||
|
|
||||||
|
@ -502,18 +505,21 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
|
||||||
|
|
||||||
if not materials:
|
if not materials:
|
||||||
material_container = xml_material_profile(container_id)
|
material_container = xml_material_profile(container_id)
|
||||||
material_container.deserialize(archive.open(material_container_file).read().decode("utf-8"))
|
material_container.deserialize(archive.open(material_container_file).read().decode("utf-8"),
|
||||||
|
file_name = material_container_file)
|
||||||
containers_to_add.append(material_container)
|
containers_to_add.append(material_container)
|
||||||
else:
|
else:
|
||||||
material_container = materials[0]
|
material_container = materials[0]
|
||||||
if not material_container.isReadOnly(): # Only create new materials if they are not read only.
|
if not material_container.isReadOnly(): # Only create new materials if they are not read only.
|
||||||
if self._resolve_strategies["material"] == "override":
|
if self._resolve_strategies["material"] == "override":
|
||||||
material_container.deserialize(archive.open(material_container_file).read().decode("utf-8"))
|
material_container.deserialize(archive.open(material_container_file).read().decode("utf-8"),
|
||||||
|
file_name = material_container_file)
|
||||||
elif self._resolve_strategies["material"] == "new":
|
elif self._resolve_strategies["material"] == "new":
|
||||||
# Note that we *must* deserialize it with a new ID, as multiple containers will be
|
# Note that we *must* deserialize it with a new ID, as multiple containers will be
|
||||||
# auto created & added.
|
# auto created & added.
|
||||||
material_container = xml_material_profile(self.getNewId(container_id))
|
material_container = xml_material_profile(self.getNewId(container_id))
|
||||||
material_container.deserialize(archive.open(material_container_file).read().decode("utf-8"))
|
material_container.deserialize(archive.open(material_container_file).read().decode("utf-8"),
|
||||||
|
file_name = material_container_file)
|
||||||
containers_to_add.append(material_container)
|
containers_to_add.append(material_container)
|
||||||
|
|
||||||
material_containers.append(material_container)
|
material_containers.append(material_container)
|
||||||
|
@ -540,7 +546,7 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
|
||||||
instance_container = InstanceContainer(container_id)
|
instance_container = InstanceContainer(container_id)
|
||||||
|
|
||||||
# Deserialize InstanceContainer by converting read data from bytes to string
|
# Deserialize InstanceContainer by converting read data from bytes to string
|
||||||
instance_container.deserialize(serialized)
|
instance_container.deserialize(serialized, file_name = instance_container_file)
|
||||||
container_type = instance_container.getMetaDataEntry("type")
|
container_type = instance_container.getMetaDataEntry("type")
|
||||||
Job.yieldThread()
|
Job.yieldThread()
|
||||||
|
|
||||||
|
@ -562,7 +568,8 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
|
||||||
else:
|
else:
|
||||||
if self._resolve_strategies["machine"] == "override" or self._resolve_strategies["machine"] is None:
|
if self._resolve_strategies["machine"] == "override" or self._resolve_strategies["machine"] is None:
|
||||||
instance_container = user_containers[0]
|
instance_container = user_containers[0]
|
||||||
instance_container.deserialize(archive.open(instance_container_file).read().decode("utf-8"))
|
instance_container.deserialize(archive.open(instance_container_file).read().decode("utf-8"),
|
||||||
|
file_name = instance_container_file)
|
||||||
instance_container.setDirty(True)
|
instance_container.setDirty(True)
|
||||||
elif self._resolve_strategies["machine"] == "new":
|
elif self._resolve_strategies["machine"] == "new":
|
||||||
# The machine is going to get a spiffy new name, so ensure that the id's of user settings match.
|
# The machine is going to get a spiffy new name, so ensure that the id's of user settings match.
|
||||||
|
@ -595,7 +602,8 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
|
||||||
# selected strategy.
|
# selected strategy.
|
||||||
if self._resolve_strategies[container_type] == "override":
|
if self._resolve_strategies[container_type] == "override":
|
||||||
instance_container = changes_containers[0]
|
instance_container = changes_containers[0]
|
||||||
instance_container.deserialize(archive.open(instance_container_file).read().decode("utf-8"))
|
instance_container.deserialize(archive.open(instance_container_file).read().decode("utf-8"),
|
||||||
|
file_name = instance_container_file)
|
||||||
instance_container.setDirty(True)
|
instance_container.setDirty(True)
|
||||||
|
|
||||||
elif self._resolve_strategies[container_type] == "new":
|
elif self._resolve_strategies[container_type] == "new":
|
||||||
|
@ -656,7 +664,8 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
|
||||||
# There is a machine, check if it has authentication data. If so, keep that data.
|
# There is a machine, check if it has authentication data. If so, keep that data.
|
||||||
network_authentication_id = container_stacks[0].getMetaDataEntry("network_authentication_id")
|
network_authentication_id = container_stacks[0].getMetaDataEntry("network_authentication_id")
|
||||||
network_authentication_key = container_stacks[0].getMetaDataEntry("network_authentication_key")
|
network_authentication_key = container_stacks[0].getMetaDataEntry("network_authentication_key")
|
||||||
container_stacks[0].deserialize(archive.open(global_stack_file).read().decode("utf-8"))
|
container_stacks[0].deserialize(archive.open(global_stack_file).read().decode("utf-8"),
|
||||||
|
file_name = global_stack_file)
|
||||||
if network_authentication_id:
|
if network_authentication_id:
|
||||||
container_stacks[0].addMetaDataEntry("network_authentication_id", network_authentication_id)
|
container_stacks[0].addMetaDataEntry("network_authentication_id", network_authentication_id)
|
||||||
if network_authentication_key:
|
if network_authentication_key:
|
||||||
|
@ -666,7 +675,8 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
|
||||||
# create a new global stack
|
# create a new global stack
|
||||||
stack = GlobalStack(global_stack_id_new)
|
stack = GlobalStack(global_stack_id_new)
|
||||||
# Deserialize stack by converting read data from bytes to string
|
# Deserialize stack by converting read data from bytes to string
|
||||||
stack.deserialize(archive.open(global_stack_file).read().decode("utf-8"))
|
stack.deserialize(archive.open(global_stack_file).read().decode("utf-8"),
|
||||||
|
file_name = global_stack_file)
|
||||||
|
|
||||||
# Ensure a unique ID and name
|
# Ensure a unique ID and name
|
||||||
stack._id = global_stack_id_new
|
stack._id = global_stack_id_new
|
||||||
|
@ -706,7 +716,7 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
|
||||||
if self._resolve_strategies["machine"] == "override":
|
if self._resolve_strategies["machine"] == "override":
|
||||||
if global_stack.getProperty("machine_extruder_count", "value") > 1:
|
if global_stack.getProperty("machine_extruder_count", "value") > 1:
|
||||||
# deserialize new extruder stack over the current ones (if any)
|
# deserialize new extruder stack over the current ones (if any)
|
||||||
stack = self._overrideExtruderStack(global_stack, extruder_file_content)
|
stack = self._overrideExtruderStack(global_stack, extruder_file_content, extruder_stack_file)
|
||||||
if stack is None:
|
if stack is None:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
@ -726,7 +736,7 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
|
||||||
extruder_config.write(tmp_string_io)
|
extruder_config.write(tmp_string_io)
|
||||||
extruder_file_content = tmp_string_io.getvalue()
|
extruder_file_content = tmp_string_io.getvalue()
|
||||||
|
|
||||||
stack.deserialize(extruder_file_content)
|
stack.deserialize(extruder_file_content, file_name = extruder_stack_file)
|
||||||
|
|
||||||
# Ensure a unique ID and name
|
# Ensure a unique ID and name
|
||||||
stack._id = new_id
|
stack._id = new_id
|
||||||
|
@ -741,12 +751,15 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
|
||||||
if stack.definitionChanges == self._container_registry.getEmptyInstanceContainer():
|
if stack.definitionChanges == self._container_registry.getEmptyInstanceContainer():
|
||||||
stack.setDefinitionChanges(CuraStackBuilder.createDefinitionChangesContainer(stack, stack.getId() + "_settings"))
|
stack.setDefinitionChanges(CuraStackBuilder.createDefinitionChangesContainer(stack, stack.getId() + "_settings"))
|
||||||
|
|
||||||
extruder_stacks.append(stack)
|
if stack.getMetaDataEntry("type") == "extruder_train":
|
||||||
|
extruder_stacks.append(stack)
|
||||||
|
|
||||||
# If not extruder stacks were saved in the project file (pre 3.1) create one manually
|
# If not extruder stacks were saved in the project file (pre 3.1) create one manually
|
||||||
# We re-use the container registry's addExtruderStackForSingleExtrusionMachine method for this
|
# We re-use the container registry's addExtruderStackForSingleExtrusionMachine method for this
|
||||||
if not extruder_stacks:
|
if not extruder_stacks:
|
||||||
self._container_registry.addExtruderStackForSingleExtrusionMachine(global_stack, "fdmextruder")
|
stack = self._container_registry.addExtruderStackForSingleExtrusionMachine(global_stack, "fdmextruder")
|
||||||
|
if stack:
|
||||||
|
extruder_stacks.append(stack)
|
||||||
|
|
||||||
except:
|
except:
|
||||||
Logger.logException("w", "We failed to serialize the stack. Trying to clean up.")
|
Logger.logException("w", "We failed to serialize the stack. Trying to clean up.")
|
||||||
|
@ -780,6 +793,46 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
|
||||||
for stack in [global_stack] + extruder_stacks:
|
for stack in [global_stack] + extruder_stacks:
|
||||||
stack.replaceContainer(_ContainerIndexes.Quality, empty_quality_container)
|
stack.replaceContainer(_ContainerIndexes.Quality, empty_quality_container)
|
||||||
|
|
||||||
|
# Fix quality:
|
||||||
|
# The quality specified in an old project file can be wrong, for example, for UM2, it should be "um2_normal"
|
||||||
|
# but instead it was "normal". This should be fixed by setting it to the correct quality.
|
||||||
|
# Note that this only seems to happen on single-extrusion machines on the global stack, so we only apply the
|
||||||
|
# fix for that
|
||||||
|
quality = global_stack.quality
|
||||||
|
if quality.getId() not in ("empty", "empty_quality"):
|
||||||
|
quality_type = quality.getMetaDataEntry("quality_type")
|
||||||
|
quality_containers = self._container_registry.findInstanceContainers(definition = global_stack.definition.getId(),
|
||||||
|
type = "quality",
|
||||||
|
quality_type = quality_type)
|
||||||
|
quality_containers = [q for q in quality_containers if q.getMetaDataEntry("material", "") == ""]
|
||||||
|
if quality_containers:
|
||||||
|
global_stack.quality = quality_containers[0]
|
||||||
|
else:
|
||||||
|
# look for "fdmprinter" qualities if the machine-specific qualities cannot be found
|
||||||
|
quality_containers = self._container_registry.findInstanceContainers(definition = "fdmprinter",
|
||||||
|
type = "quality",
|
||||||
|
quality_type = quality_type)
|
||||||
|
quality_containers = [q for q in quality_containers if q.getMetaDataEntry("material", "") == ""]
|
||||||
|
if quality_containers:
|
||||||
|
global_stack.quality = quality_containers[0]
|
||||||
|
else:
|
||||||
|
# the quality_type of the quality profile cannot be found.
|
||||||
|
# this can happen if a quality_type has been removed in a newer version, for example:
|
||||||
|
# "extra_coarse" is removed from 2.7 to 3.0
|
||||||
|
# in this case, the quality will be reset to "normal"
|
||||||
|
quality_containers = self._container_registry.findInstanceContainers(
|
||||||
|
definition = global_stack.definition.getId(),
|
||||||
|
type = "quality",
|
||||||
|
quality_type = "normal")
|
||||||
|
quality_containers = [q for q in quality_containers if q.getMetaDataEntry("material", "") == ""]
|
||||||
|
if quality_containers:
|
||||||
|
global_stack.quality = quality_containers[0]
|
||||||
|
else:
|
||||||
|
# This should not happen!
|
||||||
|
Logger.log("e", "Cannot find quality normal for global stack [%s] [%s]",
|
||||||
|
global_stack.getId(), global_stack.definition.getId())
|
||||||
|
global_stack.quality = self._container_registry.findInstanceContainers(id = "empty_quality")[0]
|
||||||
|
|
||||||
# Replacing the old containers if resolve is "new".
|
# Replacing the old containers if resolve is "new".
|
||||||
# When resolve is "new", some containers will get renamed, so all the other containers that reference to those
|
# When resolve is "new", some containers will get renamed, so all the other containers that reference to those
|
||||||
# MUST get updated too.
|
# MUST get updated too.
|
||||||
|
|
|
@ -95,22 +95,22 @@ class ProcessSlicedLayersJob(Job):
|
||||||
|
|
||||||
# Find the minimum layer number
|
# Find the minimum layer number
|
||||||
# When using a raft, the raft layers are sent as layers < 0. Instead of allowing layers < 0, we
|
# When using a raft, the raft layers are sent as layers < 0. Instead of allowing layers < 0, we
|
||||||
# instead simply offset all other layers so the lowest layer is always 0.
|
# instead simply offset all other layers so the lowest layer is always 0. It could happens that
|
||||||
|
# the first raft layer has value -8 but there are just 4 raft (negative) layers.
|
||||||
min_layer_number = 0
|
min_layer_number = 0
|
||||||
|
negative_layers = 0
|
||||||
for layer in self._layers:
|
for layer in self._layers:
|
||||||
if layer.id < min_layer_number:
|
if layer.id < min_layer_number:
|
||||||
min_layer_number = layer.id
|
min_layer_number = layer.id
|
||||||
|
if layer.id < 0:
|
||||||
|
negative_layers += 1
|
||||||
|
|
||||||
current_layer = 0
|
current_layer = 0
|
||||||
|
|
||||||
for layer in self._layers:
|
for layer in self._layers:
|
||||||
abs_layer_number = layer.id + abs(min_layer_number)
|
# Negative layers are offset by the minimum layer number, but the positive layers are just
|
||||||
|
# offset by the number of negative layers so there is no layer gap between raft and model
|
||||||
# Workaround when the last layer doesn't have paths, this layer is skipped because this was generating
|
abs_layer_number = layer.id + abs(min_layer_number) if layer.id < 0 else layer.id + negative_layers
|
||||||
# some glitches when rendering.
|
|
||||||
if layer.id == len(self._layers)-1 and layer.repeatedMessageCount("path_segment") == 0:
|
|
||||||
Logger.log("i", "No sliced data in the layer", layer.id)
|
|
||||||
continue
|
|
||||||
|
|
||||||
layer_data.addLayer(abs_layer_number)
|
layer_data.addLayer(abs_layer_number)
|
||||||
this_layer = layer_data.getLayer(abs_layer_number)
|
this_layer = layer_data.getLayer(abs_layer_number)
|
||||||
|
|
|
@ -143,6 +143,11 @@ class GCodeReader(MeshReader):
|
||||||
this_layer.polygons.append(this_poly)
|
this_layer.polygons.append(this_poly)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def _createEmptyLayer(self, layer_number):
|
||||||
|
self._layer_data_builder.addLayer(layer_number)
|
||||||
|
self._layer_data_builder.setLayerHeight(layer_number, 0)
|
||||||
|
self._layer_data_builder.setLayerThickness(layer_number, 0)
|
||||||
|
|
||||||
def _calculateLineWidth(self, current_point, previous_point, current_extrusion, previous_extrusion, layer_thickness):
|
def _calculateLineWidth(self, current_point, previous_point, current_extrusion, previous_extrusion, layer_thickness):
|
||||||
# Area of the filament
|
# Area of the filament
|
||||||
Af = (self._filament_diameter / 2) ** 2 * numpy.pi
|
Af = (self._filament_diameter / 2) ** 2 * numpy.pi
|
||||||
|
@ -322,6 +327,9 @@ class GCodeReader(MeshReader):
|
||||||
|
|
||||||
current_position = self._position(0, 0, 0, 0, [0])
|
current_position = self._position(0, 0, 0, 0, [0])
|
||||||
current_path = []
|
current_path = []
|
||||||
|
min_layer_number = 0
|
||||||
|
negative_layers = 0
|
||||||
|
previous_layer = 0
|
||||||
|
|
||||||
for line in file:
|
for line in file:
|
||||||
if self._cancelled:
|
if self._cancelled:
|
||||||
|
@ -359,7 +367,23 @@ class GCodeReader(MeshReader):
|
||||||
layer_number = int(line[len(self._layer_keyword):])
|
layer_number = int(line[len(self._layer_keyword):])
|
||||||
self._createPolygon(self._current_layer_thickness, current_path, self._extruder_offsets.get(self._extruder_number, [0, 0]))
|
self._createPolygon(self._current_layer_thickness, current_path, self._extruder_offsets.get(self._extruder_number, [0, 0]))
|
||||||
current_path.clear()
|
current_path.clear()
|
||||||
|
|
||||||
|
# When using a raft, the raft layers are stored as layers < 0, it mimics the same behavior
|
||||||
|
# as in ProcessSlicedLayersJob
|
||||||
|
if layer_number < min_layer_number:
|
||||||
|
min_layer_number = layer_number
|
||||||
|
if layer_number < 0:
|
||||||
|
layer_number += abs(min_layer_number)
|
||||||
|
negative_layers += 1
|
||||||
|
else:
|
||||||
|
layer_number += negative_layers
|
||||||
|
|
||||||
|
# In case there is a gap in the layer count, empty layers are created
|
||||||
|
for empty_layer in range(previous_layer + 1, layer_number):
|
||||||
|
self._createEmptyLayer(empty_layer)
|
||||||
|
|
||||||
self._layer_number = layer_number
|
self._layer_number = layer_number
|
||||||
|
previous_layer = layer_number
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
|
@ -34,6 +34,7 @@ class SimulationPass(RenderPass):
|
||||||
self._nozzle_shader = None
|
self._nozzle_shader = None
|
||||||
self._old_current_layer = 0
|
self._old_current_layer = 0
|
||||||
self._old_current_path = 0
|
self._old_current_path = 0
|
||||||
|
self._switching_layers = True # It tracks when the user is moving the layers' slider
|
||||||
self._gl = OpenGL.getInstance().getBindingsObject()
|
self._gl = OpenGL.getInstance().getBindingsObject()
|
||||||
self._scene = Application.getInstance().getController().getScene()
|
self._scene = Application.getInstance().getController().getScene()
|
||||||
self._extruder_manager = ExtruderManager.getInstance()
|
self._extruder_manager = ExtruderManager.getInstance()
|
||||||
|
@ -91,7 +92,7 @@ class SimulationPass(RenderPass):
|
||||||
|
|
||||||
self.bind()
|
self.bind()
|
||||||
|
|
||||||
tool_handle_batch = RenderBatch(self._tool_handle_shader, type = RenderBatch.RenderType.Solid)
|
tool_handle_batch = RenderBatch(self._tool_handle_shader, type = RenderBatch.RenderType.Overlay)
|
||||||
head_position = None # Indicates the current position of the print head
|
head_position = None # Indicates the current position of the print head
|
||||||
nozzle_node = None
|
nozzle_node = None
|
||||||
|
|
||||||
|
@ -143,8 +144,10 @@ class SimulationPass(RenderPass):
|
||||||
# All the layers but the current selected layer are rendered first
|
# All the layers but the current selected layer are rendered first
|
||||||
if self._old_current_path != self._layer_view._current_path_num:
|
if self._old_current_path != self._layer_view._current_path_num:
|
||||||
self._current_shader = self._layer_shadow_shader
|
self._current_shader = self._layer_shadow_shader
|
||||||
|
self._switching_layers = False
|
||||||
if not self._layer_view.isSimulationRunning() and self._old_current_layer != self._layer_view._current_layer_num:
|
if not self._layer_view.isSimulationRunning() and self._old_current_layer != self._layer_view._current_layer_num:
|
||||||
self._current_shader = self._layer_shader
|
self._current_shader = self._layer_shader
|
||||||
|
self._switching_layers = True
|
||||||
|
|
||||||
layers_batch = RenderBatch(self._current_shader, type = RenderBatch.RenderType.Solid, mode = RenderBatch.RenderMode.Lines, range = (start, end))
|
layers_batch = RenderBatch(self._current_shader, type = RenderBatch.RenderType.Solid, mode = RenderBatch.RenderMode.Lines, range = (start, end))
|
||||||
layers_batch.addItem(node.getWorldTransformation(), layer_data)
|
layers_batch.addItem(node.getWorldTransformation(), layer_data)
|
||||||
|
@ -170,8 +173,9 @@ class SimulationPass(RenderPass):
|
||||||
if len(batch.items) > 0:
|
if len(batch.items) > 0:
|
||||||
batch.render(self._scene.getActiveCamera())
|
batch.render(self._scene.getActiveCamera())
|
||||||
|
|
||||||
# The nozzle is drawn once we know the correct position
|
# The nozzle is drawn when once we know the correct position of the head,
|
||||||
if not self._compatibility_mode and self._layer_view.getActivity() and nozzle_node is not None:
|
# but the user is not using the layer slider, and the compatibility mode is not enabled
|
||||||
|
if not self._switching_layers and not self._compatibility_mode and self._layer_view.getActivity() and nozzle_node is not None:
|
||||||
if head_position is not None:
|
if head_position is not None:
|
||||||
nozzle_node.setVisible(True)
|
nozzle_node.setVisible(True)
|
||||||
nozzle_node.setPosition(head_position)
|
nozzle_node.setPosition(head_position)
|
||||||
|
|
|
@ -376,7 +376,7 @@ class SimulationView(View):
|
||||||
if layer is None:
|
if layer is None:
|
||||||
return
|
return
|
||||||
new_max_paths = layer.lineMeshElementCount()
|
new_max_paths = layer.lineMeshElementCount()
|
||||||
if new_max_paths > 0 and new_max_paths != self._max_paths:
|
if new_max_paths >= 0 and new_max_paths != self._max_paths:
|
||||||
self._max_paths = new_max_paths
|
self._max_paths = new_max_paths
|
||||||
self.maxPathsChanged.emit()
|
self.maxPathsChanged.emit()
|
||||||
|
|
||||||
|
|
|
@ -138,10 +138,11 @@ Item
|
||||||
text: catalog.i18nc("@label:listbox", "Feedrate"),
|
text: catalog.i18nc("@label:listbox", "Feedrate"),
|
||||||
type_id: 2
|
type_id: 2
|
||||||
})
|
})
|
||||||
layerViewTypes.append({
|
// TODO DON'T DELETE!!!! This part must be enabled when adaptive layer height feature is available
|
||||||
text: catalog.i18nc("@label:listbox", "Layer thickness"),
|
// layerViewTypes.append({
|
||||||
type_id: 3 // these ids match the switching in the shader
|
// text: catalog.i18nc("@label:listbox", "Layer thickness"),
|
||||||
})
|
// type_id: 3 // these ids match the switching in the shader
|
||||||
|
// })
|
||||||
}
|
}
|
||||||
|
|
||||||
ComboBox
|
ComboBox
|
||||||
|
@ -619,7 +620,7 @@ Item
|
||||||
Timer
|
Timer
|
||||||
{
|
{
|
||||||
id: simulationTimer
|
id: simulationTimer
|
||||||
interval: 250
|
interval: 100
|
||||||
running: false
|
running: false
|
||||||
repeat: true
|
repeat: true
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
|
|
|
@ -11,7 +11,7 @@ catalog = i18nCatalog("cura")
|
||||||
def getMetaData():
|
def getMetaData():
|
||||||
return {
|
return {
|
||||||
"view": {
|
"view": {
|
||||||
"name": catalog.i18nc("@item:inlistbox", "Simulation view"),
|
"name": catalog.i18nc("@item:inlistbox", "Layer view"),
|
||||||
"view_panel": "SimulationView.qml",
|
"view_panel": "SimulationView.qml",
|
||||||
"weight": 2
|
"weight": 2
|
||||||
}
|
}
|
||||||
|
|
|
@ -116,7 +116,10 @@ class VersionUpgrade30to31(VersionUpgrade):
|
||||||
all_quality_changes = self._getSingleExtrusionMachineQualityChanges(parser)
|
all_quality_changes = self._getSingleExtrusionMachineQualityChanges(parser)
|
||||||
# Note that DO NOT!!! use the quality_changes returned from _getSingleExtrusionMachineQualityChanges().
|
# Note that DO NOT!!! use the quality_changes returned from _getSingleExtrusionMachineQualityChanges().
|
||||||
# Those are loaded from the hard drive which are original files that haven't been upgraded yet.
|
# Those are loaded from the hard drive which are original files that haven't been upgraded yet.
|
||||||
if len(all_quality_changes) == 1 and not parser.has_option("metadata", "extruder"):
|
# NOTE 2: The number can be 0 or 1 depends on whether you are loading it from the qualities folder or
|
||||||
|
# from a project file. When you load from a project file, the custom profile may not be in cura
|
||||||
|
# yet, so you will get 0.
|
||||||
|
if len(all_quality_changes) <= 1 and not parser.has_option("metadata", "extruder"):
|
||||||
self._createExtruderQualityChangesForSingleExtrusionMachine(filename, parser)
|
self._createExtruderQualityChangesForSingleExtrusionMachine(filename, parser)
|
||||||
|
|
||||||
# Update version numbers
|
# Update version numbers
|
||||||
|
@ -199,7 +202,7 @@ class VersionUpgrade30to31(VersionUpgrade):
|
||||||
|
|
||||||
def _createExtruderQualityChangesForSingleExtrusionMachine(self, filename, global_quality_changes):
|
def _createExtruderQualityChangesForSingleExtrusionMachine(self, filename, global_quality_changes):
|
||||||
suffix = "_" + quote_plus(global_quality_changes["general"]["name"].lower())
|
suffix = "_" + quote_plus(global_quality_changes["general"]["name"].lower())
|
||||||
machine_name = filename.strip("." + os.sep).replace(suffix, "")
|
machine_name = os.path.os.path.basename(filename).replace(".inst.cfg", "").replace(suffix, "")
|
||||||
new_filename = machine_name + "_" + "fdmextruder" + suffix
|
new_filename = machine_name + "_" + "fdmextruder" + suffix
|
||||||
|
|
||||||
extruder_quality_changes_parser = configparser.ConfigParser()
|
extruder_quality_changes_parser = configparser.ConfigParser()
|
||||||
|
|
|
@ -422,11 +422,11 @@ class XmlMaterialProfile(InstanceContainer):
|
||||||
return version * 1000000 + setting_version
|
return version * 1000000 + setting_version
|
||||||
|
|
||||||
## Overridden from InstanceContainer
|
## Overridden from InstanceContainer
|
||||||
def deserialize(self, serialized):
|
def deserialize(self, serialized, file_name = None):
|
||||||
containers_to_add = []
|
containers_to_add = []
|
||||||
# update the serialized data first
|
# update the serialized data first
|
||||||
from UM.Settings.Interfaces import ContainerInterface
|
from UM.Settings.Interfaces import ContainerInterface
|
||||||
serialized = ContainerInterface.deserialize(self, serialized)
|
serialized = ContainerInterface.deserialize(self, serialized, file_name)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
data = ET.fromstring(serialized)
|
data = ET.fromstring(serialized)
|
||||||
|
|
|
@ -159,7 +159,7 @@ UM.PreferencesPage
|
||||||
append({ text: "Nederlands", code: "nl_NL" })
|
append({ text: "Nederlands", code: "nl_NL" })
|
||||||
append({ text: "Polski", code: "pl_PL" })
|
append({ text: "Polski", code: "pl_PL" })
|
||||||
append({ text: "Português do Brasil", code: "pt_BR" })
|
append({ text: "Português do Brasil", code: "pt_BR" })
|
||||||
append({ text: "Русский", code: "ru_RU" })
|
//Russian is disabled for being incomplete: append({ text: "Русский", code: "ru_RU" })
|
||||||
append({ text: "Türkçe", code: "tr_TR" })
|
append({ text: "Türkçe", code: "tr_TR" })
|
||||||
append({ text: "简体中文", code: "zh_CN" })
|
append({ text: "简体中文", code: "zh_CN" })
|
||||||
|
|
||||||
|
|
|
@ -322,7 +322,6 @@ UM.ManagementPage
|
||||||
{
|
{
|
||||||
messageDialog.icon = StandardIcon.Information
|
messageDialog.icon = StandardIcon.Information
|
||||||
messageDialog.text = catalog.i18nc("@info:status Don't translate the XML tag <filename>!", "Successfully imported material <filename>%1</filename>").arg(fileUrl)
|
messageDialog.text = catalog.i18nc("@info:status Don't translate the XML tag <filename>!", "Successfully imported material <filename>%1</filename>").arg(fileUrl)
|
||||||
currentItem = base.model.getItem(base.objectList.currentIndex)
|
|
||||||
}
|
}
|
||||||
else if(result.status == "duplicate")
|
else if(result.status == "duplicate")
|
||||||
{
|
{
|
||||||
|
|
|
@ -220,6 +220,83 @@ Rectangle
|
||||||
menu: PrinterMenu { }
|
menu: PrinterMenu { }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//View orientation Item
|
||||||
|
Row
|
||||||
|
{
|
||||||
|
id: viewOrientationControl
|
||||||
|
height: 30
|
||||||
|
|
||||||
|
spacing: 2
|
||||||
|
|
||||||
|
visible: !base.monitoringPrint
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
verticalCenter: base.verticalCenter
|
||||||
|
right: viewModeButton.right
|
||||||
|
rightMargin: UM.Theme.getSize("default_margin").width + viewModeButton.width
|
||||||
|
}
|
||||||
|
|
||||||
|
// #1 3d view
|
||||||
|
Button
|
||||||
|
{
|
||||||
|
iconSource: UM.Theme.getIcon("view_3d")
|
||||||
|
style: UM.Theme.styles.orientation_button
|
||||||
|
anchors.verticalCenter: viewOrientationControl.verticalCenter
|
||||||
|
onClicked:{
|
||||||
|
UM.Controller.rotateView("3d", 0);
|
||||||
|
}
|
||||||
|
visible: base.width > 1100
|
||||||
|
}
|
||||||
|
|
||||||
|
// #2 Front view
|
||||||
|
Button
|
||||||
|
{
|
||||||
|
iconSource: UM.Theme.getIcon("view_front")
|
||||||
|
style: UM.Theme.styles.orientation_button
|
||||||
|
anchors.verticalCenter: viewOrientationControl.verticalCenter
|
||||||
|
onClicked:{
|
||||||
|
UM.Controller.rotateView("home", 0);
|
||||||
|
}
|
||||||
|
visible: base.width > 1130
|
||||||
|
}
|
||||||
|
|
||||||
|
// #3 Top view
|
||||||
|
Button
|
||||||
|
{
|
||||||
|
iconSource: UM.Theme.getIcon("view_top")
|
||||||
|
style: UM.Theme.styles.orientation_button
|
||||||
|
anchors.verticalCenter: viewOrientationControl.verticalCenter
|
||||||
|
onClicked:{
|
||||||
|
UM.Controller.rotateView("y", 90);
|
||||||
|
}
|
||||||
|
visible: base.width > 1160
|
||||||
|
}
|
||||||
|
|
||||||
|
// #4 Left view
|
||||||
|
Button
|
||||||
|
{
|
||||||
|
iconSource: UM.Theme.getIcon("view_left")
|
||||||
|
style: UM.Theme.styles.orientation_button
|
||||||
|
anchors.verticalCenter: viewOrientationControl.verticalCenter
|
||||||
|
onClicked:{
|
||||||
|
UM.Controller.rotateView("x", 90);
|
||||||
|
}
|
||||||
|
visible: base.width > 1190
|
||||||
|
}
|
||||||
|
|
||||||
|
// #5 Left view
|
||||||
|
Button
|
||||||
|
{
|
||||||
|
iconSource: UM.Theme.getIcon("view_right")
|
||||||
|
style: UM.Theme.styles.orientation_button
|
||||||
|
anchors.verticalCenter: viewOrientationControl.verticalCenter
|
||||||
|
onClicked:{
|
||||||
|
UM.Controller.rotateView("x", -90);
|
||||||
|
}
|
||||||
|
visible: base.width > 1210
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ComboBox
|
ComboBox
|
||||||
{
|
{
|
||||||
id: viewModeButton
|
id: viewModeButton
|
||||||
|
|
14
resources/themes/cura-light/icons/view_3d.svg
Normal file
14
resources/themes/cura-light/icons/view_3d.svg
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg width="30px" height="30px" viewBox="0 0 30 30" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
|
<!-- Generator: Sketch 47.1 (45422) - http://www.bohemiancoding.com/sketch -->
|
||||||
|
<title>icn_perspectives_white</title>
|
||||||
|
<desc>Created with Sketch.</desc>
|
||||||
|
<defs></defs>
|
||||||
|
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||||
|
<g id="icn_perspectives_white" fill="#1F2427">
|
||||||
|
<polygon id="Polygon" points="15 3 26 7.75 15 13 4 7.75"></polygon>
|
||||||
|
<polygon id="Rectangle-3" points="16 15 26 10 26 21 16 26"></polygon>
|
||||||
|
<polygon id="Rectangle-3-Copy" transform="translate(9.000000, 18.000000) scale(-1, 1) translate(-9.000000, -18.000000) " points="4 15 14 10 14 21 4 26"></polygon>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 868 B |
19
resources/themes/cura-light/icons/view_front.svg
Normal file
19
resources/themes/cura-light/icons/view_front.svg
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg width="30px" height="30px" viewBox="0 0 30 30" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
|
<!-- Generator: Sketch 47.1 (45422) - http://www.bohemiancoding.com/sketch -->
|
||||||
|
<title>icn_front_white</title>
|
||||||
|
<desc>Created with Sketch.</desc>
|
||||||
|
<defs>
|
||||||
|
<rect id="path-1" x="4" y="11" width="15" height="15"></rect>
|
||||||
|
</defs>
|
||||||
|
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||||
|
<g id="icn_front_white">
|
||||||
|
<polygon id="Path-2" stroke="#1F2427" points="25.9854977 4 11.3636023 4 4.05692142 11.3333333 4 26 18.6890523 26 26 18.6666667"></polygon>
|
||||||
|
<g id="Rectangle-3-Copy-3">
|
||||||
|
<use fill="#1F2427" fill-rule="evenodd" xlink:href="#path-1"></use>
|
||||||
|
<rect stroke="#1F2427" stroke-width="1" x="4.5" y="11.5" width="14" height="14"></rect>
|
||||||
|
</g>
|
||||||
|
<path d="M18.4012594,11.6428026 L26,4" id="Path-3" stroke="#1F2427"></path>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1 KiB |
21
resources/themes/cura-light/icons/view_left.svg
Normal file
21
resources/themes/cura-light/icons/view_left.svg
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg width="30px" height="30px" viewBox="0 0 30 30" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
|
<!-- Generator: Sketch 47.1 (45422) - http://www.bohemiancoding.com/sketch -->
|
||||||
|
<title>icn_left_white</title>
|
||||||
|
<desc>Created with Sketch.</desc>
|
||||||
|
<defs>
|
||||||
|
<polygon id="path-1" points="0 7.33333333 7.13513514 0 7.13513514 14.6666667 0 22"></polygon>
|
||||||
|
</defs>
|
||||||
|
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||||
|
<g id="icn_left_white">
|
||||||
|
<g id="Group-Copy" transform="translate(4.000000, 4.000000)">
|
||||||
|
<polygon id="Path-2-Copy-4" stroke="#1F2427" transform="translate(10.735474, 11.000000) scale(-1, 1) translate(-10.735474, -11.000000) " points="21.4567937 0 7.18652344 0 0.0555525861 7.33333333 0 22 14.3358121 22 21.4709473 14.6666667"></polygon>
|
||||||
|
<g id="Rectangle-3-Copy-14" transform="translate(3.567568, 11.000000) scale(-1, 1) translate(-3.567568, -11.000000) ">
|
||||||
|
<use fill="#1F2427" fill-rule="evenodd" xlink:href="#path-1"></use>
|
||||||
|
<path stroke="#1F2427" stroke-width="1" d="M0.5,7.53643942 L0.5,20.7691161 L6.63513514,14.4635606 L6.63513514,1.23088386 L0.5,7.53643942 Z"></path>
|
||||||
|
</g>
|
||||||
|
<path d="M6.65507549,7.42568631 L21.5199404,7.42568631" id="Path-3" stroke="#1F2427"></path>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.4 KiB |
21
resources/themes/cura-light/icons/view_right.svg
Normal file
21
resources/themes/cura-light/icons/view_right.svg
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg width="30px" height="30px" viewBox="0 0 30 30" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
|
<!-- Generator: Sketch 47.1 (45422) - http://www.bohemiancoding.com/sketch -->
|
||||||
|
<title>icn_right_white</title>
|
||||||
|
<desc>Created with Sketch.</desc>
|
||||||
|
<defs>
|
||||||
|
<polygon id="path-1" points="14.2702703 7.33333333 21.4054054 0 21.4054054 14.6666667 14.2702703 22"></polygon>
|
||||||
|
</defs>
|
||||||
|
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||||
|
<g id="icn_right_white">
|
||||||
|
<g id="Group-2-Copy" transform="translate(4.000000, 4.000000)">
|
||||||
|
<polygon id="Path-2-Copy-2" stroke="#1F2427" points="21.4567937 0 7.18652344 0 0.0555525861 7.33333333 0 22 14.3358121 22 21.4709473 14.6666667"></polygon>
|
||||||
|
<g id="Rectangle-3-Copy-10">
|
||||||
|
<use fill="#1F2427" fill-rule="evenodd" xlink:href="#path-1"></use>
|
||||||
|
<path stroke="#1F2427" stroke-width="1" d="M14.7702703,7.53643942 L14.7702703,20.7691161 L20.9054054,14.4635606 L20.9054054,1.23088386 L14.7702703,7.53643942 Z"></path>
|
||||||
|
</g>
|
||||||
|
<path d="M0.114534945,7.42568631 L14.9793998,7.42568631" id="Path-3" stroke="#1F2427"></path>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.3 KiB |
21
resources/themes/cura-light/icons/view_top.svg
Normal file
21
resources/themes/cura-light/icons/view_top.svg
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg width="30px" height="30px" viewBox="0 0 30 30" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
|
<!-- Generator: Sketch 47.1 (45422) - http://www.bohemiancoding.com/sketch -->
|
||||||
|
<title>icn_top_white</title>
|
||||||
|
<desc>Created with Sketch.</desc>
|
||||||
|
<defs>
|
||||||
|
<polygon id="path-1" points="7.13513514 0 21.4054054 0 14.2702703 7.33333333 0 7.33333333"></polygon>
|
||||||
|
</defs>
|
||||||
|
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||||
|
<g id="icn_top_white">
|
||||||
|
<g id="Group-3-Copy" transform="translate(4.000000, 4.000000)">
|
||||||
|
<polygon id="Path-2-Copy" stroke="#1F2427" points="21.4567937 0 7.18652344 0 0.0555525861 7.33333333 0 22 14.3358121 22 21.4709473 14.6666667"></polygon>
|
||||||
|
<g id="Rectangle-3-Copy-6">
|
||||||
|
<use fill="#1F2427" fill-rule="evenodd" xlink:href="#path-1"></use>
|
||||||
|
<path stroke="#1F2427" stroke-width="1" d="M7.34626538,0.5 L1.18410322,6.83333333 L14.05914,6.83333333 L20.2213022,0.5 L7.34626538,0.5 Z"></path>
|
||||||
|
</g>
|
||||||
|
<path d="M14.2702703,22 L14.2702703,6.72222222" id="Path-3" stroke="#1F2427"></path>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.3 KiB |
|
@ -381,6 +381,111 @@ QtObject {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
property Component orientation_button: Component {
|
||||||
|
ButtonStyle {
|
||||||
|
background: Item {
|
||||||
|
implicitWidth: 25;
|
||||||
|
implicitHeight: 25;
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: buttonFace2;
|
||||||
|
|
||||||
|
anchors.fill: parent;
|
||||||
|
property bool down: control.pressed || (control.checkable && control.checked);
|
||||||
|
|
||||||
|
color: {
|
||||||
|
if(control.customColor !== undefined && control.customColor !== null) {
|
||||||
|
return control.customColor
|
||||||
|
} else if(control.checkable && control.checked && control.hovered) {
|
||||||
|
return Theme.getColor("button_active_hover");
|
||||||
|
} else if(control.pressed || (control.checkable && control.checked)) {
|
||||||
|
return Theme.getColor("button_active");
|
||||||
|
} else if(control.hovered) {
|
||||||
|
return Theme.getColor("button_hover");
|
||||||
|
} else {
|
||||||
|
//return Theme.getColor("button");
|
||||||
|
return "transparent"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Behavior on color { ColorAnimation { duration: 50; } }
|
||||||
|
|
||||||
|
border.width: (control.hasOwnProperty("needBorder") && control.needBorder) ? 2 * screenScaleFactor : 0
|
||||||
|
border.color: Theme.getColor("tool_button_border")
|
||||||
|
|
||||||
|
UM.RecolorImage {
|
||||||
|
id: tool_button_arrow2
|
||||||
|
//anchors.right: parent.right;
|
||||||
|
//anchors.rightMargin: (Theme.getSize("button").width - Theme.getSize("button_icon").width) / 4
|
||||||
|
//anchors.bottom: parent.bottom;
|
||||||
|
//anchors.bottomMargin: (Theme.getSize("button").height - Theme.getSize("button_icon").height) / 4
|
||||||
|
//width: Theme.getSize("standard_arrow").width
|
||||||
|
//height: Theme.getSize("standard_arrow").height
|
||||||
|
|
||||||
|
width: 5
|
||||||
|
height: 5
|
||||||
|
|
||||||
|
sourceSize.width: 5
|
||||||
|
sourceSize.height: 5
|
||||||
|
visible: control.menu != null;
|
||||||
|
color:
|
||||||
|
{
|
||||||
|
if(control.checkable && control.checked && control.hovered)
|
||||||
|
{
|
||||||
|
return Theme.getColor("button_text_active_hover");
|
||||||
|
}
|
||||||
|
else if(control.pressed || (control.checkable && control.checked))
|
||||||
|
{
|
||||||
|
return Theme.getColor("button_text_active");
|
||||||
|
}
|
||||||
|
else if(control.hovered)
|
||||||
|
{
|
||||||
|
return Theme.getColor("button_text_hover");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return Theme.getColor("button_text");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
source: Theme.getIcon("arrow_bottom")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
label: Item {
|
||||||
|
UM.RecolorImage {
|
||||||
|
anchors.centerIn: parent;
|
||||||
|
opacity: !control.enabled ? 0.2 : 1.0
|
||||||
|
source: control.iconSource;
|
||||||
|
width: 20;
|
||||||
|
height: 20;
|
||||||
|
color:
|
||||||
|
{
|
||||||
|
if(control.checkable && control.checked && control.hovered)
|
||||||
|
{
|
||||||
|
return Theme.getColor("button_text_active_hover");
|
||||||
|
}
|
||||||
|
else if(control.pressed || (control.checkable && control.checked))
|
||||||
|
{
|
||||||
|
return Theme.getColor("button_text_active");
|
||||||
|
}
|
||||||
|
else if(control.hovered)
|
||||||
|
{
|
||||||
|
//return Theme.getColor("button_text_hover");
|
||||||
|
return "white"
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//return Theme.getColor("button_text");
|
||||||
|
return "black"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceSize: Theme.getSize("button_icon")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
property Component progressbar: Component{
|
property Component progressbar: Component{
|
||||||
ProgressBarStyle {
|
ProgressBarStyle {
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
|
@ -753,6 +858,49 @@ QtObject {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
property Component partially_checkbox: Component {
|
||||||
|
CheckBoxStyle {
|
||||||
|
background: Item { }
|
||||||
|
indicator: Rectangle {
|
||||||
|
implicitWidth: Theme.getSize("checkbox").width;
|
||||||
|
implicitHeight: Theme.getSize("checkbox").height;
|
||||||
|
|
||||||
|
color: (control.hovered || control._hovered) ? Theme.getColor("checkbox_hover") : Theme.getColor("checkbox");
|
||||||
|
Behavior on color { ColorAnimation { duration: 50; } }
|
||||||
|
|
||||||
|
radius: control.exclusiveGroup ? Theme.getSize("checkbox").width / 2 : 0
|
||||||
|
|
||||||
|
border.width: Theme.getSize("default_lining").width;
|
||||||
|
border.color: (control.hovered || control._hovered) ? Theme.getColor("checkbox_border_hover") : Theme.getColor("checkbox_border");
|
||||||
|
|
||||||
|
UM.RecolorImage {
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
width: parent.width / 2.5
|
||||||
|
height: parent.height / 2.5
|
||||||
|
sourceSize.width: width
|
||||||
|
sourceSize.height: width
|
||||||
|
color: Theme.getColor("checkbox_mark")
|
||||||
|
source: {
|
||||||
|
if (control.checkbox_state == 2){
|
||||||
|
return Theme.getIcon("solid")
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
return control.exclusiveGroup ? Theme.getIcon("dot") : Theme.getIcon("check")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
opacity: control.checked
|
||||||
|
Behavior on opacity { NumberAnimation { duration: 100; } }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
label: Label {
|
||||||
|
text: control.text;
|
||||||
|
color: Theme.getColor("checkbox_text");
|
||||||
|
font: Theme.getFont("default");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
property Component slider: Component {
|
property Component slider: Component {
|
||||||
SliderStyle {
|
SliderStyle {
|
||||||
groove: Rectangle {
|
groove: Rectangle {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue