mirror of
https://git.mirrors.martin98.com/https://github.com/Ultimaker/Cura
synced 2025-08-12 04:39:02 +08:00
Merge branch 'master' into speedup
This commit is contained in:
commit
98b0559c9a
@ -91,6 +91,7 @@ from cura.Settings.UserChangesModel import UserChangesModel
|
||||
from cura.Settings.ExtrudersModel import ExtrudersModel
|
||||
from cura.Settings.MaterialSettingsVisibilityHandler import MaterialSettingsVisibilityHandler
|
||||
from cura.Settings.ContainerManager import ContainerManager
|
||||
from cura.Settings.SettingVisibilityPresetsModel import SettingVisibilityPresetsModel
|
||||
|
||||
from cura.ObjectsModel import ObjectsModel
|
||||
|
||||
@ -140,6 +141,7 @@ class CuraApplication(QtApplication):
|
||||
MachineStack = Resources.UserType + 7
|
||||
ExtruderStack = Resources.UserType + 8
|
||||
DefinitionChangesContainer = Resources.UserType + 9
|
||||
SettingVisibilityPreset = Resources.UserType + 10
|
||||
|
||||
Q_ENUMS(ResourceTypes)
|
||||
|
||||
@ -187,6 +189,7 @@ class CuraApplication(QtApplication):
|
||||
Resources.addStorageType(self.ResourceTypes.ExtruderStack, "extruders")
|
||||
Resources.addStorageType(self.ResourceTypes.MachineStack, "machine_instances")
|
||||
Resources.addStorageType(self.ResourceTypes.DefinitionChangesContainer, "definition_changes")
|
||||
Resources.addStorageType(self.ResourceTypes.SettingVisibilityPreset, "setting_visibility")
|
||||
|
||||
ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.QualityInstanceContainer, "quality")
|
||||
ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.QualityInstanceContainer, "quality_changes")
|
||||
@ -378,19 +381,9 @@ class CuraApplication(QtApplication):
|
||||
|
||||
preferences.setDefault("local_file/last_used_type", "text/x-gcode")
|
||||
|
||||
setting_visibily_preset_names = self.getVisibilitySettingPresetTypes()
|
||||
preferences.setDefault("general/visible_settings_preset", setting_visibily_preset_names)
|
||||
default_visibility_profile = SettingVisibilityPresetsModel.getInstance().getItem(0)
|
||||
|
||||
preset_setting_visibility_choice = Preferences.getInstance().getValue("general/preset_setting_visibility_choice")
|
||||
|
||||
default_preset_visibility_group_name = "Basic"
|
||||
if preset_setting_visibility_choice == "" or preset_setting_visibility_choice is None:
|
||||
if preset_setting_visibility_choice not in setting_visibily_preset_names:
|
||||
preset_setting_visibility_choice = default_preset_visibility_group_name
|
||||
|
||||
visible_settings = self.getVisibilitySettingPreset(settings_preset_name = preset_setting_visibility_choice)
|
||||
preferences.setDefault("general/visible_settings", visible_settings)
|
||||
preferences.setDefault("general/preset_setting_visibility_choice", preset_setting_visibility_choice)
|
||||
preferences.setDefault("general/visible_settings", ";".join(default_visibility_profile["settings"]))
|
||||
|
||||
self.applicationShuttingDown.connect(self.saveSettings)
|
||||
self.engineCreatedSignal.connect(self._onEngineCreated)
|
||||
@ -407,91 +400,6 @@ class CuraApplication(QtApplication):
|
||||
|
||||
CuraApplication.Created = True
|
||||
|
||||
@pyqtSlot(str, result = str)
|
||||
def getVisibilitySettingPreset(self, settings_preset_name) -> str:
|
||||
result = self._loadPresetSettingVisibilityGroup(settings_preset_name)
|
||||
formatted_preset_settings = self._serializePresetSettingVisibilityData(result)
|
||||
|
||||
return formatted_preset_settings
|
||||
|
||||
## Serialise the given preset setting visibitlity group dictionary into a string which is concatenated by ";"
|
||||
#
|
||||
def _serializePresetSettingVisibilityData(self, settings_data: dict) -> str:
|
||||
result_string = ""
|
||||
|
||||
for key in settings_data:
|
||||
result_string += key + ";"
|
||||
for value in settings_data[key]:
|
||||
result_string += value + ";"
|
||||
|
||||
return result_string
|
||||
|
||||
## Load the preset setting visibility group with the given name
|
||||
#
|
||||
def _loadPresetSettingVisibilityGroup(self, visibility_preset_name) -> Dict[str, str]:
|
||||
preset_dir = Resources.getPath(Resources.PresetSettingVisibilityGroups)
|
||||
|
||||
result = {}
|
||||
right_preset_found = False
|
||||
|
||||
for item in os.listdir(preset_dir):
|
||||
file_path = os.path.join(preset_dir, item)
|
||||
if not os.path.isfile(file_path):
|
||||
continue
|
||||
|
||||
parser = ConfigParser(allow_no_value = True) # accept options without any value,
|
||||
|
||||
try:
|
||||
parser.read([file_path])
|
||||
|
||||
if not parser.has_option("general", "name"):
|
||||
continue
|
||||
|
||||
if parser["general"]["name"] == visibility_preset_name:
|
||||
right_preset_found = True
|
||||
for section in parser.sections():
|
||||
if section == 'general':
|
||||
continue
|
||||
else:
|
||||
section_settings = []
|
||||
for option in parser[section].keys():
|
||||
section_settings.append(option)
|
||||
|
||||
result[section] = section_settings
|
||||
|
||||
if right_preset_found:
|
||||
break
|
||||
|
||||
except Exception as e:
|
||||
Logger.log("e", "Failed to load setting visibility preset %s: %s", file_path, str(e))
|
||||
|
||||
return result
|
||||
|
||||
## Check visibility setting preset folder and returns available types
|
||||
#
|
||||
def getVisibilitySettingPresetTypes(self):
|
||||
preset_dir = Resources.getPath(Resources.PresetSettingVisibilityGroups)
|
||||
result = {}
|
||||
|
||||
for item in os.listdir(preset_dir):
|
||||
file_path = os.path.join(preset_dir, item)
|
||||
if not os.path.isfile(file_path):
|
||||
continue
|
||||
|
||||
parser = ConfigParser(allow_no_value=True) # accept options without any value,
|
||||
|
||||
try:
|
||||
parser.read([file_path])
|
||||
|
||||
if not parser.has_option("general", "name") and not parser.has_option("general", "weight"):
|
||||
continue
|
||||
|
||||
result[parser["general"]["weight"]] = parser["general"]["name"]
|
||||
|
||||
except Exception as e:
|
||||
Logger.log("e", "Failed to load setting preset %s: %s", file_path, str(e))
|
||||
|
||||
return result
|
||||
|
||||
def _onEngineCreated(self):
|
||||
self._engine.addImageProvider("camera", CameraImageProvider.CameraImageProvider())
|
||||
@ -991,6 +899,7 @@ class CuraApplication(QtApplication):
|
||||
qmlRegisterType(MachineNameValidator, "Cura", 1, 0, "MachineNameValidator")
|
||||
qmlRegisterType(UserChangesModel, "Cura", 1, 0, "UserChangesModel")
|
||||
qmlRegisterSingletonType(ContainerManager, "Cura", 1, 0, "ContainerManager", ContainerManager.createContainerManager)
|
||||
qmlRegisterSingletonType(SettingVisibilityPresetsModel, "Cura", 1, 0, "SettingVisibilityPresetsModel", SettingVisibilityPresetsModel.createSettingVisibilityPresetsModel)
|
||||
|
||||
# As of Qt5.7, it is necessary to get rid of any ".." in the path for the singleton to work.
|
||||
actions_url = QUrl.fromLocalFile(os.path.abspath(Resources.getPath(CuraApplication.ResourceTypes.QmlFiles, "Actions.qml")))
|
||||
|
@ -87,9 +87,11 @@ class QualitySettingsModel(ListModel):
|
||||
if self._selected_position == self.GLOBAL_STACK_POSITION:
|
||||
quality_node = quality_group.node_for_global
|
||||
else:
|
||||
quality_node = quality_group.nodes_for_extruders.get(self._selected_position)
|
||||
quality_node = quality_group.nodes_for_extruders.get(str(self._selected_position))
|
||||
settings_keys = quality_group.getAllKeys()
|
||||
quality_containers = [quality_node.getContainer()]
|
||||
quality_containers = []
|
||||
if quality_node is not None:
|
||||
quality_containers.append(quality_node.getContainer())
|
||||
|
||||
# Here, if the user has selected a quality changes, then "quality_changes_group" will not be None, and we fetch
|
||||
# the settings in that quality_changes_group.
|
||||
@ -97,7 +99,7 @@ class QualitySettingsModel(ListModel):
|
||||
if self._selected_position == self.GLOBAL_STACK_POSITION:
|
||||
quality_changes_node = quality_changes_group.node_for_global
|
||||
else:
|
||||
quality_changes_node = quality_changes_group.nodes_for_extruders.get(self._selected_position)
|
||||
quality_changes_node = quality_changes_group.nodes_for_extruders.get(str(self._selected_position))
|
||||
if quality_changes_node is not None: # it can be None if number of extruders are changed during runtime
|
||||
try:
|
||||
quality_containers.insert(0, quality_changes_node.getContainer())
|
||||
|
@ -16,6 +16,7 @@ from .QualityGroup import QualityGroup
|
||||
from .QualityNode import QualityNode
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from UM.Settings.DefinitionContainer import DefinitionContainer
|
||||
from cura.Settings.GlobalStack import GlobalStack
|
||||
from .QualityChangesGroup import QualityChangesGroup
|
||||
|
||||
@ -178,7 +179,7 @@ class QualityManager(QObject):
|
||||
|
||||
# Returns a dict of "custom profile name" -> QualityChangesGroup
|
||||
def getQualityChangesGroups(self, machine: "GlobalStack") -> dict:
|
||||
machine_definition_id = getMachineDefinitionIDForQualitySearch(machine)
|
||||
machine_definition_id = getMachineDefinitionIDForQualitySearch(machine.definition)
|
||||
|
||||
machine_node = self._machine_quality_type_to_quality_changes_dict.get(machine_definition_id)
|
||||
if not machine_node:
|
||||
@ -206,7 +207,7 @@ class QualityManager(QObject):
|
||||
# For more details, see QualityGroup.
|
||||
#
|
||||
def getQualityGroups(self, machine: "GlobalStack") -> dict:
|
||||
machine_definition_id = getMachineDefinitionIDForQualitySearch(machine)
|
||||
machine_definition_id = getMachineDefinitionIDForQualitySearch(machine.definition)
|
||||
|
||||
# This determines if we should only get the global qualities for the global stack and skip the global qualities for the extruder stacks
|
||||
has_variant_materials = parseBool(machine.getMetaDataEntry("has_variant_materials", False))
|
||||
@ -315,7 +316,7 @@ class QualityManager(QObject):
|
||||
return quality_group_dict
|
||||
|
||||
def getQualityGroupsForMachineDefinition(self, machine: "GlobalStack") -> dict:
|
||||
machine_definition_id = getMachineDefinitionIDForQualitySearch(machine)
|
||||
machine_definition_id = getMachineDefinitionIDForQualitySearch(machine.definition)
|
||||
|
||||
# To find the quality container for the GlobalStack, check in the following fall-back manner:
|
||||
# (1) the machine-specific node
|
||||
@ -460,7 +461,7 @@ class QualityManager(QObject):
|
||||
quality_changes.addMetaDataEntry("position", extruder_stack.getMetaDataEntry("position"))
|
||||
|
||||
# If the machine specifies qualities should be filtered, ensure we match the current criteria.
|
||||
machine_definition_id = getMachineDefinitionIDForQualitySearch(machine)
|
||||
machine_definition_id = getMachineDefinitionIDForQualitySearch(machine.definition)
|
||||
quality_changes.setDefinition(machine_definition_id)
|
||||
|
||||
quality_changes.addMetaDataEntry("setting_version", self._application.SettingVersion)
|
||||
@ -480,12 +481,13 @@ class QualityManager(QObject):
|
||||
# Example: for an Ultimaker 3 Extended, it has "quality_definition = ultimaker3". This means Ultimaker 3 Extended
|
||||
# shares the same set of qualities profiles as Ultimaker 3.
|
||||
#
|
||||
def getMachineDefinitionIDForQualitySearch(machine: "GlobalStack", default_definition_id: str = "fdmprinter") -> str:
|
||||
def getMachineDefinitionIDForQualitySearch(machine_definition: "DefinitionContainer",
|
||||
default_definition_id: str = "fdmprinter") -> str:
|
||||
machine_definition_id = default_definition_id
|
||||
if parseBool(machine.getMetaDataEntry("has_machine_quality", False)):
|
||||
if parseBool(machine_definition.getMetaDataEntry("has_machine_quality", False)):
|
||||
# Only use the machine's own quality definition ID if this machine has machine quality.
|
||||
machine_definition_id = machine.getMetaDataEntry("quality_definition")
|
||||
machine_definition_id = machine_definition.getMetaDataEntry("quality_definition")
|
||||
if machine_definition_id is None:
|
||||
machine_definition_id = machine.definition.getId()
|
||||
machine_definition_id = machine_definition.getId()
|
||||
|
||||
return machine_definition_id
|
||||
|
@ -204,7 +204,7 @@ class CuraContainerRegistry(ContainerRegistry):
|
||||
global_profile = profile_or_list[0]
|
||||
else:
|
||||
for profile in profile_or_list:
|
||||
if not profile.getMetaDataEntry("extruder"):
|
||||
if not profile.getMetaDataEntry("position"):
|
||||
global_profile = profile
|
||||
break
|
||||
if not global_profile:
|
||||
@ -212,16 +212,34 @@ class CuraContainerRegistry(ContainerRegistry):
|
||||
return { "status": "error",
|
||||
"message": catalog.i18nc("@info:status Don't translate the XML tags <filename> or <message>!", "This profile <filename>{0}</filename> contains incorrect data, could not import it.", file_name)}
|
||||
profile_definition = global_profile.getMetaDataEntry("definition")
|
||||
expected_machine_definition = "fdmprinter"
|
||||
if parseBool(global_container_stack.getMetaDataEntry("has_machine_quality", "False")):
|
||||
expected_machine_definition = global_container_stack.getMetaDataEntry("quality_definition")
|
||||
if not expected_machine_definition:
|
||||
expected_machine_definition = global_container_stack.definition.getId()
|
||||
if expected_machine_definition is not None and profile_definition is not None and profile_definition != expected_machine_definition:
|
||||
|
||||
# Make sure we have a profile_definition in the file:
|
||||
if profile_definition is None:
|
||||
break
|
||||
machine_definition = self.findDefinitionContainers(id = profile_definition)
|
||||
if not machine_definition:
|
||||
Logger.log("e", "Incorrect profile [%s]. Unknown machine type [%s]", file_name, profile_definition)
|
||||
return {"status": "error",
|
||||
"message": catalog.i18nc("@info:status Don't translate the XML tags <filename> or <message>!", "This profile <filename>{0}</filename> contains incorrect data, could not import it.", file_name)
|
||||
}
|
||||
machine_definition = machine_definition[0]
|
||||
|
||||
# Get the expected machine definition.
|
||||
# i.e.: We expect gcode for a UM2 Extended to be defined as normal UM2 gcode...
|
||||
profile_definition = getMachineDefinitionIDForQualitySearch(machine_definition)
|
||||
expected_machine_definition = getMachineDefinitionIDForQualitySearch(global_container_stack.definition)
|
||||
|
||||
# And check if the profile_definition matches either one (showing error if not):
|
||||
if profile_definition != expected_machine_definition:
|
||||
Logger.log("e", "Profile [%s] is for machine [%s] but the current active machine is [%s]. Will not import the profile", file_name, profile_definition, expected_machine_definition)
|
||||
return { "status": "error",
|
||||
"message": catalog.i18nc("@info:status Don't translate the XML tags <filename> or <message>!", "The machine defined in profile <filename>{0}</filename> ({1}) doesn't match with your current machine ({2}), could not import it.", file_name, profile_definition, expected_machine_definition)}
|
||||
|
||||
# Fix the global quality profile's definition field in case it's not correct
|
||||
global_profile.setMetaDataEntry("definition", expected_machine_definition)
|
||||
quality_name = global_profile.getName()
|
||||
quality_type = global_profile.getMetaDataEntry("quality_type")
|
||||
|
||||
name_seed = os.path.splitext(os.path.basename(file_name))[0]
|
||||
new_name = self.uniqueName(name_seed)
|
||||
|
||||
@ -236,11 +254,11 @@ class CuraContainerRegistry(ContainerRegistry):
|
||||
for idx, extruder in enumerate(global_container_stack.extruders.values()):
|
||||
profile_id = ContainerRegistry.getInstance().uniqueName(global_container_stack.getId() + "_extruder_" + str(idx + 1))
|
||||
profile = InstanceContainer(profile_id)
|
||||
profile.setName(global_profile.getName())
|
||||
profile.setName(quality_name)
|
||||
profile.addMetaDataEntry("setting_version", CuraApplication.SettingVersion)
|
||||
profile.addMetaDataEntry("type", "quality_changes")
|
||||
profile.addMetaDataEntry("definition", global_profile.getMetaDataEntry("definition"))
|
||||
profile.addMetaDataEntry("quality_type", global_profile.getMetaDataEntry("quality_type"))
|
||||
profile.addMetaDataEntry("definition", expected_machine_definition)
|
||||
profile.addMetaDataEntry("quality_type", quality_type)
|
||||
profile.addMetaDataEntry("position", "0")
|
||||
profile.setDirty(True)
|
||||
if idx == 0:
|
||||
@ -283,7 +301,7 @@ class CuraContainerRegistry(ContainerRegistry):
|
||||
else: #More extruders in the imported file than in the machine.
|
||||
continue #Delete the additional profiles.
|
||||
|
||||
result = self._configureProfile(profile, profile_id, new_name)
|
||||
result = self._configureProfile(profile, profile_id, new_name, expected_machine_definition)
|
||||
if result is not None:
|
||||
return {"status": "error", "message": catalog.i18nc(
|
||||
"@info:status Don't translate the XML tags <filename> or <message>!",
|
||||
@ -311,7 +329,7 @@ class CuraContainerRegistry(ContainerRegistry):
|
||||
# \param new_name The new name for the profile.
|
||||
#
|
||||
# \return None if configuring was successful or an error message if an error occurred.
|
||||
def _configureProfile(self, profile: InstanceContainer, id_seed: str, new_name: str) -> Optional[str]:
|
||||
def _configureProfile(self, profile: InstanceContainer, id_seed: str, new_name: str, machine_definition_id: str) -> Optional[str]:
|
||||
profile.setDirty(True) # Ensure the profiles are correctly saved
|
||||
|
||||
new_id = self.createUniqueName("quality_changes", "", id_seed, catalog.i18nc("@label", "Custom profile"))
|
||||
@ -321,6 +339,7 @@ class CuraContainerRegistry(ContainerRegistry):
|
||||
# Set the unique Id to the profile, so it's generating a new one even if the user imports the same profile
|
||||
# It also solves an issue with importing profiles from G-Codes
|
||||
profile.setMetaDataEntry("id", new_id)
|
||||
profile.setMetaDataEntry("definition", machine_definition_id)
|
||||
|
||||
if "type" in profile.getMetaData():
|
||||
profile.setMetaDataEntry("type", "quality_changes")
|
||||
@ -331,9 +350,8 @@ class CuraContainerRegistry(ContainerRegistry):
|
||||
if not quality_type:
|
||||
return catalog.i18nc("@info:status", "Profile is missing a quality type.")
|
||||
|
||||
quality_type_criteria = {"quality_type": quality_type}
|
||||
global_stack = Application.getInstance().getGlobalContainerStack()
|
||||
definition_id = getMachineDefinitionIDForQualitySearch(global_stack)
|
||||
definition_id = getMachineDefinitionIDForQualitySearch(global_stack.definition)
|
||||
profile.setDefinition(definition_id)
|
||||
|
||||
# Check to make sure the imported profile actually makes sense in context of the current configuration.
|
||||
|
@ -628,7 +628,7 @@ class MachineManager(QObject):
|
||||
@pyqtProperty(str, notify = globalContainerChanged)
|
||||
def activeQualityDefinitionId(self) -> str:
|
||||
if self._global_container_stack:
|
||||
return getMachineDefinitionIDForQualitySearch(self._global_container_stack)
|
||||
return getMachineDefinitionIDForQualitySearch(self._global_container_stack.definition)
|
||||
return ""
|
||||
|
||||
## Gets how the active definition calls variants
|
||||
@ -882,7 +882,7 @@ class MachineManager(QObject):
|
||||
@pyqtSlot()
|
||||
def forceUpdateAllSettings(self):
|
||||
with postponeSignals(*self._getContainerChangedSignals(), compress = CompressTechnique.CompressPerParameterValue):
|
||||
property_names = ["value", "resolve"]
|
||||
property_names = ["value", "resolve", "validationState"]
|
||||
for container in [self._global_container_stack] + list(self._global_container_stack.extruders.values()):
|
||||
for setting_key in container.getAllKeys():
|
||||
container.propertiesChanged.emit(setting_key, property_names)
|
||||
|
136
cura/Settings/SettingVisibilityPresetsModel.py
Normal file
136
cura/Settings/SettingVisibilityPresetsModel.py
Normal file
@ -0,0 +1,136 @@
|
||||
# Copyright (c) 2018 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import os
|
||||
import urllib
|
||||
from configparser import ConfigParser
|
||||
|
||||
from PyQt5.QtCore import pyqtProperty, Qt, pyqtSignal, pyqtSlot, QUrl
|
||||
|
||||
from UM.Logger import Logger
|
||||
from UM.Qt.ListModel import ListModel
|
||||
from UM.Preferences import Preferences
|
||||
from UM.Resources import Resources
|
||||
from UM.MimeTypeDatabase import MimeTypeDatabase, MimeTypeNotFoundError
|
||||
|
||||
import cura.CuraApplication
|
||||
|
||||
|
||||
class SettingVisibilityPresetsModel(ListModel):
|
||||
IdRole = Qt.UserRole + 1
|
||||
NameRole = Qt.UserRole + 2
|
||||
SettingsRole = Qt.UserRole + 4
|
||||
|
||||
def __init__(self, parent = None):
|
||||
super().__init__(parent)
|
||||
self.addRoleName(self.IdRole, "id")
|
||||
self.addRoleName(self.NameRole, "name")
|
||||
self.addRoleName(self.SettingsRole, "settings")
|
||||
|
||||
self._populate()
|
||||
|
||||
self._preferences = Preferences.getInstance()
|
||||
self._preferences.addPreference("cura/active_setting_visibility_preset", "custom") # Preference to store which preset is currently selected
|
||||
self._preferences.addPreference("cura/custom_visible_settings", "") # Preference that stores the "custom" set so it can always be restored (even after a restart)
|
||||
self._preferences.preferenceChanged.connect(self._onPreferencesChanged)
|
||||
|
||||
self._active_preset = self._preferences.getValue("cura/active_setting_visibility_preset")
|
||||
if self.find("id", self._active_preset) < 0:
|
||||
self._active_preset = "custom"
|
||||
|
||||
self.activePresetChanged.emit()
|
||||
|
||||
|
||||
def _populate(self):
|
||||
items = []
|
||||
for item in Resources.getAllResourcesOfType(cura.CuraApplication.CuraApplication.ResourceTypes.SettingVisibilityPreset):
|
||||
try:
|
||||
mime_type = MimeTypeDatabase.getMimeTypeForFile(item)
|
||||
except MimeTypeNotFoundError:
|
||||
Logger.log("e", "Could not determine mime type of file %s", item)
|
||||
continue
|
||||
|
||||
id = urllib.parse.unquote_plus(mime_type.stripExtension(os.path.basename(item)))
|
||||
|
||||
if not os.path.isfile(item):
|
||||
continue
|
||||
|
||||
parser = ConfigParser(allow_no_value=True) # accept options without any value,
|
||||
|
||||
try:
|
||||
parser.read([item])
|
||||
|
||||
if not parser.has_option("general", "name") and not parser.has_option("general", "weight"):
|
||||
continue
|
||||
|
||||
settings = []
|
||||
for section in parser.sections():
|
||||
if section == 'general':
|
||||
continue
|
||||
|
||||
settings.append(section)
|
||||
for option in parser[section].keys():
|
||||
settings.append(option)
|
||||
|
||||
items.append({
|
||||
"id": id,
|
||||
"name": parser["general"]["name"],
|
||||
"weight": parser["general"]["weight"],
|
||||
"settings": settings
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
Logger.log("e", "Failed to load setting preset %s: %s", file_path, str(e))
|
||||
|
||||
|
||||
items.sort(key = lambda k: (k["weight"], k["id"]))
|
||||
self.setItems(items)
|
||||
|
||||
@pyqtSlot(str)
|
||||
def setActivePreset(self, preset_id):
|
||||
if preset_id != "custom" and self.find("id", preset_id) == -1:
|
||||
Logger.log("w", "Tried to set active preset to unknown id %s", preset_id)
|
||||
return
|
||||
|
||||
if preset_id == "custom" and self._active_preset == "custom":
|
||||
# Copy current visibility set to custom visibility set preference so it can be restored later
|
||||
visibility_string = self._preferences.getValue("general/visible_settings")
|
||||
self._preferences.setValue("cura/custom_visible_settings", visibility_string)
|
||||
|
||||
self._preferences.setValue("cura/active_setting_visibility_preset", preset_id)
|
||||
|
||||
self._active_preset = preset_id
|
||||
self.activePresetChanged.emit()
|
||||
|
||||
activePresetChanged = pyqtSignal()
|
||||
|
||||
@pyqtProperty(str, notify = activePresetChanged)
|
||||
def activePreset(self):
|
||||
return self._active_preset
|
||||
|
||||
def _onPreferencesChanged(self, name):
|
||||
if name != "general/visible_settings":
|
||||
return
|
||||
|
||||
if self._active_preset != "custom":
|
||||
return
|
||||
|
||||
# Copy current visibility set to custom visibility set preference so it can be restored later
|
||||
visibility_string = self._preferences.getValue("general/visible_settings")
|
||||
self._preferences.setValue("cura/custom_visible_settings", visibility_string)
|
||||
|
||||
|
||||
# Factory function, used by QML
|
||||
@staticmethod
|
||||
def createSettingVisibilityPresetsModel(engine, js_engine):
|
||||
return SettingVisibilityPresetsModel.getInstance()
|
||||
|
||||
## Get the singleton instance for this class.
|
||||
@classmethod
|
||||
def getInstance(cls) -> "SettingVisibilityPresetsModel":
|
||||
# Note: Explicit use of class name to prevent issues with inheritance.
|
||||
if not SettingVisibilityPresetsModel.__instance:
|
||||
SettingVisibilityPresetsModel.__instance = cls()
|
||||
return SettingVisibilityPresetsModel.__instance
|
||||
|
||||
__instance = None # type: "SettingVisibilityPresetsModel"
|
@ -66,7 +66,7 @@ class Snapshot:
|
||||
size = max(bbox.width, bbox.height, bbox.depth * 0.5)
|
||||
|
||||
# Looking from this direction (x, y, z) in OGL coordinates
|
||||
looking_from_offset = Vector(1, 1, -2)
|
||||
looking_from_offset = Vector(-1, 1, 2)
|
||||
if size > 0:
|
||||
# determine the watch distance depending on the size
|
||||
looking_from_offset = looking_from_offset * size * 1.3
|
||||
|
@ -122,7 +122,7 @@ class ThreeMFReader(MeshReader):
|
||||
um_node.callDecoration("setActiveExtruder", default_stack.getId())
|
||||
|
||||
# Get the definition & set it
|
||||
definition_id = getMachineDefinitionIDForQualitySearch(global_container_stack)
|
||||
definition_id = getMachineDefinitionIDForQualitySearch(global_container_stack.definition)
|
||||
um_node.callDecoration("getStack").getTop().setDefinition(definition_id)
|
||||
|
||||
setting_container = um_node.callDecoration("getStack").getTop()
|
||||
|
@ -594,7 +594,7 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
|
||||
Logger.log("w", "Workspace did not contain visible settings. Leaving visibility unchanged")
|
||||
else:
|
||||
global_preferences.setValue("general/visible_settings", visible_settings)
|
||||
global_preferences.setValue("general/preset_setting_visibility_choice", "Custom")
|
||||
global_preferences.setValue("cura/active_setting_visibility_preset", "custom")
|
||||
|
||||
categories_expanded = temp_preferences.getValue("cura/categories_expanded")
|
||||
if categories_expanded is None:
|
||||
@ -719,7 +719,7 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
|
||||
|
||||
# Get the correct extruder definition IDs for quality changes
|
||||
from cura.Machines.QualityManager import getMachineDefinitionIDForQualitySearch
|
||||
machine_definition_id_for_quality = getMachineDefinitionIDForQualitySearch(global_stack)
|
||||
machine_definition_id_for_quality = getMachineDefinitionIDForQualitySearch(global_stack.definition)
|
||||
machine_definition_for_quality = self._container_registry.findDefinitionContainers(id = machine_definition_id_for_quality)[0]
|
||||
|
||||
quality_changes_info = self._machine_info.quality_changes_info
|
||||
|
@ -22,7 +22,7 @@ from PyQt5.QtCore import pyqtSlot, QUrl, pyqtSignal, pyqtProperty, QObject
|
||||
|
||||
from time import time
|
||||
from datetime import datetime
|
||||
from typing import Optional
|
||||
from typing import Optional, Dict, List
|
||||
|
||||
import json
|
||||
import os
|
||||
@ -79,7 +79,6 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
|
||||
|
||||
self._latest_reply_handler = None
|
||||
|
||||
|
||||
def requestWrite(self, nodes, file_name=None, filter_by_machine=False, file_handler=None, **kwargs):
|
||||
self.writeStarted.emit(self)
|
||||
|
||||
@ -116,7 +115,7 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
|
||||
|
||||
@pyqtSlot()
|
||||
@pyqtSlot(str)
|
||||
def sendPrintJob(self, target_printer = ""):
|
||||
def sendPrintJob(self, target_printer: str = ""):
|
||||
Logger.log("i", "Sending print job to printer.")
|
||||
if self._sending_gcode:
|
||||
self._error_message = Message(
|
||||
@ -157,11 +156,11 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
|
||||
return True
|
||||
|
||||
@pyqtProperty(QObject, notify=activePrinterChanged)
|
||||
def activePrinter(self) -> Optional["PrinterOutputModel"]:
|
||||
def activePrinter(self) -> Optional[PrinterOutputModel]:
|
||||
return self._active_printer
|
||||
|
||||
@pyqtSlot(QObject)
|
||||
def setActivePrinter(self, printer):
|
||||
def setActivePrinter(self, printer: Optional[PrinterOutputModel]):
|
||||
if self._active_printer != printer:
|
||||
if self._active_printer and self._active_printer.camera:
|
||||
self._active_printer.camera.stop()
|
||||
@ -173,7 +172,7 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
|
||||
self._compressing_gcode = False
|
||||
self._sending_gcode = False
|
||||
|
||||
def _onUploadPrintJobProgress(self, bytes_sent, bytes_total):
|
||||
def _onUploadPrintJobProgress(self, bytes_sent:int, bytes_total:int):
|
||||
if bytes_total > 0:
|
||||
new_progress = bytes_sent / bytes_total * 100
|
||||
# Treat upload progress as response. Uploading can take more than 10 seconds, so if we don't, we can get
|
||||
@ -186,7 +185,7 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
|
||||
self._progress_message.setProgress(0)
|
||||
self._progress_message.hide()
|
||||
|
||||
def _progressMessageActionTriggered(self, message_id=None, action_id=None):
|
||||
def _progressMessageActionTriggered(self, message_id: Optional[str]=None, action_id: Optional[str]=None) -> None:
|
||||
if action_id == "Abort":
|
||||
Logger.log("d", "User aborted sending print to remote.")
|
||||
self._progress_message.hide()
|
||||
@ -202,29 +201,29 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
|
||||
|
||||
|
||||
@pyqtSlot()
|
||||
def openPrintJobControlPanel(self):
|
||||
def openPrintJobControlPanel(self) -> None:
|
||||
Logger.log("d", "Opening print job control panel...")
|
||||
QDesktopServices.openUrl(QUrl("http://" + self._address + "/print_jobs"))
|
||||
|
||||
@pyqtSlot()
|
||||
def openPrinterControlPanel(self):
|
||||
def openPrinterControlPanel(self) -> None:
|
||||
Logger.log("d", "Opening printer control panel...")
|
||||
QDesktopServices.openUrl(QUrl("http://" + self._address + "/printers"))
|
||||
|
||||
@pyqtProperty("QVariantList", notify=printJobsChanged)
|
||||
def printJobs(self):
|
||||
def printJobs(self)-> List[PrintJobOutputModel] :
|
||||
return self._print_jobs
|
||||
|
||||
@pyqtProperty("QVariantList", notify=printJobsChanged)
|
||||
def queuedPrintJobs(self):
|
||||
def queuedPrintJobs(self) -> List[PrintJobOutputModel]:
|
||||
return [print_job for print_job in self._print_jobs if print_job.assignedPrinter is None or print_job.state == "queued"]
|
||||
|
||||
@pyqtProperty("QVariantList", notify=printJobsChanged)
|
||||
def activePrintJobs(self):
|
||||
def activePrintJobs(self) -> List[PrintJobOutputModel]:
|
||||
return [print_job for print_job in self._print_jobs if print_job.assignedPrinter is not None and print_job.state != "queued"]
|
||||
|
||||
@pyqtProperty("QVariantList", notify=clusterPrintersChanged)
|
||||
def connectedPrintersTypeCount(self):
|
||||
def connectedPrintersTypeCount(self) -> List[PrinterOutputModel]:
|
||||
printer_count = {}
|
||||
for printer in self._printers:
|
||||
if printer.type in printer_count:
|
||||
@ -237,22 +236,22 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
|
||||
return result
|
||||
|
||||
@pyqtSlot(int, result=str)
|
||||
def formatDuration(self, seconds):
|
||||
def formatDuration(self, seconds: int) -> str:
|
||||
return Duration(seconds).getDisplayString(DurationFormat.Format.Short)
|
||||
|
||||
@pyqtSlot(int, result=str)
|
||||
def getTimeCompleted(self, time_remaining):
|
||||
def getTimeCompleted(self, time_remaining: int) -> str:
|
||||
current_time = time()
|
||||
datetime_completed = datetime.fromtimestamp(current_time + time_remaining)
|
||||
return "{hour:02d}:{minute:02d}".format(hour=datetime_completed.hour, minute=datetime_completed.minute)
|
||||
|
||||
@pyqtSlot(int, result=str)
|
||||
def getDateCompleted(self, time_remaining):
|
||||
def getDateCompleted(self, time_remaining: int) -> str:
|
||||
current_time = time()
|
||||
datetime_completed = datetime.fromtimestamp(current_time + time_remaining)
|
||||
return (datetime_completed.strftime("%a %b ") + "{day}".format(day=datetime_completed.day)).upper()
|
||||
|
||||
def _printJobStateChanged(self):
|
||||
def _printJobStateChanged(self) -> None:
|
||||
username = self._getUserName()
|
||||
|
||||
if username is None:
|
||||
@ -275,13 +274,13 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
|
||||
# Keep a list of all completed jobs so we know if something changed next time.
|
||||
self._finished_jobs = finished_jobs
|
||||
|
||||
def _update(self):
|
||||
def _update(self) -> None:
|
||||
if not super()._update():
|
||||
return
|
||||
self.get("printers/", onFinished=self._onGetPrintersDataFinished)
|
||||
self.get("print_jobs/", onFinished=self._onGetPrintJobsFinished)
|
||||
|
||||
def _onGetPrintJobsFinished(self, reply: QNetworkReply):
|
||||
def _onGetPrintJobsFinished(self, reply: QNetworkReply) -> None:
|
||||
if not checkValidGetReply(reply):
|
||||
return
|
||||
|
||||
@ -323,7 +322,7 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
|
||||
if job_list_changed:
|
||||
self.printJobsChanged.emit() # Do a single emit for all print job changes.
|
||||
|
||||
def _onGetPrintersDataFinished(self, reply: QNetworkReply):
|
||||
def _onGetPrintersDataFinished(self, reply: QNetworkReply) -> None:
|
||||
if not checkValidGetReply(reply):
|
||||
return
|
||||
|
||||
@ -352,31 +351,37 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
|
||||
if removed_printers or printer_list_changed:
|
||||
self.printersChanged.emit()
|
||||
|
||||
def _createPrinterModel(self, data):
|
||||
def _createPrinterModel(self, data: Dict) -> PrinterOutputModel:
|
||||
printer = PrinterOutputModel(output_controller=ClusterUM3PrinterOutputController(self),
|
||||
number_of_extruders=self._number_of_extruders)
|
||||
printer.setCamera(NetworkCamera("http://" + data["ip_address"] + ":8080/?action=stream"))
|
||||
self._printers.append(printer)
|
||||
return printer
|
||||
|
||||
def _createPrintJobModel(self, data):
|
||||
def _createPrintJobModel(self, data: Dict) -> PrintJobOutputModel:
|
||||
print_job = PrintJobOutputModel(output_controller=ClusterUM3PrinterOutputController(self),
|
||||
key=data["uuid"], name= data["name"])
|
||||
print_job.stateChanged.connect(self._printJobStateChanged)
|
||||
self._print_jobs.append(print_job)
|
||||
return print_job
|
||||
|
||||
def _updatePrintJob(self, print_job, data):
|
||||
def _updatePrintJob(self, print_job: PrintJobOutputModel, data: Dict) -> None:
|
||||
print_job.updateTimeTotal(data["time_total"])
|
||||
print_job.updateTimeElapsed(data["time_elapsed"])
|
||||
print_job.updateState(data["status"])
|
||||
print_job.updateOwner(data["owner"])
|
||||
|
||||
def _updatePrinter(self, printer, data):
|
||||
def _updatePrinter(self, printer: PrinterOutputModel, data: Dict) -> None:
|
||||
# For some unknown reason the cluster wants UUID for everything, except for sending a job directly to a printer.
|
||||
# Then we suddenly need the unique name. So in order to not have to mess up all the other code, we save a mapping.
|
||||
self._printer_uuid_to_unique_name_mapping[data["uuid"]] = data["unique_name"]
|
||||
machine_definition = ContainerRegistry.getInstance().findDefinitionContainers(name = data["machine_variant"])[0]
|
||||
|
||||
definitions = ContainerRegistry.getInstance().findDefinitionContainers(name = data["machine_variant"])
|
||||
if not definitions:
|
||||
Logger.log("w", "Unable to find definition for machine variant %s", data["machine_variant"])
|
||||
return
|
||||
|
||||
machine_definition = definitions[0]
|
||||
|
||||
printer.updateName(data["friendly_name"])
|
||||
printer.updateKey(data["uuid"])
|
||||
@ -421,7 +426,7 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
|
||||
brand=brand, color=color, name=name)
|
||||
extruder.updateActiveMaterial(material)
|
||||
|
||||
def _removeJob(self, job):
|
||||
def _removeJob(self, job: PrintJobOutputModel):
|
||||
if job not in self._print_jobs:
|
||||
return False
|
||||
|
||||
@ -432,7 +437,7 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
|
||||
|
||||
return True
|
||||
|
||||
def _removePrinter(self, printer):
|
||||
def _removePrinter(self, printer: PrinterOutputModel):
|
||||
self._printers.remove(printer)
|
||||
if self._active_printer == printer:
|
||||
self._active_printer = None
|
||||
|
@ -22,6 +22,7 @@ class AutoDetectBaudJob(Job):
|
||||
def run(self):
|
||||
Logger.log("d", "Auto detect baud rate started.")
|
||||
timeout = 3
|
||||
tries = 2
|
||||
|
||||
programmer = Stk500v2()
|
||||
serial = None
|
||||
@ -31,36 +32,38 @@ class AutoDetectBaudJob(Job):
|
||||
except:
|
||||
programmer.close()
|
||||
|
||||
for baud_rate in self._all_baud_rates:
|
||||
Logger.log("d", "Checking {serial} if baud rate {baud_rate} works".format(serial= self._serial_port, baud_rate = baud_rate))
|
||||
for retry in range(tries):
|
||||
for baud_rate in self._all_baud_rates:
|
||||
Logger.log("d", "Checking {serial} if baud rate {baud_rate} works".format(serial= self._serial_port, baud_rate = baud_rate))
|
||||
|
||||
if serial is None:
|
||||
try:
|
||||
serial = Serial(str(self._serial_port), baud_rate, timeout = timeout, writeTimeout = timeout)
|
||||
except SerialException as e:
|
||||
Logger.logException("w", "Unable to create serial")
|
||||
continue
|
||||
else:
|
||||
# We already have a serial connection, just change the baud rate.
|
||||
try:
|
||||
serial.baudrate = baud_rate
|
||||
except:
|
||||
continue
|
||||
sleep(1.5) # Ensure that we are not talking to the boot loader. 1.5 seconds seems to be the magic number
|
||||
successful_responses = 0
|
||||
|
||||
serial.write(b"\n") # Ensure we clear out previous responses
|
||||
serial.write(b"M105\n")
|
||||
|
||||
timeout_time = time() + timeout
|
||||
|
||||
while timeout_time > time():
|
||||
line = serial.readline()
|
||||
if b"ok T:" in line:
|
||||
successful_responses += 1
|
||||
if successful_responses >= 3:
|
||||
self.setResult(baud_rate)
|
||||
return
|
||||
if serial is None:
|
||||
try:
|
||||
serial = Serial(str(self._serial_port), baud_rate, timeout = timeout, writeTimeout = timeout)
|
||||
except SerialException as e:
|
||||
Logger.logException("w", "Unable to create serial")
|
||||
continue
|
||||
else:
|
||||
# We already have a serial connection, just change the baud rate.
|
||||
try:
|
||||
serial.baudrate = baud_rate
|
||||
except:
|
||||
continue
|
||||
sleep(1.5) # Ensure that we are not talking to the boot loader. 1.5 seconds seems to be the magic number
|
||||
successful_responses = 0
|
||||
|
||||
serial.write(b"\n") # Ensure we clear out previous responses
|
||||
serial.write(b"M105\n")
|
||||
|
||||
timeout_time = time() + timeout
|
||||
|
||||
while timeout_time > time():
|
||||
line = serial.readline()
|
||||
if b"ok T:" in line:
|
||||
successful_responses += 1
|
||||
if successful_responses >= 3:
|
||||
self.setResult(baud_rate)
|
||||
return
|
||||
|
||||
serial.write(b"M105\n")
|
||||
sleep(15) # Give the printer some time to init and try again.
|
||||
self.setResult(None) # Unable to detect the correct baudrate.
|
||||
|
@ -116,7 +116,8 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
|
||||
|
||||
@pyqtSlot(str)
|
||||
def updateFirmware(self, file):
|
||||
self._firmware_location = file
|
||||
# the file path is qurl encoded.
|
||||
self._firmware_location = file.replace("file://", "")
|
||||
self.showFirmwareInterface()
|
||||
self.setFirmwareUpdateState(FirmwareUpdateState.updating)
|
||||
self._update_firmware_thread.start()
|
||||
@ -126,9 +127,11 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
|
||||
if self._connection_state != ConnectionState.closed:
|
||||
self.close()
|
||||
|
||||
hex_file = intelHex.readHex(self._firmware_location)
|
||||
if len(hex_file) == 0:
|
||||
Logger.log("e", "Unable to read provided hex file. Could not update firmware")
|
||||
try:
|
||||
hex_file = intelHex.readHex(self._firmware_location)
|
||||
assert len(hex_file) > 0
|
||||
except (FileNotFoundError, AssertionError):
|
||||
Logger.log("e", "Unable to read provided hex file. Could not update firmware.")
|
||||
self.setFirmwareUpdateState(FirmwareUpdateState.firmware_not_found_error)
|
||||
return
|
||||
|
||||
@ -198,7 +201,6 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
|
||||
# Reset line number. If this is not done, first line is sometimes ignored
|
||||
self._gcode.insert(0, "M110")
|
||||
self._gcode_position = 0
|
||||
self._is_printing = True
|
||||
self._print_start_time = time()
|
||||
|
||||
self._print_estimated_time = int(Application.getInstance().getPrintInformation().currentPrintTime.getDisplayString(DurationFormat.Format.Seconds))
|
||||
@ -206,6 +208,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
|
||||
for i in range(0, 4): # Push first 4 entries before accepting other inputs
|
||||
self._sendNextGcodeLine()
|
||||
|
||||
self._is_printing = True
|
||||
self.writeFinished.emit(self)
|
||||
|
||||
def _autoDetectFinished(self, job: AutoDetectBaudJob):
|
||||
@ -267,7 +270,6 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
|
||||
if not command.endswith(b"\n"):
|
||||
command += b"\n"
|
||||
try:
|
||||
self._serial.write(b"\n")
|
||||
self._serial.write(command)
|
||||
except SerialTimeoutException:
|
||||
Logger.log("w", "Timeout when sending command to printer via USB.")
|
||||
@ -284,7 +286,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
|
||||
self.sendCommand("M105")
|
||||
self._last_temperature_request = time()
|
||||
|
||||
if b"ok T:" in line or line.startswith(b"T:"): # Temperature message
|
||||
if b"ok T:" in line or line.startswith(b"T:") or b"ok B:" in line or line.startswith(b"B:"): # Temperature message. 'T:' for extruder and 'B:' for bed
|
||||
extruder_temperature_matches = re.findall(b"T(\d*): ?([\d\.]+) ?\/?([\d\.]+)?", line)
|
||||
# Update all temperature values
|
||||
for match, extruder in zip(extruder_temperature_matches, self._printers[0].extruders):
|
||||
@ -302,6 +304,9 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
|
||||
self._printers[0].updateTargetBedTemperature(float(match[1]))
|
||||
|
||||
if self._is_printing:
|
||||
if line.startswith(b'!!'):
|
||||
Logger.log('e', "Printer signals fatal error. Cancelling print. {}".format(line))
|
||||
self.cancelPrint()
|
||||
if b"ok" in line:
|
||||
if not self._command_queue.empty():
|
||||
self._sendCommand(self._command_queue.get())
|
||||
|
@ -56,6 +56,8 @@ _EXTRUDER_TO_POSITION = {
|
||||
## Upgrades configurations from the state they were in at version 3.2 to the
|
||||
# state they should be in at version 3.3.
|
||||
class VersionUpgrade32to33(VersionUpgrade):
|
||||
|
||||
temporary_group_name_counter = 1
|
||||
## Gets the version number from a CFG file in Uranium's 3.2 format.
|
||||
#
|
||||
# Since the format may change, this is implemented for the 3.2 format only
|
||||
@ -74,6 +76,28 @@ class VersionUpgrade32to33(VersionUpgrade):
|
||||
setting_version = int(parser.get("metadata", "setting_version", fallback = 0))
|
||||
return format_version * 1000000 + setting_version
|
||||
|
||||
## Upgrades a container stack from version 3.2 to 3.3.
|
||||
#
|
||||
# \param serialised The serialised form of a container stack.
|
||||
# \param filename The name of the file to upgrade.
|
||||
def upgradeStack(self, serialized, filename):
|
||||
parser = configparser.ConfigParser(interpolation = None)
|
||||
parser.read_string(serialized)
|
||||
|
||||
if "metadata" in parser and "um_network_key" in parser["metadata"]:
|
||||
if "hidden" not in parser["metadata"]:
|
||||
parser["metadata"]["hidden"] = "False"
|
||||
if "connect_group_name" not in parser["metadata"]:
|
||||
parser["metadata"]["connect_group_name"] = "Temporary group name #" + str(self.temporary_group_name_counter)
|
||||
self.temporary_group_name_counter += 1
|
||||
|
||||
#Update version number.
|
||||
parser["general"]["version"] = "4"
|
||||
|
||||
result = io.StringIO()
|
||||
parser.write(result)
|
||||
return [filename], [result.getvalue()]
|
||||
|
||||
## Upgrades non-quality-changes instance containers to have the new version
|
||||
# number.
|
||||
def upgradeInstanceContainer(self, serialized, filename):
|
||||
|
@ -9,11 +9,22 @@ def getMetaData():
|
||||
return {
|
||||
"version_upgrade": {
|
||||
# From To Upgrade function
|
||||
("machine_stack", 3000004): ("machine_stack", 4000004, upgrade.upgradeStack),
|
||||
("extruder_train", 3000004): ("extruder_train", 4000004, upgrade.upgradeStack),
|
||||
|
||||
("definition_changes", 2000004): ("definition_changes", 3000004, upgrade.upgradeInstanceContainer),
|
||||
("quality_changes", 2000004): ("quality_changes", 3000004, upgrade.upgradeQualityChanges),
|
||||
("user", 2000004): ("user", 3000004, upgrade.upgradeInstanceContainer)
|
||||
},
|
||||
"sources": {
|
||||
"machine_stack": {
|
||||
"get_version": upgrade.getCfgVersion,
|
||||
"location": {"./machine_instances"}
|
||||
},
|
||||
"extruder_train": {
|
||||
"get_version": upgrade.getCfgVersion,
|
||||
"location": {"./extruders"}
|
||||
},
|
||||
"definition_changes": {
|
||||
"get_version": upgrade.getCfgVersion,
|
||||
"location": {"./definition_changes"}
|
||||
|
@ -653,7 +653,10 @@ UM.MainWindow
|
||||
{
|
||||
preferences.visible = true;
|
||||
preferences.setPage(1);
|
||||
preferences.getCurrentItem().scrollToSection(source.key);
|
||||
if(source && source.key)
|
||||
{
|
||||
preferences.getCurrentItem().scrollToSection(source.key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -12,7 +12,7 @@ import "Menus"
|
||||
|
||||
ToolButton {
|
||||
id: base
|
||||
property var isNetworkPrinter: Cura.MachineManager.activeMachineNetworkKey != ""
|
||||
property bool isNetworkPrinter: Cura.MachineManager.activeMachineNetworkKey != ""
|
||||
property var printerStatus: Cura.MachineManager.printerOutputDevices.length != 0 ? "connected" : "disconnected"
|
||||
text: isNetworkPrinter ? Cura.MachineManager.activeMachineNetworkGroupName : Cura.MachineManager.activeMachineName
|
||||
|
||||
|
@ -59,13 +59,14 @@ Column
|
||||
section.criteria: ViewSection.FullString
|
||||
section.delegate: sectionHeading
|
||||
|
||||
model: (ouputDevice != null) ? outputDevice.uniqueConfigurations : []
|
||||
model: (outputDevice != null) ? outputDevice.uniqueConfigurations : []
|
||||
delegate: ConfigurationItem
|
||||
{
|
||||
width: parent.width - UM.Theme.getSize("default_margin").width
|
||||
configuration: modelData
|
||||
onActivateConfiguration:
|
||||
{
|
||||
switchPopupState()
|
||||
Cura.MachineManager.applyRemoteConfiguration(configuration)
|
||||
}
|
||||
}
|
||||
|
@ -13,54 +13,54 @@ Item
|
||||
id: configurationSelector
|
||||
property var connectedDevice: Cura.MachineManager.printerOutputDevices.length >= 1 ? Cura.MachineManager.printerOutputDevices[0] : null
|
||||
property var panelWidth: control.width
|
||||
property var panelVisible: false
|
||||
|
||||
SyncButton {
|
||||
onClicked: configurationSelector.state == "open" ? configurationSelector.state = "closed" : configurationSelector.state = "open"
|
||||
function switchPopupState()
|
||||
{
|
||||
popup.opened ? popup.close() : popup.open()
|
||||
}
|
||||
|
||||
SyncButton
|
||||
{
|
||||
id: syncButton
|
||||
onClicked: switchPopupState()
|
||||
outputDevice: connectedDevice
|
||||
}
|
||||
|
||||
Popup {
|
||||
Popup
|
||||
{
|
||||
// TODO Change once updating to Qt5.10 - This property is already in 5.10 but is manually implemented until upgrade
|
||||
property bool opened: false
|
||||
id: popup
|
||||
clip: true
|
||||
closePolicy: Popup.CloseOnPressOutsideParent
|
||||
y: configurationSelector.height - UM.Theme.getSize("default_lining").height
|
||||
x: configurationSelector.width - width
|
||||
width: panelWidth
|
||||
visible: panelVisible && connectedDevice != null
|
||||
visible: opened
|
||||
padding: UM.Theme.getSize("default_lining").width
|
||||
contentItem: ConfigurationListView {
|
||||
transformOrigin: Popup.Top
|
||||
contentItem: ConfigurationListView
|
||||
{
|
||||
id: configList
|
||||
width: panelWidth - 2 * popup.padding
|
||||
outputDevice: connectedDevice
|
||||
}
|
||||
background: Rectangle {
|
||||
background: Rectangle
|
||||
{
|
||||
color: UM.Theme.getColor("setting_control")
|
||||
border.color: UM.Theme.getColor("setting_control_border")
|
||||
}
|
||||
}
|
||||
|
||||
states: [
|
||||
// This adds a second state to the container where the rectangle is farther to the right
|
||||
State {
|
||||
name: "open"
|
||||
PropertyChanges {
|
||||
target: popup
|
||||
height: configList.computedHeight
|
||||
}
|
||||
},
|
||||
State {
|
||||
name: "closed"
|
||||
PropertyChanges {
|
||||
target: popup
|
||||
height: 0
|
||||
}
|
||||
}
|
||||
]
|
||||
transitions: [
|
||||
// This adds a transition that defaults to applying to all state changes
|
||||
Transition {
|
||||
exit: Transition
|
||||
{
|
||||
// This applies a default NumberAnimation to any changes a state change makes to x or y properties
|
||||
NumberAnimation { properties: "height"; duration: 200; easing.type: Easing.InOutQuad; }
|
||||
NumberAnimation { property: "visible"; duration: 75; }
|
||||
}
|
||||
]
|
||||
enter: Transition
|
||||
{
|
||||
// This applies a default NumberAnimation to any changes a state change makes to x or y properties
|
||||
NumberAnimation { property: "visible"; duration: 75; }
|
||||
}
|
||||
onClosed: opened = false
|
||||
onOpened: opened = true
|
||||
}
|
||||
}
|
@ -17,11 +17,15 @@ Button
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
|
||||
function updateOnSync() {
|
||||
if (outputDevice != undefined) {
|
||||
for (var index in outputDevice.uniqueConfigurations) {
|
||||
function updateOnSync()
|
||||
{
|
||||
if (outputDevice != undefined)
|
||||
{
|
||||
for (var index in outputDevice.uniqueConfigurations)
|
||||
{
|
||||
var configuration = outputDevice.uniqueConfigurations[index]
|
||||
if (Cura.MachineManager.matchesConfiguration(configuration)) {
|
||||
if (Cura.MachineManager.matchesConfiguration(configuration))
|
||||
{
|
||||
base.matched = true;
|
||||
return;
|
||||
}
|
||||
@ -82,11 +86,6 @@ Button
|
||||
label: Label {}
|
||||
}
|
||||
|
||||
onClicked:
|
||||
{
|
||||
panelVisible = !panelVisible
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: outputDevice
|
||||
onUniqueConfigurationsChanged: {
|
||||
|
82
resources/qml/Menus/SettingVisibilityPresetsMenu.qml
Normal file
82
resources/qml/Menus/SettingVisibilityPresetsMenu.qml
Normal file
@ -0,0 +1,82 @@
|
||||
// Copyright (c) 2018 Ultimaker B.V.
|
||||
// Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import QtQuick 2.2
|
||||
import QtQuick.Controls 1.1
|
||||
|
||||
import UM 1.2 as UM
|
||||
import Cura 1.0 as Cura
|
||||
|
||||
Menu
|
||||
{
|
||||
id: menu
|
||||
title: catalog.i18nc("@action:inmenu", "Visible Settings")
|
||||
|
||||
property bool showingSearchResults
|
||||
property bool showingAllSettings
|
||||
|
||||
signal showAllSettings()
|
||||
signal showSettingVisibilityProfile()
|
||||
|
||||
MenuItem
|
||||
{
|
||||
text: catalog.i18nc("@action:inmenu", "Custom selection")
|
||||
checkable: true
|
||||
checked: !showingSearchResults && !showingAllSettings && Cura.SettingVisibilityPresetsModel.activePreset == "custom"
|
||||
exclusiveGroup: group
|
||||
onTriggered:
|
||||
{
|
||||
Cura.SettingVisibilityPresetsModel.setActivePreset("custom");
|
||||
// Restore custom set from preference
|
||||
UM.Preferences.setValue("general/visible_settings", UM.Preferences.getValue("cura/custom_visible_settings"));
|
||||
showSettingVisibilityProfile();
|
||||
}
|
||||
}
|
||||
MenuSeparator { }
|
||||
|
||||
Instantiator
|
||||
{
|
||||
model: Cura.SettingVisibilityPresetsModel
|
||||
|
||||
MenuItem
|
||||
{
|
||||
text: model.name
|
||||
checkable: true
|
||||
checked: model.id == Cura.SettingVisibilityPresetsModel.activePreset
|
||||
exclusiveGroup: group
|
||||
onTriggered:
|
||||
{
|
||||
Cura.SettingVisibilityPresetsModel.setActivePreset(model.id);
|
||||
|
||||
UM.Preferences.setValue("general/visible_settings", model.settings.join(";"));
|
||||
|
||||
showSettingVisibilityProfile();
|
||||
}
|
||||
}
|
||||
|
||||
onObjectAdded: menu.insertItem(index, object)
|
||||
onObjectRemoved: menu.removeItem(object)
|
||||
}
|
||||
|
||||
MenuSeparator {}
|
||||
MenuItem
|
||||
{
|
||||
text: catalog.i18nc("@action:inmenu", "All Settings")
|
||||
checkable: true
|
||||
checked: showingAllSettings
|
||||
exclusiveGroup: group
|
||||
onTriggered:
|
||||
{
|
||||
showAllSettings();
|
||||
}
|
||||
}
|
||||
MenuSeparator {}
|
||||
MenuItem
|
||||
{
|
||||
text: catalog.i18nc("@action:inmenu", "Manage Setting Visibility...")
|
||||
iconName: "configure"
|
||||
onTriggered: Cura.Actions.configureSettingVisibility.trigger()
|
||||
}
|
||||
|
||||
ExclusiveGroup { id: group }
|
||||
}
|
@ -26,8 +26,8 @@ UM.PreferencesPage
|
||||
UM.Preferences.resetPreference("general/visible_settings")
|
||||
|
||||
// After calling this function update Setting visibility preset combobox.
|
||||
// Reset should set "Basic" setting preset
|
||||
visibilityPreset.setBasicPreset()
|
||||
// Reset should set default setting preset ("Basic")
|
||||
visibilityPreset.setDefaultPreset()
|
||||
|
||||
}
|
||||
resetEnabled: true;
|
||||
@ -37,6 +37,8 @@ UM.PreferencesPage
|
||||
id: base;
|
||||
anchors.fill: parent;
|
||||
|
||||
property bool inhibitSwitchToCustom: false
|
||||
|
||||
CheckBox
|
||||
{
|
||||
id: toggleVisibleSettings
|
||||
@ -84,7 +86,7 @@ UM.PreferencesPage
|
||||
if (visibilityPreset.currentIndex != visibilityPreset.model.count - 1)
|
||||
{
|
||||
visibilityPreset.currentIndex = visibilityPreset.model.count - 1
|
||||
UM.Preferences.setValue("general/preset_setting_visibility_choice", visibilityPreset.model.get(visibilityPreset.currentIndex).text)
|
||||
UM.Preferences.setValue("cura/active_setting_visibility_preset", visibilityPreset.model.getItem(visibilityPreset.currentIndex).id)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -110,25 +112,13 @@ UM.PreferencesPage
|
||||
|
||||
ComboBox
|
||||
{
|
||||
property int customOptionValue: 100
|
||||
|
||||
function setBasicPreset()
|
||||
function setDefaultPreset()
|
||||
{
|
||||
var index = 0
|
||||
for(var i = 0; i < presetNamesList.count; ++i)
|
||||
{
|
||||
if(model.get(i).text == "Basic")
|
||||
{
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
visibilityPreset.currentIndex = index
|
||||
visibilityPreset.currentIndex = 0
|
||||
}
|
||||
|
||||
id: visibilityPreset
|
||||
width: 150
|
||||
width: 150 * screenScaleFactor
|
||||
anchors
|
||||
{
|
||||
top: parent.top
|
||||
@ -137,56 +127,49 @@ UM.PreferencesPage
|
||||
|
||||
model: ListModel
|
||||
{
|
||||
id: presetNamesList
|
||||
id: visibilityPresetsModel
|
||||
Component.onCompleted:
|
||||
{
|
||||
// returned value is Dictionary (Ex: {1:"Basic"}, The number 1 is the weight and sort by weight)
|
||||
var itemsDict = UM.Preferences.getValue("general/visible_settings_preset")
|
||||
var sorted = [];
|
||||
for(var key in itemsDict) {
|
||||
sorted[sorted.length] = key;
|
||||
}
|
||||
visibilityPresetsModel.append({text: catalog.i18nc("@action:inmenu", "Custom selection"), id: "custom"});
|
||||
|
||||
sorted.sort();
|
||||
for(var i = 0; i < sorted.length; i++) {
|
||||
presetNamesList.append({text: itemsDict[sorted[i]], value: i});
|
||||
var presets = Cura.SettingVisibilityPresetsModel;
|
||||
for(var i = 0; i < presets.rowCount(); i++)
|
||||
{
|
||||
visibilityPresetsModel.append({text: presets.getItem(i)["name"], id: presets.getItem(i)["id"]});
|
||||
}
|
||||
|
||||
// By agreement lets "Custom" option will have value 100
|
||||
presetNamesList.append({text: "Custom", value: visibilityPreset.customOptionValue});
|
||||
}
|
||||
}
|
||||
|
||||
currentIndex:
|
||||
{
|
||||
// Load previously selected preset.
|
||||
var text = UM.Preferences.getValue("general/preset_setting_visibility_choice");
|
||||
|
||||
|
||||
|
||||
var index = 0;
|
||||
for(var i = 0; i < presetNamesList.count; ++i)
|
||||
var index = Cura.SettingVisibilityPresetsModel.find("id", Cura.SettingVisibilityPresetsModel.activePreset);
|
||||
if(index == -1)
|
||||
{
|
||||
if(model.get(i).text == text)
|
||||
{
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
return index;
|
||||
|
||||
return index + 1; // "Custom selection" entry is added in front, so index is off by 1
|
||||
}
|
||||
|
||||
onActivated:
|
||||
{
|
||||
// TODO What to do if user is selected "Custom from Combobox" ?
|
||||
if (model.get(index).text == "Custom"){
|
||||
UM.Preferences.setValue("general/preset_setting_visibility_choice", model.get(index).text)
|
||||
return
|
||||
}
|
||||
base.inhibitSwitchToCustom = true;
|
||||
var preset_id = visibilityPresetsModel.get(index).id;
|
||||
Cura.SettingVisibilityPresetsModel.setActivePreset(preset_id);
|
||||
|
||||
var newVisibleSettings = CuraApplication.getVisibilitySettingPreset(model.get(index).text)
|
||||
UM.Preferences.setValue("general/visible_settings", newVisibleSettings)
|
||||
UM.Preferences.setValue("general/preset_setting_visibility_choice", model.get(index).text)
|
||||
UM.Preferences.setValue("cura/active_setting_visibility_preset", preset_id);
|
||||
if (preset_id != "custom")
|
||||
{
|
||||
UM.Preferences.setValue("general/visible_settings", Cura.SettingVisibilityPresetsModel.getItem(index - 1).settings.join(";"));
|
||||
// "Custom selection" entry is added in front, so index is off by 1
|
||||
}
|
||||
else
|
||||
{
|
||||
// Restore custom set from preference
|
||||
UM.Preferences.setValue("general/visible_settings", UM.Preferences.getValue("cura/custom_visible_settings"));
|
||||
}
|
||||
base.inhibitSwitchToCustom = false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -216,7 +199,16 @@ UM.PreferencesPage
|
||||
exclude: ["machine_settings", "command_line_settings"]
|
||||
showAncestors: true
|
||||
expanded: ["*"]
|
||||
visibilityHandler: UM.SettingPreferenceVisibilityHandler { }
|
||||
visibilityHandler: UM.SettingPreferenceVisibilityHandler
|
||||
{
|
||||
onVisibilityChanged:
|
||||
{
|
||||
if(Cura.SettingVisibilityPresetsModel.activePreset != "" && !base.inhibitSwitchToCustom)
|
||||
{
|
||||
Cura.SettingVisibilityPresetsModel.setActivePreset("custom");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
delegate: Loader
|
||||
@ -259,19 +251,7 @@ UM.PreferencesPage
|
||||
{
|
||||
id: settingVisibilityItem;
|
||||
|
||||
UM.SettingVisibilityItem {
|
||||
|
||||
// after changing any visibility of settings, set the preset to the "Custom" option
|
||||
visibilityChangeCallback : function()
|
||||
{
|
||||
// If already "Custom" then don't do nothing
|
||||
if (visibilityPreset.currentIndex != visibilityPreset.model.count - 1)
|
||||
{
|
||||
visibilityPreset.currentIndex = visibilityPreset.model.count - 1
|
||||
UM.Preferences.setValue("general/preset_setting_visibility_choice", visibilityPreset.model.get(visibilityPreset.currentIndex).text)
|
||||
}
|
||||
}
|
||||
}
|
||||
UM.SettingVisibilityItem { }
|
||||
}
|
||||
}
|
||||
}
|
@ -15,10 +15,11 @@ Item
|
||||
{
|
||||
id: base;
|
||||
|
||||
property Action configureSettings;
|
||||
property bool findingSettings;
|
||||
signal showTooltip(Item item, point location, string text);
|
||||
signal hideTooltip();
|
||||
property Action configureSettings
|
||||
property bool findingSettings
|
||||
property bool showingAllSettings
|
||||
signal showTooltip(Item item, point location, string text)
|
||||
signal hideTooltip()
|
||||
|
||||
Item
|
||||
{
|
||||
@ -107,6 +108,57 @@ Item
|
||||
}
|
||||
}
|
||||
|
||||
ToolButton
|
||||
{
|
||||
id: settingVisibilityMenu
|
||||
|
||||
width: height
|
||||
height: UM.Theme.getSize("setting_control").height
|
||||
anchors
|
||||
{
|
||||
top: globalProfileRow.bottom
|
||||
topMargin: UM.Theme.getSize("sidebar_margin").height
|
||||
right: parent.right
|
||||
rightMargin: UM.Theme.getSize("sidebar_margin").width
|
||||
}
|
||||
style: ButtonStyle
|
||||
{
|
||||
background: Item {
|
||||
UM.RecolorImage {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
width: UM.Theme.getSize("standard_arrow").width
|
||||
height: UM.Theme.getSize("standard_arrow").height
|
||||
sourceSize.width: width
|
||||
sourceSize.height: width
|
||||
color: control.enabled ? UM.Theme.getColor("setting_category_text") : UM.Theme.getColor("setting_category_disabled_text")
|
||||
source: UM.Theme.getIcon("menu")
|
||||
}
|
||||
}
|
||||
label: Label{}
|
||||
}
|
||||
menu: SettingVisibilityPresetsMenu
|
||||
{
|
||||
showingSearchResults: findingSettings
|
||||
showingAllSettings: showingAllSettings
|
||||
|
||||
onShowAllSettings:
|
||||
{
|
||||
base.showingAllSettings = true;
|
||||
base.findingSettings = false;
|
||||
filter.text = "";
|
||||
filter.updateDefinitionModel();
|
||||
}
|
||||
onShowSettingVisibilityProfile:
|
||||
{
|
||||
base.showingAllSettings = false;
|
||||
base.findingSettings = false;
|
||||
filter.text = "";
|
||||
filter.updateDefinitionModel();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle
|
||||
{
|
||||
id: filterContainer
|
||||
@ -132,9 +184,9 @@ Item
|
||||
top: globalProfileRow.bottom
|
||||
topMargin: UM.Theme.getSize("sidebar_margin").height
|
||||
left: parent.left
|
||||
leftMargin: Math.round(UM.Theme.getSize("sidebar_margin").width)
|
||||
right: parent.right
|
||||
rightMargin: Math.round(UM.Theme.getSize("sidebar_margin").width)
|
||||
leftMargin: UM.Theme.getSize("sidebar_margin").width
|
||||
right: settingVisibilityMenu.left
|
||||
rightMargin: Math.floor(UM.Theme.getSize("default_margin").width / 2)
|
||||
}
|
||||
height: visible ? UM.Theme.getSize("setting_control").height : 0
|
||||
Behavior on height { NumberAnimation { duration: 100 } }
|
||||
@ -168,17 +220,9 @@ Item
|
||||
{
|
||||
if(findingSettings)
|
||||
{
|
||||
expandedCategories = definitionsModel.expanded.slice();
|
||||
definitionsModel.expanded = ["*"];
|
||||
definitionsModel.showAncestors = true;
|
||||
definitionsModel.showAll = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
definitionsModel.expanded = expandedCategories;
|
||||
definitionsModel.showAncestors = false;
|
||||
definitionsModel.showAll = false;
|
||||
showingAllSettings = false;
|
||||
}
|
||||
updateDefinitionModel();
|
||||
lastFindingSettings = findingSettings;
|
||||
}
|
||||
}
|
||||
@ -187,6 +231,27 @@ Item
|
||||
{
|
||||
filter.text = "";
|
||||
}
|
||||
|
||||
function updateDefinitionModel()
|
||||
{
|
||||
if(findingSettings || showingAllSettings)
|
||||
{
|
||||
expandedCategories = definitionsModel.expanded.slice();
|
||||
definitionsModel.expanded = [""]; // keep categories closed while to prevent render while making settings visible one by one
|
||||
definitionsModel.showAncestors = true;
|
||||
definitionsModel.showAll = true;
|
||||
definitionsModel.expanded = ["*"];
|
||||
}
|
||||
else
|
||||
{
|
||||
if(expandedCategories)
|
||||
{
|
||||
definitionsModel.expanded = expandedCategories;
|
||||
}
|
||||
definitionsModel.showAncestors = false;
|
||||
definitionsModel.showAll = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea
|
||||
@ -209,7 +274,7 @@ Item
|
||||
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: Math.round(UM.Theme.getSize("sidebar_margin").width)
|
||||
anchors.rightMargin: UM.Theme.getSize("default_margin").width
|
||||
|
||||
color: UM.Theme.getColor("setting_control_button")
|
||||
hoverColor: UM.Theme.getColor("setting_control_button_hover")
|
||||
@ -491,9 +556,17 @@ Item
|
||||
MenuItem
|
||||
{
|
||||
//: Settings context menu action
|
||||
visible: !findingSettings;
|
||||
visible: !(findingSettings || showingAllSettings);
|
||||
text: catalog.i18nc("@action:menu", "Hide this setting");
|
||||
onTriggered: definitionsModel.hide(contextMenu.key);
|
||||
onTriggered:
|
||||
{
|
||||
definitionsModel.hide(contextMenu.key);
|
||||
// visible settings have changed, so we're no longer showing a preset
|
||||
if (Cura.SettingVisibilityPresetsModel.activePreset != "" && !showingAllSettings)
|
||||
{
|
||||
Cura.SettingVisibilityPresetsModel.setActivePreset("custom");
|
||||
}
|
||||
}
|
||||
}
|
||||
MenuItem
|
||||
{
|
||||
@ -509,7 +582,7 @@ Item
|
||||
return catalog.i18nc("@action:menu", "Keep this setting visible");
|
||||
}
|
||||
}
|
||||
visible: findingSettings;
|
||||
visible: (findingSettings || showingAllSettings);
|
||||
onTriggered:
|
||||
{
|
||||
if (contextMenu.settingVisible)
|
||||
@ -520,6 +593,11 @@ Item
|
||||
{
|
||||
definitionsModel.show(contextMenu.key);
|
||||
}
|
||||
// visible settings have changed, so we're no longer showing a preset
|
||||
if (Cura.SettingVisibilityPresetsModel.activePreset != "" && !showingAllSettings)
|
||||
{
|
||||
Cura.SettingVisibilityPresetsModel.setActivePreset("custom");
|
||||
}
|
||||
}
|
||||
}
|
||||
MenuItem
|
||||
|
@ -19,6 +19,7 @@ Rectangle
|
||||
property bool hideView: Cura.MachineManager.activeMachineName == ""
|
||||
|
||||
// Is there an output device for this printer?
|
||||
property bool isNetworkPrinter: Cura.MachineManager.activeMachineNetworkKey != ""
|
||||
property bool printerConnected: Cura.MachineManager.printerOutputDevices.length != 0
|
||||
property bool printerAcceptsCommands: printerConnected && Cura.MachineManager.printerOutputDevices[0].acceptsCommands
|
||||
property var connectedPrinter: Cura.MachineManager.printerOutputDevices.length >= 1 ? Cura.MachineManager.printerOutputDevices[0] : null
|
||||
@ -106,7 +107,7 @@ Rectangle
|
||||
|
||||
ConfigurationSelection {
|
||||
id: configSelection
|
||||
visible: printerConnected && !sidebar.monitoringPrint && !sidebar.hideSettings
|
||||
visible: isNetworkPrinter && !sidebar.monitoringPrint && !sidebar.hideSettings
|
||||
width: visible ? Math.round(base.width * 0.15) : 0
|
||||
height: UM.Theme.getSize("sidebar_header").height
|
||||
anchors.top: base.top
|
||||
|
@ -473,6 +473,74 @@ Column
|
||||
}
|
||||
}
|
||||
|
||||
// Material info row
|
||||
Item
|
||||
{
|
||||
id: materialInfoRow
|
||||
height: Math.round(UM.Theme.getSize("sidebar_setup").height / 2)
|
||||
visible: (Cura.MachineManager.hasVariants || Cura.MachineManager.hasMaterials) && !sidebar.monitoringPrint && !sidebar.hideSettings
|
||||
|
||||
anchors
|
||||
{
|
||||
left: parent.left
|
||||
leftMargin: UM.Theme.getSize("sidebar_margin").width
|
||||
right: parent.right
|
||||
rightMargin: UM.Theme.getSize("sidebar_margin").width
|
||||
}
|
||||
|
||||
Item {
|
||||
height: UM.Theme.getSize("sidebar_setup").height
|
||||
anchors.right: parent.right
|
||||
width: Math.round(parent.width * 0.7 + UM.Theme.getSize("sidebar_margin").width)
|
||||
|
||||
UM.RecolorImage {
|
||||
id: warningImage
|
||||
anchors.right: materialInfoLabel.left
|
||||
anchors.rightMargin: UM.Theme.getSize("default_margin").width
|
||||
anchors.verticalCenter: parent.Bottom
|
||||
source: UM.Theme.getIcon("warning")
|
||||
width: UM.Theme.getSize("section_icon").width
|
||||
height: UM.Theme.getSize("section_icon").height
|
||||
color: UM.Theme.getColor("material_compatibility_warning")
|
||||
visible: !Cura.MachineManager.isCurrentSetupSupported
|
||||
}
|
||||
|
||||
Label {
|
||||
id: materialInfoLabel
|
||||
wrapMode: Text.WordWrap
|
||||
text: "<a href='%1'>" + catalog.i18nc("@label", "Check compatibility") + "</a>"
|
||||
font: UM.Theme.getFont("default")
|
||||
color: UM.Theme.getColor("text")
|
||||
linkColor: UM.Theme.getColor("text_link")
|
||||
verticalAlignment: Text.AlignTop
|
||||
anchors.top: parent.top
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: parent.bottom
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
onClicked: {
|
||||
// open the material URL with web browser
|
||||
var version = UM.Application.version;
|
||||
var machineName = Cura.MachineManager.activeMachine.definition.id;
|
||||
var url = "https://ultimaker.com/materialcompatibility/" + version + "/" + machineName + "?utm_source=cura&utm_medium=software&utm_campaign=resources";
|
||||
Qt.openUrlExternally(url);
|
||||
}
|
||||
onEntered: {
|
||||
var content = catalog.i18nc("@tooltip", "Click to check the material compatibility on Ultimaker.com.");
|
||||
base.showTooltip(
|
||||
materialInfoRow,
|
||||
Qt.point(-UM.Theme.getSize("sidebar_margin").width, 0),
|
||||
catalog.i18nc("@tooltip", content)
|
||||
);
|
||||
}
|
||||
onExited: base.hideTooltip();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
UM.SettingPropertyProvider
|
||||
{
|
||||
id: machineExtruderCount
|
||||
|
@ -243,6 +243,81 @@ Item
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: UM.Theme.getSize("sidebar_margin").height
|
||||
|
||||
// This Item is used only for tooltip, for slider area which is unavailable
|
||||
Item
|
||||
{
|
||||
function showTooltip (showTooltip)
|
||||
{
|
||||
if (showTooltip) {
|
||||
var content = catalog.i18nc("@tooltip", "This quality profile is not available for you current material and nozzle configuration. Please change these to enable this quality profile")
|
||||
base.showTooltip(qualityRow, Qt.point(-UM.Theme.getSize("sidebar_margin").width, customisedSettings.height), content)
|
||||
}
|
||||
else {
|
||||
base.hideTooltip()
|
||||
}
|
||||
}
|
||||
|
||||
id: unavailableLineToolTip
|
||||
height: 20 // hovered area height
|
||||
z: parent.z + 1 // should be higher, otherwise the area can be hovered
|
||||
x: 0
|
||||
anchors.verticalCenter: qualitySlider.verticalCenter
|
||||
|
||||
Rectangle
|
||||
{
|
||||
id: leftArea
|
||||
width:
|
||||
{
|
||||
if (qualityModel.availableTotalTicks == 0) {
|
||||
return qualityModel.qualitySliderStepWidth * qualityModel.totalTicks
|
||||
}
|
||||
return qualityModel.qualitySliderStepWidth * qualityModel.qualitySliderAvailableMin - 10
|
||||
}
|
||||
height: parent.height
|
||||
color: "transparent"
|
||||
|
||||
MouseArea
|
||||
{
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
enabled: Cura.SimpleModeSettingsManager.isProfileUserCreated == false
|
||||
onEntered: unavailableLineToolTip.showTooltip(true)
|
||||
onExited: unavailableLineToolTip.showTooltip(false)
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle
|
||||
{
|
||||
id: rightArea
|
||||
width: {
|
||||
if(qualityModel.availableTotalTicks == 0)
|
||||
return 0
|
||||
|
||||
return qualityModel.qualitySliderMarginRight - 10
|
||||
}
|
||||
height: parent.height
|
||||
color: "transparent"
|
||||
x: {
|
||||
if (qualityModel.availableTotalTicks == 0) {
|
||||
return 0
|
||||
}
|
||||
|
||||
var leftUnavailableArea = qualityModel.qualitySliderStepWidth * qualityModel.qualitySliderAvailableMin
|
||||
var totalGap = qualityModel.qualitySliderStepWidth * (qualityModel.availableTotalTicks -1) + leftUnavailableArea + 10
|
||||
|
||||
return totalGap
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
enabled: Cura.SimpleModeSettingsManager.isProfileUserCreated == false
|
||||
onEntered: unavailableLineToolTip.showTooltip(true)
|
||||
onExited: unavailableLineToolTip.showTooltip(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Draw Unavailable line
|
||||
Rectangle
|
||||
{
|
||||
|
3
resources/themes/cura-light/icons/menu.svg
Normal file
3
resources/themes/cura-light/icons/menu.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 30 30">
|
||||
<path d="m 30,23.75 v 2.5 q 0,0.50781 -0.37109,0.87891 Q 29.25781,27.5 28.75,27.5 H 1.25 Q 0.74219,27.5 0.37109,27.12891 0,26.75781 0,26.25 v -2.5 Q 0,23.24219 0.37109,22.87109 0.74219,22.5 1.25,22.5 h 27.5 q 0.50781,0 0.87891,0.37109 Q 30,23.24219 30,23.75 Z m 0,-10 v 2.5 q 0,0.50781 -0.37109,0.87891 Q 29.25781,17.5 28.75,17.5 H 1.25 Q 0.74219,17.5 0.37109,17.12891 0,16.75781 0,16.25 v -2.5 Q 0,13.24219 0.37109,12.87109 0.74219,12.5 1.25,12.5 h 27.5 q 0.50781,0 0.87891,0.37109 Q 30,13.24219 30,13.75 Z m 0,-10 v 2.5 Q 30,6.75781 29.62891,7.12891 29.25781,7.5 28.75,7.5 H 1.25 Q 0.74219,7.5 0.37109,7.12891 0,6.75781 0,6.25 V 3.75 Q 0,3.24219 0.37109,2.87109 0.74219,2.5 1.25,2.5 h 27.5 q 0.50781,0 0.87891,0.37109 Q 30,3.24219 30,3.75 Z" />
|
||||
</svg>
|
After Width: | Height: | Size: 817 B |
@ -56,7 +56,6 @@ retraction_amount = 4.5
|
||||
retraction_count_max = 15
|
||||
retraction_extrusion_window = =retraction_amount
|
||||
retraction_hop = 2
|
||||
retraction_hop_enabled = True
|
||||
retraction_hop_only_when_collides = True
|
||||
retraction_min_travel = 5
|
||||
retraction_prime_speed = 15
|
||||
|
@ -22,4 +22,4 @@ def test_ultimaker3extended_variants(um3_file, um3e_file):
|
||||
um3.read_file(open(os.path.join(directory, um3_file)))
|
||||
um3e = configparser.ConfigParser()
|
||||
um3e.read_file(open(os.path.join(directory, um3e_file)))
|
||||
assert um3["values"] == um3e["values"]
|
||||
assert um3["values"] == um3e["values"]
|
||||
|
Loading…
x
Reference in New Issue
Block a user