mirror of
https://git.mirrors.martin98.com/https://github.com/Ultimaker/Cura
synced 2025-08-11 04:39:59 +08:00
Merge branch 'master' into feature_support_eraser_ux
This commit is contained in:
commit
11be8f158f
@ -136,6 +136,7 @@ class BuildVolume(SceneNode):
|
||||
if active_extruder_changed is not None:
|
||||
node.callDecoration("getActiveExtruderChangedSignal").disconnect(self._updateDisallowedAreasAndRebuild)
|
||||
node.decoratorsChanged.disconnect(self._updateNodeListeners)
|
||||
self._updateDisallowedAreasAndRebuild() # make sure we didn't miss anything before we updated the node listeners
|
||||
|
||||
self._scene_objects = new_scene_objects
|
||||
self._onSettingPropertyChanged("print_sequence", "value") # Create fake event, so right settings are triggered.
|
||||
@ -150,7 +151,6 @@ class BuildVolume(SceneNode):
|
||||
active_extruder_changed = node.callDecoration("getActiveExtruderChangedSignal")
|
||||
if active_extruder_changed is not None:
|
||||
active_extruder_changed.connect(self._updateDisallowedAreasAndRebuild)
|
||||
self._updateDisallowedAreasAndRebuild()
|
||||
|
||||
def setWidth(self, width):
|
||||
if width is not None:
|
||||
@ -239,7 +239,7 @@ class BuildVolume(SceneNode):
|
||||
# Group nodes should override the _outside_buildarea property of their children.
|
||||
for group_node in group_nodes:
|
||||
for child_node in group_node.getAllChildren():
|
||||
child_node.setOutsideBuildArea(group_node.isOutsideBuildArea)
|
||||
child_node.setOutsideBuildArea(group_node.isOutsideBuildArea())
|
||||
|
||||
## Update the outsideBuildArea of a single node, given bounds or current build volume
|
||||
def checkBoundsAndUpdate(self, node: CuraSceneNode, bounds: Optional[AxisAlignedBox] = None):
|
||||
|
@ -109,10 +109,6 @@ class CuraActions(QObject):
|
||||
|
||||
nodes_to_change = []
|
||||
for node in Selection.getAllSelectedObjects():
|
||||
# Do not change any nodes that already have the right extruder set.
|
||||
if node.callDecoration("getActiveExtruder") == extruder_id:
|
||||
continue
|
||||
|
||||
# If the node is a group, apply the active extruder to all children of the group.
|
||||
if node.callDecoration("isGroup"):
|
||||
for grouped_node in BreadthFirstIterator(node):
|
||||
@ -125,6 +121,10 @@ class CuraActions(QObject):
|
||||
nodes_to_change.append(grouped_node)
|
||||
continue
|
||||
|
||||
# Do not change any nodes that already have the right extruder set.
|
||||
if node.callDecoration("getActiveExtruder") == extruder_id:
|
||||
continue
|
||||
|
||||
nodes_to_change.append(node)
|
||||
|
||||
if not nodes_to_change:
|
||||
|
@ -4,7 +4,7 @@
|
||||
#Type hinting.
|
||||
from typing import Dict
|
||||
|
||||
from PyQt5.QtCore import QObject
|
||||
from PyQt5.QtCore import QObject, QTimer
|
||||
from PyQt5.QtNetwork import QLocalServer
|
||||
from PyQt5.QtNetwork import QLocalSocket
|
||||
|
||||
@ -60,18 +60,20 @@ from cura.Machines.Models.BuildPlateModel import BuildPlateModel
|
||||
from cura.Machines.Models.NozzleModel import NozzleModel
|
||||
from cura.Machines.Models.QualityProfilesDropDownMenuModel import QualityProfilesDropDownMenuModel
|
||||
from cura.Machines.Models.CustomQualityProfilesDropDownMenuModel import CustomQualityProfilesDropDownMenuModel
|
||||
|
||||
from cura.Machines.Models.MultiBuildPlateModel import MultiBuildPlateModel
|
||||
|
||||
from cura.Machines.Models.MaterialManagementModel import MaterialManagementModel
|
||||
from cura.Machines.Models.GenericMaterialsModel import GenericMaterialsModel
|
||||
from cura.Machines.Models.BrandMaterialsModel import BrandMaterialsModel
|
||||
from cura.Machines.Models.QualityManagementModel import QualityManagementModel
|
||||
from cura.Machines.Models.QualitySettingsModel import QualitySettingsModel
|
||||
from cura.Machines.Models.MachineManagementModel import MachineManagementModel
|
||||
|
||||
from cura.Machines.MachineErrorChecker import MachineErrorChecker
|
||||
|
||||
from cura.Settings.SettingInheritanceManager import SettingInheritanceManager
|
||||
from cura.Settings.SimpleModeSettingsManager import SimpleModeSettingsManager
|
||||
|
||||
from cura.Machines.VariantManager import VariantManager
|
||||
from cura.Machines.Models.QualityManagementModel import QualityManagementModel
|
||||
|
||||
from . import PlatformPhysics
|
||||
from . import BuildVolume
|
||||
@ -88,8 +90,8 @@ from cura.Settings.ExtruderManager import ExtruderManager
|
||||
from cura.Settings.UserChangesModel import UserChangesModel
|
||||
from cura.Settings.ExtrudersModel import ExtrudersModel
|
||||
from cura.Settings.MaterialSettingsVisibilityHandler import MaterialSettingsVisibilityHandler
|
||||
from cura.Machines.Models.QualitySettingsModel import QualitySettingsModel
|
||||
from cura.Settings.ContainerManager import ContainerManager
|
||||
from cura.Settings.SettingVisibilityPresetsModel import SettingVisibilityPresetsModel
|
||||
|
||||
from cura.ObjectsModel import ObjectsModel
|
||||
|
||||
@ -139,15 +141,10 @@ class CuraApplication(QtApplication):
|
||||
MachineStack = Resources.UserType + 7
|
||||
ExtruderStack = Resources.UserType + 8
|
||||
DefinitionChangesContainer = Resources.UserType + 9
|
||||
SettingVisibilityPreset = Resources.UserType + 10
|
||||
|
||||
Q_ENUMS(ResourceTypes)
|
||||
|
||||
# FIXME: This signal belongs to the MachineManager, but the CuraEngineBackend plugin requires on it.
|
||||
# Because plugins are initialized before the ContainerRegistry, putting this signal in MachineManager
|
||||
# will make it initialized before ContainerRegistry does, and it won't find the active machine, thus
|
||||
# Cura will always show the Add Machine Dialog upon start.
|
||||
stacksValidationFinished = pyqtSignal() # Emitted whenever a validation is finished
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
# this list of dir names will be used by UM to detect an old cura directory
|
||||
for dir_name in ["extruders", "machine_instances", "materials", "plugins", "quality", "user", "variants"]:
|
||||
@ -192,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")
|
||||
@ -224,12 +222,14 @@ class CuraApplication(QtApplication):
|
||||
self._machine_manager = None # This is initialized on demand.
|
||||
self._extruder_manager = None
|
||||
self._material_manager = None
|
||||
self._quality_manager = None
|
||||
self._object_manager = None
|
||||
self._build_plate_model = None
|
||||
self._multi_build_plate_model = None
|
||||
self._setting_inheritance_manager = None
|
||||
self._simple_mode_settings_manager = None
|
||||
self._cura_scene_controller = None
|
||||
self._machine_error_checker = None
|
||||
|
||||
self._additional_components = {} # Components to add to certain areas in the interface
|
||||
|
||||
@ -286,10 +286,15 @@ class CuraApplication(QtApplication):
|
||||
self._preferred_mimetype = ""
|
||||
self._i18n_catalog = i18nCatalog("cura")
|
||||
|
||||
self.getController().getScene().sceneChanged.connect(self.updatePlatformActivity)
|
||||
self._update_platform_activity_timer = QTimer()
|
||||
self._update_platform_activity_timer.setInterval(500)
|
||||
self._update_platform_activity_timer.setSingleShot(True)
|
||||
self._update_platform_activity_timer.timeout.connect(self.updatePlatformActivity)
|
||||
|
||||
self.getController().getScene().sceneChanged.connect(self.updatePlatformActivityDelayed)
|
||||
self.getController().toolOperationStopped.connect(self._onToolOperationStopped)
|
||||
self.getController().contextMenuRequested.connect(self._onContextMenuRequested)
|
||||
self.getCuraSceneController().activeBuildPlateChanged.connect(self.updatePlatformActivity)
|
||||
self.getCuraSceneController().activeBuildPlateChanged.connect(self.updatePlatformActivityDelayed)
|
||||
|
||||
Resources.addType(self.ResourceTypes.QmlFiles, "qml")
|
||||
Resources.addType(self.ResourceTypes.Firmware, "firmware")
|
||||
@ -376,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)
|
||||
@ -405,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())
|
||||
@ -743,19 +653,28 @@ class CuraApplication(QtApplication):
|
||||
self.preRun()
|
||||
|
||||
container_registry = ContainerRegistry.getInstance()
|
||||
|
||||
Logger.log("i", "Initializing variant manager")
|
||||
self._variant_manager = VariantManager(container_registry)
|
||||
self._variant_manager.initialize()
|
||||
|
||||
Logger.log("i", "Initializing material manager")
|
||||
from cura.Machines.MaterialManager import MaterialManager
|
||||
self._material_manager = MaterialManager(container_registry, parent = self)
|
||||
self._material_manager.initialize()
|
||||
|
||||
Logger.log("i", "Initializing quality manager")
|
||||
from cura.Machines.QualityManager import QualityManager
|
||||
self._quality_manager = QualityManager(container_registry, parent = self)
|
||||
self._quality_manager.initialize()
|
||||
|
||||
Logger.log("i", "Initializing machine manager")
|
||||
self._machine_manager = MachineManager(self)
|
||||
|
||||
Logger.log("i", "Initializing machine error checker")
|
||||
self._machine_error_checker = MachineErrorChecker(self)
|
||||
self._machine_error_checker.initialize()
|
||||
|
||||
# Check if we should run as single instance or not
|
||||
self._setUpSingleInstanceServer()
|
||||
|
||||
@ -781,8 +700,11 @@ class CuraApplication(QtApplication):
|
||||
self._openFile(file_name)
|
||||
|
||||
self.started = True
|
||||
self.initializationFinished.emit()
|
||||
self.exec_()
|
||||
|
||||
initializationFinished = pyqtSignal()
|
||||
|
||||
## Run Cura without GUI elements and interaction (server mode).
|
||||
def runWithoutGUI(self):
|
||||
self._use_gui = False
|
||||
@ -847,6 +769,9 @@ class CuraApplication(QtApplication):
|
||||
def hasGui(self):
|
||||
return self._use_gui
|
||||
|
||||
def getMachineErrorChecker(self, *args) -> MachineErrorChecker:
|
||||
return self._machine_error_checker
|
||||
|
||||
def getMachineManager(self, *args) -> MachineManager:
|
||||
if self._machine_manager is None:
|
||||
self._machine_manager = MachineManager(self)
|
||||
@ -961,6 +886,7 @@ class CuraApplication(QtApplication):
|
||||
qmlRegisterType(BrandMaterialsModel, "Cura", 1, 0, "BrandMaterialsModel")
|
||||
qmlRegisterType(MaterialManagementModel, "Cura", 1, 0, "MaterialManagementModel")
|
||||
qmlRegisterType(QualityManagementModel, "Cura", 1, 0, "QualityManagementModel")
|
||||
qmlRegisterType(MachineManagementModel, "Cura", 1, 0, "MachineManagementModel")
|
||||
|
||||
qmlRegisterSingletonType(QualityProfilesDropDownMenuModel, "Cura", 1, 0,
|
||||
"QualityProfilesDropDownMenuModel", self.getQualityProfilesDropDownMenuModel)
|
||||
@ -973,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")))
|
||||
@ -1048,6 +975,10 @@ class CuraApplication(QtApplication):
|
||||
def getSceneBoundingBoxString(self):
|
||||
return self._i18n_catalog.i18nc("@info 'width', 'depth' and 'height' are variable names that must NOT be translated; just translate the format of ##x##x## mm.", "%(width).1f x %(depth).1f x %(height).1f mm") % {'width' : self._scene_bounding_box.width.item(), 'depth': self._scene_bounding_box.depth.item(), 'height' : self._scene_bounding_box.height.item()}
|
||||
|
||||
def updatePlatformActivityDelayed(self, node = None):
|
||||
if node is not None and node.getMeshData() is not None:
|
||||
self._update_platform_activity_timer.start()
|
||||
|
||||
## Update scene bounding box for current build plate
|
||||
def updatePlatformActivity(self, node = None):
|
||||
count = 0
|
||||
|
181
cura/Machines/MachineErrorChecker.py
Normal file
181
cura/Machines/MachineErrorChecker.py
Normal file
@ -0,0 +1,181 @@
|
||||
# Copyright (c) 2018 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import time
|
||||
|
||||
from collections import deque
|
||||
|
||||
from PyQt5.QtCore import QObject, QTimer, pyqtSignal, pyqtProperty
|
||||
|
||||
from UM.Application import Application
|
||||
from UM.Logger import Logger
|
||||
from UM.Settings.SettingDefinition import SettingDefinition
|
||||
from UM.Settings.Validator import ValidatorState
|
||||
|
||||
|
||||
#
|
||||
# This class performs setting error checks for the currently active machine.
|
||||
#
|
||||
# The whole error checking process is pretty heavy which can take ~0.5 secs, so it can cause GUI to lag.
|
||||
# The idea here is to split the whole error check into small tasks, each of which only checks a single setting key
|
||||
# in a stack. According to my profiling results, the maximal runtime for such a sub-task is <0.03 secs, which should
|
||||
# be good enough. Moreover, if any changes happened to the machine, we can cancel the check in progress without wait
|
||||
# for it to finish the complete work.
|
||||
#
|
||||
class MachineErrorChecker(QObject):
|
||||
|
||||
def __init__(self, parent = None):
|
||||
super().__init__(parent)
|
||||
|
||||
self._global_stack = None
|
||||
|
||||
self._has_errors = True # Result of the error check, indicating whether there are errors in the stack
|
||||
self._error_keys = set() # A set of settings keys that have errors
|
||||
self._error_keys_in_progress = set() # The variable that stores the results of the currently in progress check
|
||||
|
||||
self._stacks_and_keys_to_check = None # a FIFO queue of tuples (stack, key) to check for errors
|
||||
|
||||
self._need_to_check = False # Whether we need to schedule a new check or not. This flag is set when a new
|
||||
# error check needs to take place while there is already one running at the moment.
|
||||
self._check_in_progress = False # Whether there is an error check running in progress at the moment.
|
||||
|
||||
self._application = Application.getInstance()
|
||||
self._machine_manager = self._application.getMachineManager()
|
||||
|
||||
self._start_time = 0 # measure checking time
|
||||
|
||||
# This timer delays the starting of error check so we can react less frequently if the user is frequently
|
||||
# changing settings.
|
||||
self._error_check_timer = QTimer(self)
|
||||
self._error_check_timer.setInterval(100)
|
||||
self._error_check_timer.setSingleShot(True)
|
||||
|
||||
def initialize(self):
|
||||
self._error_check_timer.timeout.connect(self._rescheduleCheck)
|
||||
|
||||
# Reconnect all signals when the active machine gets changed.
|
||||
self._machine_manager.globalContainerChanged.connect(self._onMachineChanged)
|
||||
|
||||
# Whenever the machine settings get changed, we schedule an error check.
|
||||
self._machine_manager.globalContainerChanged.connect(self.startErrorCheck)
|
||||
self._machine_manager.globalValueChanged.connect(self.startErrorCheck)
|
||||
|
||||
self._onMachineChanged()
|
||||
|
||||
def _onMachineChanged(self):
|
||||
if self._global_stack:
|
||||
self._global_stack.propertyChanged.disconnect(self.startErrorCheck)
|
||||
self._global_stack.containersChanged.disconnect(self.startErrorCheck)
|
||||
|
||||
for extruder in self._global_stack.extruders.values():
|
||||
extruder.propertyChanged.disconnect(self.startErrorCheck)
|
||||
extruder.containersChanged.disconnect(self.startErrorCheck)
|
||||
|
||||
self._global_stack = self._machine_manager.activeMachine
|
||||
|
||||
if self._global_stack:
|
||||
self._global_stack.propertyChanged.connect(self.startErrorCheck)
|
||||
self._global_stack.containersChanged.connect(self.startErrorCheck)
|
||||
|
||||
for extruder in self._global_stack.extruders.values():
|
||||
extruder.propertyChanged.connect(self.startErrorCheck)
|
||||
extruder.containersChanged.connect(self.startErrorCheck)
|
||||
|
||||
hasErrorUpdated = pyqtSignal()
|
||||
needToWaitForResultChanged = pyqtSignal()
|
||||
errorCheckFinished = pyqtSignal()
|
||||
|
||||
@pyqtProperty(bool, notify = hasErrorUpdated)
|
||||
def hasError(self) -> bool:
|
||||
return self._has_errors
|
||||
|
||||
@pyqtProperty(bool, notify = needToWaitForResultChanged)
|
||||
def needToWaitForResult(self) -> bool:
|
||||
return self._need_to_check or self._check_in_progress
|
||||
|
||||
# Starts the error check timer to schedule a new error check.
|
||||
def startErrorCheck(self, *args):
|
||||
if not self._check_in_progress:
|
||||
self._need_to_check = True
|
||||
self.needToWaitForResultChanged.emit()
|
||||
self._error_check_timer.start()
|
||||
|
||||
# This function is called by the timer to reschedule a new error check.
|
||||
# If there is no check in progress, it will start a new one. If there is any, it sets the "_need_to_check" flag
|
||||
# to notify the current check to stop and start a new one.
|
||||
def _rescheduleCheck(self):
|
||||
if self._check_in_progress and not self._need_to_check:
|
||||
self._need_to_check = True
|
||||
self.needToWaitForResultChanged.emit()
|
||||
return
|
||||
|
||||
self._error_keys_in_progress = set()
|
||||
self._need_to_check = False
|
||||
self.needToWaitForResultChanged.emit()
|
||||
|
||||
global_stack = self._machine_manager.activeMachine
|
||||
if global_stack is None:
|
||||
Logger.log("i", "No active machine, nothing to check.")
|
||||
return
|
||||
|
||||
# Populate the (stack, key) tuples to check
|
||||
self._stacks_and_keys_to_check = deque()
|
||||
for stack in [global_stack] + list(global_stack.extruders.values()):
|
||||
for key in stack.getAllKeys():
|
||||
self._stacks_and_keys_to_check.append((stack, key))
|
||||
|
||||
self._application.callLater(self._checkStack)
|
||||
self._start_time = time.time()
|
||||
Logger.log("d", "New error check scheduled.")
|
||||
|
||||
def _checkStack(self):
|
||||
if self._need_to_check:
|
||||
Logger.log("d", "Need to check for errors again. Discard the current progress and reschedule a check.")
|
||||
self._check_in_progress = False
|
||||
self._application.callLater(self.startErrorCheck)
|
||||
return
|
||||
|
||||
self._check_in_progress = True
|
||||
|
||||
# If there is nothing to check any more, it means there is no error.
|
||||
if not self._stacks_and_keys_to_check:
|
||||
# Finish
|
||||
self._setResult(False)
|
||||
return
|
||||
|
||||
# Get the next stack and key to check
|
||||
stack, key = self._stacks_and_keys_to_check.popleft()
|
||||
|
||||
enabled = stack.getProperty(key, "enabled")
|
||||
if not enabled:
|
||||
self._application.callLater(self._checkStack)
|
||||
return
|
||||
|
||||
validation_state = stack.getProperty(key, "validationState")
|
||||
if validation_state is None:
|
||||
# Setting is not validated. This can happen if there is only a setting definition.
|
||||
# We do need to validate it, because a setting definitions value can be set by a function, which could
|
||||
# be an invalid setting.
|
||||
definition = stack.getSettingDefinition(key)
|
||||
validator_type = SettingDefinition.getValidatorForType(definition.type)
|
||||
if validator_type:
|
||||
validator = validator_type(key)
|
||||
validation_state = validator(stack)
|
||||
if validation_state in (ValidatorState.Exception, ValidatorState.MaximumError, ValidatorState.MinimumError):
|
||||
# Finish
|
||||
self._setResult(True)
|
||||
return
|
||||
|
||||
# Schedule the check for the next key
|
||||
self._application.callLater(self._checkStack)
|
||||
|
||||
def _setResult(self, result: bool):
|
||||
if result != self._has_errors:
|
||||
self._has_errors = result
|
||||
self.hasErrorUpdated.emit()
|
||||
self._machine_manager.stacksValidationChanged.emit()
|
||||
self._need_to_check = False
|
||||
self._check_in_progress = False
|
||||
self.needToWaitForResultChanged.emit()
|
||||
self.errorCheckFinished.emit()
|
||||
Logger.log("i", "Error check finished, result = %s, time = %0.1fs", result, time.time() - self._start_time)
|
@ -16,10 +16,11 @@ from cura.Machines.MaterialNode import MaterialNode #For type checking.
|
||||
# so "generic_abs_ultimaker3", "generic_abs_ultimaker3_AA_0.4", etc.
|
||||
#
|
||||
class MaterialGroup:
|
||||
__slots__ = ("name", "root_material_node", "derived_material_node_list")
|
||||
__slots__ = ("name", "is_read_only", "root_material_node", "derived_material_node_list")
|
||||
|
||||
def __init__(self, name: str, root_material_node: MaterialNode):
|
||||
self.name = name
|
||||
self.is_read_only = False
|
||||
self.root_material_node = root_material_node
|
||||
self.derived_material_node_list = [] #type: List[MaterialNode]
|
||||
|
||||
|
@ -86,6 +86,7 @@ class MaterialManager(QObject):
|
||||
root_material_id = material_metadata.get("base_file")
|
||||
if root_material_id not in self._material_group_map:
|
||||
self._material_group_map[root_material_id] = MaterialGroup(root_material_id, MaterialNode(material_metadatas[root_material_id]))
|
||||
self._material_group_map[root_material_id].is_read_only = self._container_registry.isReadOnly(root_material_id)
|
||||
group = self._material_group_map[root_material_id]
|
||||
|
||||
#Store this material in the group of the appropriate root material.
|
||||
@ -100,13 +101,6 @@ class MaterialManager(QObject):
|
||||
# GUID -> material group list
|
||||
self._guid_material_groups_map = defaultdict(list)
|
||||
for root_material_id, material_group in self._material_group_map.items():
|
||||
# This can happen when we are updating with incomplete data.
|
||||
if material_group.root_material_node is None:
|
||||
Logger.log("e", "Missing root material node for [%s]. Probably caused by update using incomplete data."
|
||||
" Check all related signals for further debugging.",
|
||||
material_group.name)
|
||||
self._update_timer.start()
|
||||
return
|
||||
guid = material_group.root_material_node.metadata["GUID"]
|
||||
self._guid_material_groups_map[guid].append(material_group)
|
||||
|
||||
@ -331,6 +325,35 @@ class MaterialManager(QObject):
|
||||
|
||||
return material_node
|
||||
|
||||
#
|
||||
# Gets MaterialNode for the given extruder and machine with the given material type.
|
||||
# Returns None if:
|
||||
# 1. the given machine doesn't have materials;
|
||||
# 2. cannot find any material InstanceContainers with the given settings.
|
||||
#
|
||||
def getMaterialNodeByType(self, global_stack: "GlobalStack", extruder_variant_name: str, material_guid: str) -> Optional["MaterialNode"]:
|
||||
node = None
|
||||
machine_definition = global_stack.definition
|
||||
if parseBool(machine_definition.getMetaDataEntry("has_materials", False)):
|
||||
material_diameter = machine_definition.getProperty("material_diameter", "value")
|
||||
if isinstance(material_diameter, SettingFunction):
|
||||
material_diameter = material_diameter(global_stack)
|
||||
|
||||
# Look at the guid to material dictionary
|
||||
root_material_id = None
|
||||
for material_group in self._guid_material_groups_map[material_guid]:
|
||||
if material_group.is_read_only:
|
||||
root_material_id = material_group.root_material_node.metadata["id"]
|
||||
break
|
||||
|
||||
if not root_material_id:
|
||||
Logger.log("i", "Cannot find materials with guid [%s] ", material_guid)
|
||||
return None
|
||||
|
||||
node = self.getMaterialNode(machine_definition.getId(), extruder_variant_name,
|
||||
material_diameter, root_material_id)
|
||||
return node
|
||||
|
||||
#
|
||||
# Used by QualityManager. Built-in quality profiles may be based on generic material IDs such as "generic_pla".
|
||||
# For materials such as ultimaker_pla_orange, no quality profiles may be found, so we should fall back to use
|
||||
|
@ -53,8 +53,8 @@ class BrandMaterialsModel(ListModel):
|
||||
self._extruder_manager = CuraApplication.getInstance().getExtruderManager()
|
||||
self._material_manager = CuraApplication.getInstance().getMaterialManager()
|
||||
|
||||
self._machine_manager.globalContainerChanged.connect(self._update)
|
||||
self._material_manager.materialsUpdated.connect(self._update)
|
||||
self._machine_manager.activeStackChanged.connect(self._update) #Update when switching machines.
|
||||
self._material_manager.materialsUpdated.connect(self._update) #Update when the list of materials changes.
|
||||
self._update()
|
||||
|
||||
def setExtruderPosition(self, position: int):
|
||||
|
@ -15,8 +15,8 @@ class GenericMaterialsModel(BaseMaterialsModel):
|
||||
self._extruder_manager = CuraApplication.getInstance().getExtruderManager()
|
||||
self._material_manager = CuraApplication.getInstance().getMaterialManager()
|
||||
|
||||
self._machine_manager.globalContainerChanged.connect(self._update)
|
||||
self._material_manager.materialsUpdated.connect(self._update)
|
||||
self._machine_manager.activeStackChanged.connect(self._update) #Update when switching machines.
|
||||
self._material_manager.materialsUpdated.connect(self._update) #Update when the list of materials changes.
|
||||
self._update()
|
||||
|
||||
def _update(self):
|
||||
|
82
cura/Machines/Models/MachineManagementModel.py
Normal file
82
cura/Machines/Models/MachineManagementModel.py
Normal file
@ -0,0 +1,82 @@
|
||||
# Copyright (c) 2018 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
from UM.Qt.ListModel import ListModel
|
||||
|
||||
from PyQt5.QtCore import Qt
|
||||
|
||||
from UM.Settings.ContainerRegistry import ContainerRegistry
|
||||
from UM.Settings.ContainerStack import ContainerStack
|
||||
|
||||
from UM.i18n import i18nCatalog
|
||||
catalog = i18nCatalog("cura")
|
||||
|
||||
|
||||
#
|
||||
# This the QML model for the quality management page.
|
||||
#
|
||||
class MachineManagementModel(ListModel):
|
||||
NameRole = Qt.UserRole + 1
|
||||
IdRole = Qt.UserRole + 2
|
||||
MetaDataRole = Qt.UserRole + 3
|
||||
GroupRole = Qt.UserRole + 4
|
||||
|
||||
def __init__(self, parent = None):
|
||||
super().__init__(parent)
|
||||
self.addRoleName(self.NameRole, "name")
|
||||
self.addRoleName(self.IdRole, "id")
|
||||
self.addRoleName(self.MetaDataRole, "metadata")
|
||||
self.addRoleName(self.GroupRole, "group")
|
||||
self._local_container_stacks = []
|
||||
self._network_container_stacks = []
|
||||
|
||||
# Listen to changes
|
||||
ContainerRegistry.getInstance().containerAdded.connect(self._onContainerChanged)
|
||||
ContainerRegistry.getInstance().containerMetaDataChanged.connect(self._onContainerChanged)
|
||||
ContainerRegistry.getInstance().containerRemoved.connect(self._onContainerChanged)
|
||||
self._filter_dict = {}
|
||||
self._update()
|
||||
|
||||
## Handler for container added/removed events from registry
|
||||
def _onContainerChanged(self, container):
|
||||
# We only need to update when the added / removed container is a stack.
|
||||
if isinstance(container, ContainerStack) and container.getMetaDataEntry("type") == "machine":
|
||||
self._update()
|
||||
|
||||
## Private convenience function to reset & repopulate the model.
|
||||
def _update(self):
|
||||
items = []
|
||||
|
||||
# Get first the network enabled printers
|
||||
network_filter_printers = {"type": "machine",
|
||||
"um_network_key": "*",
|
||||
"hidden": "False"}
|
||||
self._network_container_stacks = ContainerRegistry.getInstance().findContainerStacks(**network_filter_printers)
|
||||
self._network_container_stacks.sort(key = lambda i: i.getMetaDataEntry("connect_group_name"))
|
||||
|
||||
for container in self._network_container_stacks:
|
||||
metadata = container.getMetaData().copy()
|
||||
if container.getBottom():
|
||||
metadata["definition_name"] = container.getBottom().getName()
|
||||
|
||||
items.append({"name": metadata["connect_group_name"],
|
||||
"id": container.getId(),
|
||||
"metadata": metadata,
|
||||
"group": catalog.i18nc("@info:title", "Network enabled printers")})
|
||||
|
||||
# Get now the local printers
|
||||
local_filter_printers = {"type": "machine", "um_network_key": None}
|
||||
self._local_container_stacks = ContainerRegistry.getInstance().findContainerStacks(**local_filter_printers)
|
||||
self._local_container_stacks.sort(key = lambda i: i.getName())
|
||||
|
||||
for container in self._local_container_stacks:
|
||||
metadata = container.getMetaData().copy()
|
||||
if container.getBottom():
|
||||
metadata["definition_name"] = container.getBottom().getName()
|
||||
|
||||
items.append({"name": container.getName(),
|
||||
"id": container.getId(),
|
||||
"metadata": metadata,
|
||||
"group": catalog.i18nc("@info:title", "Local printers")})
|
||||
|
||||
self.setItems(items)
|
@ -1,7 +1,7 @@
|
||||
# Copyright (c) 2018 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
from PyQt5.QtCore import pyqtSignal, pyqtProperty
|
||||
from PyQt5.QtCore import QTimer, pyqtSignal, pyqtProperty
|
||||
|
||||
from UM.Application import Application
|
||||
from UM.Scene.Selection import Selection
|
||||
@ -21,8 +21,13 @@ class MultiBuildPlateModel(ListModel):
|
||||
def __init__(self, parent = None):
|
||||
super().__init__(parent)
|
||||
|
||||
self._update_timer = QTimer()
|
||||
self._update_timer.setInterval(100)
|
||||
self._update_timer.setSingleShot(True)
|
||||
self._update_timer.timeout.connect(self._updateSelectedObjectBuildPlateNumbers)
|
||||
|
||||
self._application = Application.getInstance()
|
||||
self._application.getController().getScene().sceneChanged.connect(self._updateSelectedObjectBuildPlateNumbers)
|
||||
self._application.getController().getScene().sceneChanged.connect(self._updateSelectedObjectBuildPlateNumbersDelayed)
|
||||
Selection.selectionChanged.connect(self._updateSelectedObjectBuildPlateNumbers)
|
||||
|
||||
self._max_build_plate = 1 # default
|
||||
@ -45,6 +50,9 @@ class MultiBuildPlateModel(ListModel):
|
||||
def activeBuildPlate(self):
|
||||
return self._active_build_plate
|
||||
|
||||
def _updateSelectedObjectBuildPlateNumbersDelayed(self, *args):
|
||||
self._update_timer.start()
|
||||
|
||||
def _updateSelectedObjectBuildPlateNumbers(self, *args):
|
||||
result = set()
|
||||
for node in Selection.getAllSelectedObjects():
|
||||
|
@ -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
|
||||
|
@ -25,7 +25,7 @@ ALL_VARIANT_TYPES = (VariantType.BUILD_PLATE, VariantType.NOZZLE)
|
||||
|
||||
|
||||
#
|
||||
# VariantManager is THE place to look for a specific variant. It maintains a variant lookup table with the following
|
||||
# VariantManager is THE place to look for a specific variant. It maintains two variant lookup tables with the following
|
||||
# structure:
|
||||
#
|
||||
# [machine_definition_id] -> [variant_type] -> [variant_name] -> ContainerNode(metadata / container)
|
||||
@ -35,6 +35,9 @@ ALL_VARIANT_TYPES = (VariantType.BUILD_PLATE, VariantType.NOZZLE)
|
||||
# -> "BB 0.8"
|
||||
# -> ...
|
||||
#
|
||||
# [machine_definition_id] -> [machine_buildplate_type] -> ContainerNode(metadata / container)
|
||||
# Example: "ultimaker3" -> "glass" (this is different from the variant name) -> ContainerNode
|
||||
#
|
||||
# Note that the "container" field is not loaded in the beginning because it would defeat the purpose of lazy-loading.
|
||||
# A container is loaded when getVariant() is called to load a variant InstanceContainer.
|
||||
#
|
||||
@ -44,6 +47,7 @@ class VariantManager:
|
||||
self._container_registry = container_registry # type: ContainerRegistry
|
||||
|
||||
self._machine_to_variant_dict_map = dict() # <machine_type> -> <variant_dict>
|
||||
self._machine_to_buildplate_dict_map = dict()
|
||||
|
||||
self._exclude_variant_id_list = ["empty_variant"]
|
||||
|
||||
@ -53,6 +57,7 @@ class VariantManager:
|
||||
#
|
||||
def initialize(self):
|
||||
self._machine_to_variant_dict_map = OrderedDict()
|
||||
self._machine_to_buildplate_dict_map = OrderedDict()
|
||||
|
||||
# Cache all variants from the container registry to a variant map for better searching and organization.
|
||||
variant_metadata_list = self._container_registry.findContainersMetadata(type = "variant")
|
||||
@ -78,6 +83,22 @@ class VariantManager:
|
||||
|
||||
variant_dict[variant_name] = ContainerNode(metadata = variant_metadata)
|
||||
|
||||
# If the variant is a buildplate then fill also the buildplate map
|
||||
if variant_type == VariantType.BUILD_PLATE:
|
||||
if variant_definition not in self._machine_to_buildplate_dict_map:
|
||||
self._machine_to_buildplate_dict_map[variant_definition] = OrderedDict()
|
||||
|
||||
variant_container = self._container_registry.findContainers(type = "variant", id = variant_metadata["id"])
|
||||
if not variant_container:
|
||||
# ERROR: not variant container. This should never happen
|
||||
raise RuntimeError("Not variant found [%s], type [%s] for machine [%s]" %
|
||||
(variant_name, variant_type, variant_definition))
|
||||
buildplate_type = variant_container[0].getProperty("machine_buildplate_type", "value")
|
||||
if buildplate_type not in self._machine_to_buildplate_dict_map[variant_definition]:
|
||||
self._machine_to_variant_dict_map[variant_definition][buildplate_type] = dict()
|
||||
|
||||
self._machine_to_buildplate_dict_map[variant_definition][buildplate_type] = variant_dict[variant_name]
|
||||
|
||||
#
|
||||
# Gets the variant InstanceContainer with the given information.
|
||||
# Almost the same as getVariantMetadata() except that this returns an InstanceContainer if present.
|
||||
@ -117,3 +138,8 @@ class VariantManager:
|
||||
if preferred_variant_name:
|
||||
node = self.getVariantNode(machine_definition_id, preferred_variant_name, variant_type)
|
||||
return node
|
||||
|
||||
def getBuildplateVariantNode(self, machine_definition_id: str, buildplate_type: str) -> Optional["ContainerNode"]:
|
||||
if machine_definition_id in self._machine_to_buildplate_dict_map:
|
||||
return self._machine_to_buildplate_dict_map[machine_definition_id].get(buildplate_type)
|
||||
return None
|
||||
|
@ -1,3 +1,8 @@
|
||||
# Copyright (c) 2018 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
from PyQt5.QtCore import QTimer
|
||||
|
||||
from UM.Application import Application
|
||||
from UM.Qt.ListModel import ListModel
|
||||
from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
|
||||
@ -14,8 +19,13 @@ class ObjectsModel(ListModel):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
Application.getInstance().getController().getScene().sceneChanged.connect(self._update)
|
||||
Preferences.getInstance().preferenceChanged.connect(self._update)
|
||||
Application.getInstance().getController().getScene().sceneChanged.connect(self._updateDelayed)
|
||||
Preferences.getInstance().preferenceChanged.connect(self._updateDelayed)
|
||||
|
||||
self._update_timer = QTimer()
|
||||
self._update_timer.setInterval(100)
|
||||
self._update_timer.setSingleShot(True)
|
||||
self._update_timer.timeout.connect(self._update)
|
||||
|
||||
self._build_plate_number = -1
|
||||
|
||||
@ -23,6 +33,9 @@ class ObjectsModel(ListModel):
|
||||
self._build_plate_number = nr
|
||||
self._update()
|
||||
|
||||
def _updateDelayed(self, *args):
|
||||
self._update_timer.start()
|
||||
|
||||
def _update(self, *args):
|
||||
nodes = []
|
||||
filter_current_build_plate = Preferences.getInstance().getValue("view/filter_current_build_plate")
|
||||
|
@ -40,6 +40,8 @@ class PlatformPhysics:
|
||||
Preferences.getInstance().addPreference("physics/automatic_drop_down", True)
|
||||
|
||||
def _onSceneChanged(self, source):
|
||||
if not source.getMeshData():
|
||||
return
|
||||
self._change_timer.start()
|
||||
|
||||
def _onChangeTimerFinished(self):
|
||||
|
81
cura/PrinterOutput/ConfigurationModel.py
Normal file
81
cura/PrinterOutput/ConfigurationModel.py
Normal file
@ -0,0 +1,81 @@
|
||||
# Copyright (c) 2018 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
from PyQt5.QtCore import pyqtProperty, QObject, pyqtSignal
|
||||
from typing import List
|
||||
|
||||
MYPY = False
|
||||
if MYPY:
|
||||
from cura.PrinterOutput.ExtruderConfigurationModel import ExtruderConfigurationModel
|
||||
|
||||
|
||||
class ConfigurationModel(QObject):
|
||||
|
||||
configurationChanged = pyqtSignal()
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self._printer_type = None
|
||||
self._extruder_configurations = [] # type: List[ExtruderConfigurationModel]
|
||||
self._buildplate_configuration = None
|
||||
|
||||
def setPrinterType(self, printer_type):
|
||||
self._printer_type = printer_type
|
||||
|
||||
@pyqtProperty(str, fset = setPrinterType, notify = configurationChanged)
|
||||
def printerType(self):
|
||||
return self._printer_type
|
||||
|
||||
def setExtruderConfigurations(self, extruder_configurations):
|
||||
self._extruder_configurations = extruder_configurations
|
||||
|
||||
@pyqtProperty("QVariantList", fset = setExtruderConfigurations, notify = configurationChanged)
|
||||
def extruderConfigurations(self):
|
||||
return self._extruder_configurations
|
||||
|
||||
def setBuildplateConfiguration(self, buildplate_configuration):
|
||||
self._buildplate_configuration = buildplate_configuration
|
||||
|
||||
@pyqtProperty(str, fset = setBuildplateConfiguration, notify = configurationChanged)
|
||||
def buildplateConfiguration(self):
|
||||
return self._buildplate_configuration
|
||||
|
||||
## This method is intended to indicate whether the configuration is valid or not.
|
||||
# The method checks if the mandatory fields are or not set
|
||||
def isValid(self):
|
||||
if not self._extruder_configurations:
|
||||
return False
|
||||
for configuration in self._extruder_configurations:
|
||||
if configuration is None:
|
||||
return False
|
||||
return self._printer_type is not None
|
||||
|
||||
def __str__(self):
|
||||
message_chunks = []
|
||||
message_chunks.append("Printer type: " + self._printer_type)
|
||||
message_chunks.append("Extruders: [")
|
||||
for configuration in self._extruder_configurations:
|
||||
message_chunks.append(" " + str(configuration))
|
||||
message_chunks.append("]")
|
||||
if self._buildplate_configuration is not None:
|
||||
message_chunks.append("Buildplate: " + self._buildplate_configuration)
|
||||
|
||||
return "\n".join(message_chunks)
|
||||
|
||||
def __eq__(self, other):
|
||||
return hash(self) == hash(other)
|
||||
|
||||
## The hash function is used to compare and create unique sets. The configuration is unique if the configuration
|
||||
# of the extruders is unique (the order of the extruders matters), and the type and buildplate is the same.
|
||||
def __hash__(self):
|
||||
extruder_hash = hash(0)
|
||||
first_extruder = None
|
||||
for configuration in self._extruder_configurations:
|
||||
extruder_hash ^= hash(configuration)
|
||||
if configuration.position == 0:
|
||||
first_extruder = configuration
|
||||
# To ensure the correct order of the extruders, we add an "and" operation using the first extruder hash value
|
||||
if first_extruder:
|
||||
extruder_hash &= hash(first_extruder)
|
||||
|
||||
return hash(self._printer_type) ^ extruder_hash ^ hash(self._buildplate_configuration)
|
59
cura/PrinterOutput/ExtruderConfigurationModel.py
Normal file
59
cura/PrinterOutput/ExtruderConfigurationModel.py
Normal file
@ -0,0 +1,59 @@
|
||||
# Copyright (c) 2018 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
from PyQt5.QtCore import pyqtProperty, QObject, pyqtSignal
|
||||
|
||||
|
||||
class ExtruderConfigurationModel(QObject):
|
||||
|
||||
extruderConfigurationChanged = pyqtSignal()
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self._position = -1
|
||||
self._material = None
|
||||
self._hotend_id = None
|
||||
|
||||
def setPosition(self, position):
|
||||
self._position = position
|
||||
|
||||
@pyqtProperty(int, fset = setPosition, notify = extruderConfigurationChanged)
|
||||
def position(self):
|
||||
return self._position
|
||||
|
||||
def setMaterial(self, material):
|
||||
self._material = material
|
||||
|
||||
@pyqtProperty(QObject, fset = setMaterial, notify = extruderConfigurationChanged)
|
||||
def material(self):
|
||||
return self._material
|
||||
|
||||
def setHotendID(self, hotend_id):
|
||||
self._hotend_id = hotend_id
|
||||
|
||||
@pyqtProperty(str, fset = setHotendID, notify = extruderConfigurationChanged)
|
||||
def hotendID(self):
|
||||
return self._hotend_id
|
||||
|
||||
## This method is intended to indicate whether the configuration is valid or not.
|
||||
# The method checks if the mandatory fields are or not set
|
||||
# At this moment is always valid since we allow to have empty material and variants.
|
||||
def isValid(self):
|
||||
return True
|
||||
|
||||
def __str__(self):
|
||||
message_chunks = []
|
||||
message_chunks.append("Position: " + str(self._position))
|
||||
message_chunks.append("-")
|
||||
message_chunks.append("Material: " + self.material.type if self.material else "empty")
|
||||
message_chunks.append("-")
|
||||
message_chunks.append("HotendID: " + self.hotendID if self.hotendID else "empty")
|
||||
return " ".join(message_chunks)
|
||||
|
||||
def __eq__(self, other):
|
||||
return hash(self) == hash(other)
|
||||
|
||||
# Calculating a hash function using the position of the extruder, the material GUID and the hotend id to check if is
|
||||
# unique within a set
|
||||
def __hash__(self):
|
||||
return hash(self._position) ^ (hash(self._material.guid) if self._material is not None else hash(0)) ^ hash(self._hotend_id)
|
@ -1,8 +1,8 @@
|
||||
# Copyright (c) 2017 Ultimaker B.V.
|
||||
# Copyright (c) 2018 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
from PyQt5.QtCore import pyqtSignal, pyqtProperty, QObject, QVariant, pyqtSlot
|
||||
from UM.Logger import Logger
|
||||
from PyQt5.QtCore import pyqtSignal, pyqtProperty, QObject, pyqtSlot
|
||||
from cura.PrinterOutput.ExtruderConfigurationModel import ExtruderConfigurationModel
|
||||
|
||||
from typing import Optional
|
||||
|
||||
@ -17,14 +17,18 @@ class ExtruderOutputModel(QObject):
|
||||
targetHotendTemperatureChanged = pyqtSignal()
|
||||
hotendTemperatureChanged = pyqtSignal()
|
||||
activeMaterialChanged = pyqtSignal()
|
||||
extruderConfigurationChanged = pyqtSignal()
|
||||
|
||||
def __init__(self, printer: "PrinterOutputModel", parent=None):
|
||||
def __init__(self, printer: "PrinterOutputModel", position, parent=None):
|
||||
super().__init__(parent)
|
||||
self._printer = printer
|
||||
self._position = position
|
||||
self._target_hotend_temperature = 0
|
||||
self._hotend_temperature = 0
|
||||
self._hotend_id = ""
|
||||
self._active_material = None # type: Optional[MaterialOutputModel]
|
||||
self._extruder_configuration = ExtruderConfigurationModel()
|
||||
self._extruder_configuration.position = self._position
|
||||
|
||||
@pyqtProperty(QObject, notify = activeMaterialChanged)
|
||||
def activeMaterial(self) -> "MaterialOutputModel":
|
||||
@ -33,7 +37,9 @@ class ExtruderOutputModel(QObject):
|
||||
def updateActiveMaterial(self, material: Optional["MaterialOutputModel"]):
|
||||
if self._active_material != material:
|
||||
self._active_material = material
|
||||
self._extruder_configuration.material = self._active_material
|
||||
self.activeMaterialChanged.emit()
|
||||
self.extruderConfigurationChanged.emit()
|
||||
|
||||
## Update the hotend temperature. This only changes it locally.
|
||||
def updateHotendTemperature(self, temperature: float):
|
||||
@ -56,7 +62,7 @@ class ExtruderOutputModel(QObject):
|
||||
def targetHotendTemperature(self) -> float:
|
||||
return self._target_hotend_temperature
|
||||
|
||||
@pyqtProperty(float, notify=hotendTemperatureChanged)
|
||||
@pyqtProperty(float, notify = hotendTemperatureChanged)
|
||||
def hotendTemperature(self) -> float:
|
||||
return self._hotend_temperature
|
||||
|
||||
@ -67,4 +73,12 @@ class ExtruderOutputModel(QObject):
|
||||
def updateHotendID(self, id: str):
|
||||
if self._hotend_id != id:
|
||||
self._hotend_id = id
|
||||
self._extruder_configuration.hotendID = self._hotend_id
|
||||
self.hotendIDChanged.emit()
|
||||
self.extruderConfigurationChanged.emit()
|
||||
|
||||
@pyqtProperty(QObject, notify = extruderConfigurationChanged)
|
||||
def extruderConfiguration(self):
|
||||
if self._extruder_configuration.isValid():
|
||||
return self._extruder_configuration
|
||||
return None
|
@ -55,6 +55,17 @@ class NetworkedPrinterOutputDevice(PrinterOutputDevice):
|
||||
|
||||
self._connection_state_before_timeout = None # type: Optional[ConnectionState]
|
||||
|
||||
printer_type = self._properties.get(b"machine", b"").decode("utf-8")
|
||||
printer_type_identifiers = {
|
||||
"9066": "ultimaker3",
|
||||
"9511": "ultimaker3_extended"
|
||||
}
|
||||
self._printer_type = "Unknown"
|
||||
for key, value in printer_type_identifiers.items():
|
||||
if printer_type.startswith(key):
|
||||
self._printer_type = value
|
||||
break
|
||||
|
||||
def requestWrite(self, nodes, file_name=None, filter_by_machine=False, file_handler=None, **kwargs) -> None:
|
||||
raise NotImplementedError("requestWrite needs to be implemented")
|
||||
|
||||
@ -301,6 +312,10 @@ class NetworkedPrinterOutputDevice(PrinterOutputDevice):
|
||||
def firmwareVersion(self) -> str:
|
||||
return self._properties.get(b"firmware_version", b"").decode("utf-8")
|
||||
|
||||
@pyqtProperty(str, constant=True)
|
||||
def printerType(self) -> str:
|
||||
return self._printer_type
|
||||
|
||||
## IPadress of this printer
|
||||
@pyqtProperty(str, constant=True)
|
||||
def ipAddress(self) -> str:
|
||||
|
@ -6,7 +6,7 @@ from UM.Logger import Logger
|
||||
MYPY = False
|
||||
if MYPY:
|
||||
from cura.PrinterOutput.PrintJobOutputModel import PrintJobOutputModel
|
||||
from cura.PrinterOutput.ExtruderOuputModel import ExtruderOuputModel
|
||||
from cura.PrinterOutput.ExtruderOutputModel import ExtruderOutputModel
|
||||
from cura.PrinterOutput.PrinterOutputModel import PrinterOutputModel
|
||||
|
||||
|
||||
@ -18,7 +18,7 @@ class PrinterOutputController:
|
||||
self.can_control_manually = True
|
||||
self._output_device = output_device
|
||||
|
||||
def setTargetHotendTemperature(self, printer: "PrinterOutputModel", extruder: "ExtruderOuputModel", temperature: int):
|
||||
def setTargetHotendTemperature(self, printer: "PrinterOutputModel", extruder: "ExtruderOutputModel", temperature: int):
|
||||
Logger.log("w", "Set target hotend temperature not implemented in controller")
|
||||
|
||||
def setTargetBedTemperature(self, printer: "PrinterOutputModel", temperature: int):
|
||||
|
@ -1,11 +1,11 @@
|
||||
# Copyright (c) 2017 Ultimaker B.V.
|
||||
# Copyright (c) 2018 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
from PyQt5.QtCore import pyqtSignal, pyqtProperty, QObject, QVariant, pyqtSlot
|
||||
from UM.Logger import Logger
|
||||
from typing import Optional, List
|
||||
from typing import Optional
|
||||
from UM.Math.Vector import Vector
|
||||
from cura.PrinterOutput.ExtruderOuputModel import ExtruderOutputModel
|
||||
from cura.PrinterOutput.ConfigurationModel import ConfigurationModel
|
||||
from cura.PrinterOutput.ExtruderOutputModel import ExtruderOutputModel
|
||||
|
||||
MYPY = False
|
||||
if MYPY:
|
||||
@ -22,8 +22,10 @@ class PrinterOutputModel(QObject):
|
||||
nameChanged = pyqtSignal()
|
||||
headPositionChanged = pyqtSignal()
|
||||
keyChanged = pyqtSignal()
|
||||
typeChanged = pyqtSignal()
|
||||
printerTypeChanged = pyqtSignal()
|
||||
buildplateChanged = pyqtSignal()
|
||||
cameraChanged = pyqtSignal()
|
||||
configurationChanged = pyqtSignal()
|
||||
|
||||
def __init__(self, output_controller: "PrinterOutputController", number_of_extruders: int = 1, parent=None, firmware_version = ""):
|
||||
super().__init__(parent)
|
||||
@ -32,13 +34,18 @@ class PrinterOutputModel(QObject):
|
||||
self._name = ""
|
||||
self._key = "" # Unique identifier
|
||||
self._controller = output_controller
|
||||
self._extruders = [ExtruderOutputModel(printer=self) for i in range(number_of_extruders)]
|
||||
self._extruders = [ExtruderOutputModel(printer = self, position = i) for i in range(number_of_extruders)]
|
||||
self._printer_configuration = ConfigurationModel() # Indicates the current configuration setup in this printer
|
||||
self._head_position = Vector(0, 0, 0)
|
||||
self._active_print_job = None # type: Optional[PrintJobOutputModel]
|
||||
self._firmware_version = firmware_version
|
||||
self._printer_state = "unknown"
|
||||
self._is_preheating = False
|
||||
self._type = ""
|
||||
self._printer_type = ""
|
||||
self._buildplate_name = None
|
||||
# Update the printer configuration every time any of the extruders changes its configuration
|
||||
for extruder in self._extruders:
|
||||
extruder.extruderConfigurationChanged.connect(self._updateExtruderConfiguration)
|
||||
|
||||
self._camera = None
|
||||
|
||||
@ -64,14 +71,27 @@ class PrinterOutputModel(QObject):
|
||||
def camera(self):
|
||||
return self._camera
|
||||
|
||||
@pyqtProperty(str, notify = typeChanged)
|
||||
@pyqtProperty(str, notify = printerTypeChanged)
|
||||
def type(self):
|
||||
return self._type
|
||||
return self._printer_type
|
||||
|
||||
def updateType(self, type):
|
||||
if self._type != type:
|
||||
self._type = type
|
||||
self.typeChanged.emit()
|
||||
def updateType(self, printer_type):
|
||||
if self._printer_type != printer_type:
|
||||
self._printer_type = printer_type
|
||||
self._printer_configuration.printerType = self._printer_type
|
||||
self.printerTypeChanged.emit()
|
||||
self.configurationChanged.emit()
|
||||
|
||||
@pyqtProperty(str, notify = buildplateChanged)
|
||||
def buildplate(self):
|
||||
return self._buildplate_name
|
||||
|
||||
def updateBuildplateName(self, buildplate_name):
|
||||
if self._buildplate_name != buildplate_name:
|
||||
self._buildplate_name = buildplate_name
|
||||
self._printer_configuration.buildplateConfiguration = self._buildplate_name
|
||||
self.buildplateChanged.emit()
|
||||
self.configurationChanged.emit()
|
||||
|
||||
@pyqtProperty(str, notify=keyChanged)
|
||||
def key(self):
|
||||
@ -238,3 +258,14 @@ class PrinterOutputModel(QObject):
|
||||
if self._controller:
|
||||
return self._controller.can_control_manually
|
||||
return False
|
||||
|
||||
# Returns the configuration (material, variant and buildplate) of the current printer
|
||||
@pyqtProperty(QObject, notify = configurationChanged)
|
||||
def printerConfiguration(self):
|
||||
if self._printer_configuration.isValid():
|
||||
return self._printer_configuration
|
||||
return None
|
||||
|
||||
def _updateExtruderConfiguration(self):
|
||||
self._printer_configuration.extruderConfigurations = [extruder.extruderConfiguration for extruder in self._extruders]
|
||||
self.configurationChanged.emit()
|
||||
|
@ -1,12 +1,11 @@
|
||||
# Copyright (c) 2017 Ultimaker B.V.
|
||||
# Copyright (c) 2018 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
from UM.i18n import i18nCatalog
|
||||
from UM.OutputDevice.OutputDevice import OutputDevice
|
||||
from PyQt5.QtCore import pyqtProperty, QObject, QTimer, pyqtSignal
|
||||
from PyQt5.QtCore import pyqtProperty, QObject, QTimer, pyqtSignal, QVariant
|
||||
from PyQt5.QtWidgets import QMessageBox
|
||||
|
||||
|
||||
from UM.Logger import Logger
|
||||
from UM.Signal import signalemitter
|
||||
from UM.Application import Application
|
||||
@ -17,6 +16,7 @@ from typing import List, Optional
|
||||
MYPY = False
|
||||
if MYPY:
|
||||
from cura.PrinterOutput.PrinterOutputModel import PrinterOutputModel
|
||||
from cura.PrinterOutput.ConfigurationModel import ConfigurationModel
|
||||
|
||||
i18n_catalog = i18nCatalog("cura")
|
||||
|
||||
@ -44,10 +44,14 @@ class PrinterOutputDevice(QObject, OutputDevice):
|
||||
# Signal to indicate that the info text about the connection has changed.
|
||||
connectionTextChanged = pyqtSignal()
|
||||
|
||||
# Signal to indicate that the configuration of one of the printers has changed.
|
||||
uniqueConfigurationsChanged = pyqtSignal()
|
||||
|
||||
def __init__(self, device_id, parent = None):
|
||||
super().__init__(device_id = device_id, parent = parent)
|
||||
|
||||
self._printers = [] # type: List[PrinterOutputModel]
|
||||
self._unique_configurations = [] # type: List[ConfigurationModel]
|
||||
|
||||
self._monitor_view_qml_path = ""
|
||||
self._monitor_component = None
|
||||
@ -69,6 +73,8 @@ class PrinterOutputDevice(QObject, OutputDevice):
|
||||
|
||||
self._address = ""
|
||||
self._connection_text = ""
|
||||
self.printersChanged.connect(self._onPrintersChanged)
|
||||
Application.getInstance().getOutputDeviceManager().outputDevicesChanged.connect(self._updateUniqueConfigurations)
|
||||
|
||||
@pyqtProperty(str, notify = connectionTextChanged)
|
||||
def address(self):
|
||||
@ -175,6 +181,23 @@ class PrinterOutputDevice(QObject, OutputDevice):
|
||||
|
||||
self.acceptsCommandsChanged.emit()
|
||||
|
||||
# Returns the unique configurations of the printers within this output device
|
||||
@pyqtProperty("QVariantList", notify = uniqueConfigurationsChanged)
|
||||
def uniqueConfigurations(self):
|
||||
return self._unique_configurations
|
||||
|
||||
def _updateUniqueConfigurations(self):
|
||||
self._unique_configurations = list(set([printer.printerConfiguration for printer in self._printers if printer.printerConfiguration is not None]))
|
||||
self._unique_configurations.sort(key = lambda k: k.printerType)
|
||||
self.uniqueConfigurationsChanged.emit()
|
||||
|
||||
def _onPrintersChanged(self):
|
||||
for printer in self._printers:
|
||||
printer.configurationChanged.connect(self._updateUniqueConfigurations)
|
||||
|
||||
# At this point there may be non-updated configurations
|
||||
self._updateUniqueConfigurations()
|
||||
|
||||
|
||||
## The current processing state of the backend.
|
||||
class ConnectionState(IntEnum):
|
||||
|
@ -1,6 +1,8 @@
|
||||
# Copyright (c) 2016 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
from PyQt5.QtCore import QTimer
|
||||
|
||||
from UM.Application import Application
|
||||
from UM.Math.Polygon import Polygon
|
||||
from UM.Scene.SceneNodeDecorator import SceneNodeDecorator
|
||||
@ -22,6 +24,10 @@ class ConvexHullDecorator(SceneNodeDecorator):
|
||||
|
||||
self._global_stack = None
|
||||
|
||||
# Make sure the timer is created on the main thread
|
||||
self._recompute_convex_hull_timer = None
|
||||
Application.getInstance().callLater(self.createRecomputeConvexHullTimer)
|
||||
|
||||
self._raft_thickness = 0.0
|
||||
# For raft thickness, DRY
|
||||
self._build_volume = Application.getInstance().getBuildVolume()
|
||||
@ -33,6 +39,12 @@ class ConvexHullDecorator(SceneNodeDecorator):
|
||||
|
||||
self._onGlobalStackChanged()
|
||||
|
||||
def createRecomputeConvexHullTimer(self):
|
||||
self._recompute_convex_hull_timer = QTimer()
|
||||
self._recompute_convex_hull_timer.setInterval(200)
|
||||
self._recompute_convex_hull_timer.setSingleShot(True)
|
||||
self._recompute_convex_hull_timer.timeout.connect(self.recomputeConvexHull)
|
||||
|
||||
def setNode(self, node):
|
||||
previous_node = self._node
|
||||
# Disconnect from previous node signals
|
||||
@ -99,6 +111,12 @@ class ConvexHullDecorator(SceneNodeDecorator):
|
||||
return self._compute2DConvexHull()
|
||||
return None
|
||||
|
||||
def recomputeConvexHullDelayed(self):
|
||||
if self._recompute_convex_hull_timer is not None:
|
||||
self._recompute_convex_hull_timer.start()
|
||||
else:
|
||||
self.recomputeConvexHull()
|
||||
|
||||
def recomputeConvexHull(self):
|
||||
controller = Application.getInstance().getController()
|
||||
root = controller.getScene().getRoot()
|
||||
@ -279,7 +297,8 @@ class ConvexHullDecorator(SceneNodeDecorator):
|
||||
|
||||
def _onChanged(self, *args):
|
||||
self._raft_thickness = self._build_volume.getRaftThickness()
|
||||
self.recomputeConvexHull()
|
||||
if not args or args[0] == self._node:
|
||||
self.recomputeConvexHullDelayed()
|
||||
|
||||
def _onGlobalStackChanged(self):
|
||||
if self._global_stack:
|
||||
|
@ -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:
|
||||
@ -273,17 +291,17 @@ class CuraContainerRegistry(ContainerRegistry):
|
||||
elif profile_index < len(machine_extruders) + 1:
|
||||
# This is assumed to be an extruder profile
|
||||
extruder_id = machine_extruders[profile_index - 1].definition.getId()
|
||||
extuder_position = str(profile_index - 1)
|
||||
extruder_position = str(profile_index - 1)
|
||||
if not profile.getMetaDataEntry("position"):
|
||||
profile.addMetaDataEntry("position", extuder_position)
|
||||
profile.addMetaDataEntry("position", extruder_position)
|
||||
else:
|
||||
profile.setMetaDataEntry("position", extuder_position)
|
||||
profile.setMetaDataEntry("position", extruder_position)
|
||||
profile_id = (extruder_id + "_" + name_seed).lower().replace(" ", "_")
|
||||
|
||||
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.
|
||||
|
@ -668,6 +668,8 @@ class ExtruderManager(QObject):
|
||||
# global stack if not found.
|
||||
@staticmethod
|
||||
def getExtruderValue(extruder_index, key):
|
||||
if extruder_index == -1:
|
||||
extruder_index = int(Application.getInstance().getMachineManager().defaultExtruderPosition)
|
||||
extruder = ExtruderManager.getInstance().getExtruderStack(extruder_index)
|
||||
|
||||
if extruder:
|
||||
|
@ -4,7 +4,7 @@
|
||||
import collections
|
||||
import time
|
||||
#Type hinting.
|
||||
from typing import Union, List, Dict, TYPE_CHECKING, Optional
|
||||
from typing import List, Dict, TYPE_CHECKING, Optional
|
||||
|
||||
from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
|
||||
from UM.Signal import Signal
|
||||
@ -20,13 +20,15 @@ from UM.Logger import Logger
|
||||
from UM.Message import Message
|
||||
|
||||
from UM.Settings.ContainerRegistry import ContainerRegistry
|
||||
from UM.Settings.InstanceContainer import InstanceContainer
|
||||
from UM.Settings.SettingFunction import SettingFunction
|
||||
from UM.Signal import postponeSignals, CompressTechnique
|
||||
|
||||
from cura.Machines.QualityManager import getMachineDefinitionIDForQualitySearch
|
||||
|
||||
from cura.Machines.VariantManager import VariantType
|
||||
from cura.PrinterOutputDevice import PrinterOutputDevice
|
||||
from cura.PrinterOutput.ConfigurationModel import ConfigurationModel
|
||||
from cura.PrinterOutput.ExtruderConfigurationModel import ExtruderConfigurationModel
|
||||
from cura.PrinterOutput.MaterialOutputModel import MaterialOutputModel
|
||||
from cura.Settings.ExtruderManager import ExtruderManager
|
||||
|
||||
from .CuraStackBuilder import CuraStackBuilder
|
||||
@ -48,7 +50,6 @@ class MachineManager(QObject):
|
||||
self._global_container_stack = None # type: GlobalStack
|
||||
|
||||
self._current_root_material_id = {}
|
||||
self._current_root_material_name = {}
|
||||
self._current_quality_group = None
|
||||
self._current_quality_changes_group = None
|
||||
|
||||
@ -56,11 +57,6 @@ class MachineManager(QObject):
|
||||
|
||||
self.machine_extruder_material_update_dict = collections.defaultdict(list)
|
||||
|
||||
self._error_check_timer = QTimer()
|
||||
self._error_check_timer.setInterval(250)
|
||||
self._error_check_timer.setSingleShot(True)
|
||||
self._error_check_timer.timeout.connect(self._updateStacksHaveErrors)
|
||||
|
||||
self._instance_container_timer = QTimer()
|
||||
self._instance_container_timer.setInterval(250)
|
||||
self._instance_container_timer.setSingleShot(True)
|
||||
@ -109,6 +105,12 @@ class MachineManager(QObject):
|
||||
# There might already be some output devices by the time the signal is connected
|
||||
self._onOutputDevicesChanged()
|
||||
|
||||
self._current_printer_configuration = ConfigurationModel() # Indicates the current configuration setup in this printer
|
||||
self.activeMaterialChanged.connect(self._onCurrentConfigurationChanged)
|
||||
self.activeVariantChanged.connect(self._onCurrentConfigurationChanged)
|
||||
# Force to compute the current configuration
|
||||
self._onCurrentConfigurationChanged()
|
||||
|
||||
self._application.callLater(self.setInitialActiveMachine)
|
||||
|
||||
self._material_incompatible_message = Message(catalog.i18nc("@info:status",
|
||||
@ -119,12 +121,17 @@ class MachineManager(QObject):
|
||||
if containers:
|
||||
containers[0].nameChanged.connect(self._onMaterialNameChanged)
|
||||
|
||||
self._material_manager = self._application._material_manager
|
||||
self._material_manager = self._application.getMaterialManager()
|
||||
self._variant_manager = self._application.getVariantManager()
|
||||
self._quality_manager = self._application.getQualityManager()
|
||||
|
||||
# When the materials lookup table gets updated, it can mean that a material has its name changed, which should
|
||||
# be reflected on the GUI. This signal emission makes sure that it happens.
|
||||
self._material_manager.materialsUpdated.connect(self.rootMaterialChanged)
|
||||
# When the materials get updated, it can be that an activated material's diameter gets changed. In that case,
|
||||
# a material update should be triggered to make sure that the machine still has compatible materials activated.
|
||||
self._material_manager.materialsUpdated.connect(self._updateUponMaterialMetadataChange)
|
||||
self.rootMaterialChanged.connect(self._onRootMaterialChanged)
|
||||
|
||||
activeQualityGroupChanged = pyqtSignal()
|
||||
activeQualityChangesGroupChanged = pyqtSignal()
|
||||
@ -144,6 +151,7 @@ class MachineManager(QObject):
|
||||
blurSettings = pyqtSignal() # Emitted to force fields in the advanced sidebar to un-focus, so they update properly
|
||||
|
||||
outputDevicesChanged = pyqtSignal()
|
||||
currentConfigurationChanged = pyqtSignal() # Emitted every time the current configurations of the machine changes
|
||||
|
||||
rootMaterialChanged = pyqtSignal()
|
||||
|
||||
@ -163,6 +171,39 @@ class MachineManager(QObject):
|
||||
|
||||
self.outputDevicesChanged.emit()
|
||||
|
||||
@pyqtProperty(QObject, notify = currentConfigurationChanged)
|
||||
def currentConfiguration(self):
|
||||
return self._current_printer_configuration
|
||||
|
||||
def _onCurrentConfigurationChanged(self) -> None:
|
||||
if not self._global_container_stack:
|
||||
return
|
||||
|
||||
# Create the configuration model with the current data in Cura
|
||||
self._current_printer_configuration.printerType = self._global_container_stack.definition.getName()
|
||||
self._current_printer_configuration.extruderConfigurations = []
|
||||
for extruder in self._global_container_stack.extruders.values():
|
||||
extruder_configuration = ExtruderConfigurationModel()
|
||||
# For compare just the GUID is needed at this moment
|
||||
mat_type = extruder.material.getMetaDataEntry("material") if extruder.material != self._empty_material_container else None
|
||||
mat_guid = extruder.material.getMetaDataEntry("GUID") if extruder.material != self._empty_material_container else None
|
||||
mat_color = extruder.material.getMetaDataEntry("color_name") if extruder.material != self._empty_material_container else None
|
||||
mat_brand = extruder.material.getMetaDataEntry("brand") if extruder.material != self._empty_material_container else None
|
||||
mat_name = extruder.material.getMetaDataEntry("name") if extruder.material != self._empty_material_container else None
|
||||
material_model = MaterialOutputModel(mat_guid, mat_type, mat_color, mat_brand, mat_name)
|
||||
|
||||
extruder_configuration.position = int(extruder.getMetaDataEntry("position"))
|
||||
extruder_configuration.material = material_model
|
||||
extruder_configuration.hotendID = extruder.variant.getName() if extruder.variant != self._empty_variant_container else None
|
||||
self._current_printer_configuration.extruderConfigurations.append(extruder_configuration)
|
||||
|
||||
self._current_printer_configuration.buildplateConfiguration = self._global_container_stack.getProperty("machine_buildplate_type", "value") if self._global_container_stack.variant != self._empty_variant_container else None
|
||||
self.currentConfigurationChanged.emit()
|
||||
|
||||
@pyqtSlot(QObject, result = bool)
|
||||
def matchesConfiguration(self, configuration: ConfigurationModel) -> bool:
|
||||
return self._current_printer_configuration == configuration
|
||||
|
||||
@pyqtProperty("QVariantList", notify = outputDevicesChanged)
|
||||
def printerOutputDevices(self):
|
||||
return self._printer_output_devices
|
||||
@ -227,15 +268,6 @@ class MachineManager(QObject):
|
||||
del self.machine_extruder_material_update_dict[self._global_container_stack.getId()]
|
||||
|
||||
self.activeQualityGroupChanged.emit()
|
||||
self._error_check_timer.start()
|
||||
|
||||
## Update self._stacks_valid according to _checkStacksForErrors and emit if change.
|
||||
def _updateStacksHaveErrors(self) -> None:
|
||||
old_stacks_have_errors = self._stacks_have_errors
|
||||
self._stacks_have_errors = self._checkStacksHaveErrors()
|
||||
if old_stacks_have_errors != self._stacks_have_errors:
|
||||
self.stacksValidationChanged.emit()
|
||||
Application.getInstance().stacksValidationFinished.emit()
|
||||
|
||||
def _onActiveExtruderStackChanged(self) -> None:
|
||||
self.blurSettings.emit() # Ensure no-one has focus.
|
||||
@ -255,8 +287,6 @@ class MachineManager(QObject):
|
||||
|
||||
self.rootMaterialChanged.emit()
|
||||
|
||||
self._error_check_timer.start()
|
||||
|
||||
def _onInstanceContainersChanged(self, container) -> None:
|
||||
self._instance_container_timer.start()
|
||||
|
||||
@ -265,9 +295,6 @@ class MachineManager(QObject):
|
||||
# Notify UI items, such as the "changed" star in profile pull down menu.
|
||||
self.activeStackValueChanged.emit()
|
||||
|
||||
elif property_name == "validationState":
|
||||
self._error_check_timer.start()
|
||||
|
||||
## Given a global_stack, make sure that it's all valid by searching for this quality group and applying it again
|
||||
def _initMachineState(self, global_stack):
|
||||
material_dict = {}
|
||||
@ -311,9 +338,13 @@ class MachineManager(QObject):
|
||||
|
||||
self.__emitChangedSignals()
|
||||
|
||||
## Given a definition id, return the machine with this id.
|
||||
# Optional: add a list of keys and values to filter the list of machines with the given definition id
|
||||
# \param definition_id \type{str} definition id that needs to look for
|
||||
# \param metadata_filter \type{dict} list of metadata keys and values used for filtering
|
||||
@staticmethod
|
||||
def getMachine(definition_id: str) -> Optional["GlobalStack"]:
|
||||
machines = ContainerRegistry.getInstance().findContainerStacks(type = "machine")
|
||||
def getMachine(definition_id: str, metadata_filter: Dict[str, str] = None) -> Optional["GlobalStack"]:
|
||||
machines = ContainerRegistry.getInstance().findContainerStacks(type = "machine", **metadata_filter)
|
||||
for machine in machines:
|
||||
if machine.definition.getId() == definition_id:
|
||||
return machine
|
||||
@ -334,7 +365,7 @@ class MachineManager(QObject):
|
||||
return False
|
||||
|
||||
if self._global_container_stack.hasErrors():
|
||||
Logger.log("d", "Checking global stack for errors took %0.2f s and we found and error" % (time.time() - time_start))
|
||||
Logger.log("d", "Checking global stack for errors took %0.2f s and we found an error" % (time.time() - time_start))
|
||||
return True
|
||||
|
||||
# Not a very pretty solution, but the extruder manager doesn't really know how many extruders there are
|
||||
@ -418,6 +449,12 @@ class MachineManager(QObject):
|
||||
def stacksHaveErrors(self) -> bool:
|
||||
return bool(self._stacks_have_errors)
|
||||
|
||||
@pyqtProperty(str, notify = globalContainerChanged)
|
||||
def activeMachineDefinitionName(self) -> str:
|
||||
if self._global_container_stack:
|
||||
return self._global_container_stack.definition.getName()
|
||||
return ""
|
||||
|
||||
@pyqtProperty(str, notify = globalContainerChanged)
|
||||
def activeMachineName(self) -> str:
|
||||
if self._global_container_stack:
|
||||
@ -430,6 +467,18 @@ class MachineManager(QObject):
|
||||
return self._global_container_stack.getId()
|
||||
return ""
|
||||
|
||||
@pyqtProperty(str, notify = outputDevicesChanged)
|
||||
def activeMachineNetworkKey(self) -> str:
|
||||
if self._global_container_stack:
|
||||
return self._global_container_stack.getMetaDataEntry("um_network_key")
|
||||
return ""
|
||||
|
||||
@pyqtProperty(str, notify = outputDevicesChanged)
|
||||
def activeMachineNetworkGroupName(self) -> str:
|
||||
if self._global_container_stack:
|
||||
return self._global_container_stack.getMetaDataEntry("connect_group_name")
|
||||
return ""
|
||||
|
||||
@pyqtProperty(QObject, notify = globalContainerChanged)
|
||||
def activeMachine(self) -> Optional["GlobalStack"]:
|
||||
return self._global_container_stack
|
||||
@ -579,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
|
||||
@ -830,10 +879,13 @@ class MachineManager(QObject):
|
||||
return self._default_extruder_position
|
||||
|
||||
## This will fire the propertiesChanged for all settings so they will be updated in the front-end
|
||||
@pyqtSlot()
|
||||
def forceUpdateAllSettings(self):
|
||||
property_names = ["value", "resolve"]
|
||||
for setting_key in self._global_container_stack.getAllKeys():
|
||||
self._global_container_stack.propertiesChanged.emit(setting_key, property_names)
|
||||
with postponeSignals(*self._getContainerChangedSignals(), compress = CompressTechnique.CompressPerParameterValue):
|
||||
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)
|
||||
|
||||
@pyqtSlot(int, bool)
|
||||
def setExtruderEnabled(self, position: int, enabled) -> None:
|
||||
@ -874,27 +926,22 @@ class MachineManager(QObject):
|
||||
|
||||
@pyqtProperty("QVariantList", notify = globalContainerChanged)
|
||||
def currentExtruderPositions(self):
|
||||
if self._global_container_stack is None:
|
||||
return []
|
||||
return sorted(list(self._global_container_stack.extruders.keys()))
|
||||
|
||||
## Update _current_root_material_id when the current root material was changed.
|
||||
def _onRootMaterialChanged(self):
|
||||
self._current_root_material_id = {}
|
||||
|
||||
if self._global_container_stack:
|
||||
for position in self._global_container_stack.extruders:
|
||||
self._current_root_material_id[position] = self._global_container_stack.extruders[position].material.getMetaDataEntry("base_file")
|
||||
|
||||
@pyqtProperty("QVariant", notify = rootMaterialChanged)
|
||||
def currentRootMaterialId(self):
|
||||
# initial filling the current_root_material_id
|
||||
self._current_root_material_id = {}
|
||||
for position in self._global_container_stack.extruders:
|
||||
self._current_root_material_id[position] = self._global_container_stack.extruders[position].material.getMetaDataEntry("base_file")
|
||||
return self._current_root_material_id
|
||||
|
||||
@pyqtProperty("QVariant", notify = rootMaterialChanged)
|
||||
def currentRootMaterialName(self):
|
||||
# initial filling the current_root_material_name
|
||||
if self._global_container_stack:
|
||||
self._current_root_material_name = {}
|
||||
for position in self._global_container_stack.extruders:
|
||||
if position not in self._current_root_material_name:
|
||||
material = self._global_container_stack.extruders[position].material
|
||||
self._current_root_material_name[position] = material.getName()
|
||||
return self._current_root_material_name
|
||||
|
||||
## Return the variant names in the extruder stack(s).
|
||||
## For the variant in the global stack, use activeVariantBuildplateName
|
||||
@pyqtProperty("QVariant", notify = activeVariantChanged)
|
||||
@ -991,15 +1038,12 @@ class MachineManager(QObject):
|
||||
if container_node:
|
||||
self._global_container_stack.extruders[position].material = container_node.getContainer()
|
||||
root_material_id = container_node.metadata["base_file"]
|
||||
root_material_name = container_node.getContainer().getName()
|
||||
else:
|
||||
self._global_container_stack.extruders[position].material = self._empty_material_container
|
||||
root_material_id = None
|
||||
root_material_name = None
|
||||
# The _current_root_material_id is used in the MaterialMenu to see which material is selected
|
||||
if root_material_id != self._current_root_material_id[position]:
|
||||
self._current_root_material_id[position] = root_material_id
|
||||
self._current_root_material_name[position] = root_material_name
|
||||
self.rootMaterialChanged.emit()
|
||||
|
||||
def activeMaterialsCompatible(self):
|
||||
@ -1013,7 +1057,7 @@ class MachineManager(QObject):
|
||||
return True
|
||||
|
||||
## Update current quality type and machine after setting material
|
||||
def _updateQualityWithMaterial(self):
|
||||
def _updateQualityWithMaterial(self, *args):
|
||||
Logger.log("i", "Updating quality/quality_changes due to material change")
|
||||
current_quality_type = None
|
||||
if self._current_quality_group:
|
||||
@ -1058,9 +1102,15 @@ class MachineManager(QObject):
|
||||
extruder = self._global_container_stack.extruders[position]
|
||||
|
||||
current_material_base_name = extruder.material.getMetaDataEntry("base_file")
|
||||
current_variant_name = extruder.variant.getMetaDataEntry("name")
|
||||
current_variant_name = None
|
||||
if extruder.variant.getId() != self._empty_variant_container.getId():
|
||||
current_variant_name = extruder.variant.getMetaDataEntry("name")
|
||||
|
||||
material_diameter = self._global_container_stack.getProperty("material_diameter", "value")
|
||||
from UM.Settings.Interfaces import PropertyEvaluationContext
|
||||
from cura.Settings.CuraContainerStack import _ContainerIndexes
|
||||
context = PropertyEvaluationContext(extruder)
|
||||
context.context["evaluate_from_container_index"] = _ContainerIndexes.DefinitionChanges
|
||||
material_diameter = self._global_container_stack.getProperty("material_diameter", "value", context)
|
||||
candidate_materials = self._material_manager.getAvailableMaterials(
|
||||
self._global_container_stack.definition.getId(),
|
||||
current_variant_name,
|
||||
@ -1075,6 +1125,74 @@ class MachineManager(QObject):
|
||||
self._setMaterial(position, new_material)
|
||||
continue
|
||||
|
||||
# The current material is not available, find the preferred one
|
||||
material_node = self._material_manager.getDefaultMaterial(self._global_container_stack, current_variant_name)
|
||||
if material_node is not None:
|
||||
self._setMaterial(position, material_node)
|
||||
|
||||
## Given a printer definition name, select the right machine instance. In case it doesn't exist, create a new
|
||||
# instance with the same network key.
|
||||
@pyqtSlot(str)
|
||||
def switchPrinterType(self, machine_name):
|
||||
# Don't switch if the user tries to change to the same type of printer
|
||||
if self.activeMachineDefinitionName == machine_name:
|
||||
return
|
||||
# Get the definition id corresponding to this machine name
|
||||
machine_definition_id = ContainerRegistry.getInstance().findDefinitionContainers(name = machine_name)[0].getId()
|
||||
# Try to find a machine with the same network key
|
||||
new_machine = self.getMachine(machine_definition_id, metadata_filter = {"um_network_key": self.activeMachineNetworkKey})
|
||||
# If there is no machine, then create a new one and set it to the non-hidden instance
|
||||
if not new_machine:
|
||||
new_machine = CuraStackBuilder.createMachine(machine_definition_id + "_sync", machine_definition_id)
|
||||
new_machine.addMetaDataEntry("um_network_key", self.activeMachineNetworkKey)
|
||||
new_machine.addMetaDataEntry("connect_group_name", self.activeMachineNetworkGroupName)
|
||||
new_machine.addMetaDataEntry("hidden", False)
|
||||
else:
|
||||
Logger.log("i", "Found a %s with the key %s. Let's use it!", machine_name, self.activeMachineNetworkKey)
|
||||
new_machine.setMetaDataEntry("hidden", False)
|
||||
|
||||
# Set the current printer instance to hidden (the metadata entry must exist)
|
||||
self._global_container_stack.setMetaDataEntry("hidden", True)
|
||||
|
||||
self.setActiveMachine(new_machine.getId())
|
||||
|
||||
@pyqtSlot(QObject)
|
||||
def applyRemoteConfiguration(self, configuration: ConfigurationModel):
|
||||
self.blurSettings.emit()
|
||||
with postponeSignals(*self._getContainerChangedSignals(), compress = CompressTechnique.CompressPerParameterValue):
|
||||
self.switchPrinterType(configuration.printerType)
|
||||
for extruder_configuration in configuration.extruderConfigurations:
|
||||
position = str(extruder_configuration.position)
|
||||
variant_container_node = self._variant_manager.getVariantNode(self._global_container_stack.definition.getId(), extruder_configuration.hotendID)
|
||||
material_container_node = self._material_manager.getMaterialNodeByType(self._global_container_stack, extruder_configuration.hotendID,extruder_configuration.material.guid)
|
||||
if variant_container_node:
|
||||
self._setVariantNode(position, variant_container_node)
|
||||
else:
|
||||
self._global_container_stack.extruders[position].variant = self._empty_variant_container
|
||||
|
||||
if material_container_node:
|
||||
self._setMaterial(position, material_container_node)
|
||||
else:
|
||||
self._global_container_stack.extruders[position].material = self._empty_material_container
|
||||
self._updateMaterialWithVariant(position)
|
||||
|
||||
if configuration.buildplateConfiguration is not None:
|
||||
global_variant_container_node = self._variant_manager.getBuildplateVariantNode(self._global_container_stack.definition.getId(), configuration.buildplateConfiguration)
|
||||
if global_variant_container_node:
|
||||
self._setGlobalVariant(global_variant_container_node)
|
||||
else:
|
||||
self._global_container_stack.variant = self._empty_variant_container
|
||||
else:
|
||||
self._global_container_stack.variant = self._empty_variant_container
|
||||
self._updateQualityWithMaterial()
|
||||
|
||||
## Find all container stacks that has the pair 'key = value' in its metadata and replaces the value with 'new_value'
|
||||
def replaceContainersMetadata(self, key: str, value: str, new_value: str):
|
||||
machines = ContainerRegistry.getInstance().findContainerStacks(type = "machine")
|
||||
for machine in machines:
|
||||
if machine.getMetaDataEntry(key) == value:
|
||||
machine.setMetaDataEntry(key, new_value)
|
||||
|
||||
@pyqtSlot("QVariant")
|
||||
def setGlobalVariant(self, container_node):
|
||||
self.blurSettings.emit()
|
||||
@ -1136,3 +1254,8 @@ class MachineManager(QObject):
|
||||
elif self._current_quality_group:
|
||||
name = self._current_quality_group.name
|
||||
return name
|
||||
|
||||
def _updateUponMaterialMetadataChange(self):
|
||||
with postponeSignals(*self._getContainerChangedSignals(), compress = CompressTechnique.CompressPerParameterValue):
|
||||
self._updateMaterialWithVariant(None)
|
||||
self._updateQualityWithMaterial()
|
||||
|
@ -1,7 +1,7 @@
|
||||
# Copyright (c) 2017 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
from PyQt5.QtCore import QObject, pyqtProperty, pyqtSignal
|
||||
from PyQt5.QtCore import QObject, QTimer, pyqtProperty, pyqtSignal
|
||||
from UM.FlameProfiler import pyqtSlot
|
||||
from UM.Application import Application
|
||||
from UM.Logger import Logger
|
||||
@ -30,6 +30,11 @@ class SettingInheritanceManager(QObject):
|
||||
ExtruderManager.getInstance().activeExtruderChanged.connect(self._onActiveExtruderChanged)
|
||||
self._onActiveExtruderChanged()
|
||||
|
||||
self._update_timer = QTimer()
|
||||
self._update_timer.setInterval(500)
|
||||
self._update_timer.setSingleShot(True)
|
||||
self._update_timer.timeout.connect(self._update)
|
||||
|
||||
settingsWithIntheritanceChanged = pyqtSignal()
|
||||
|
||||
## Get the keys of all children settings with an override.
|
||||
@ -226,9 +231,7 @@ class SettingInheritanceManager(QObject):
|
||||
self._onActiveExtruderChanged()
|
||||
|
||||
def _onContainersChanged(self, container):
|
||||
# TODO: Multiple container changes in sequence now cause quite a few recalculations.
|
||||
# This isn't that big of an issue, but it could be in the future.
|
||||
self._update()
|
||||
self._update_timer.start()
|
||||
|
||||
@staticmethod
|
||||
def createSettingInheritanceManager(engine=None, script_engine=None):
|
||||
|
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()
|
||||
|
@ -265,13 +265,9 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
|
||||
for material_container_file in material_container_files:
|
||||
container_id = self._stripFileToId(material_container_file)
|
||||
|
||||
from hashlib import sha1
|
||||
hex_container_id = sha1(container_id.encode('utf-8')).hexdigest()
|
||||
|
||||
serialized = archive.open(material_container_file).read().decode("utf-8")
|
||||
metadata_list = xml_material_profile.deserializeMetadata(serialized, hex_container_id)
|
||||
reverse_map = {metadata["id"].replace(hex_container_id, container_id): container_id.replace(hex_container_id, container_id)
|
||||
for metadata in metadata_list}
|
||||
metadata_list = xml_material_profile.deserializeMetadata(serialized, container_id)
|
||||
reverse_map = {metadata["id"]: container_id for metadata in metadata_list}
|
||||
reverse_material_id_dict.update(reverse_map)
|
||||
|
||||
material_labels.append(self._getMaterialLabelFromSerialized(serialized))
|
||||
@ -598,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:
|
||||
@ -723,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
|
||||
@ -754,15 +750,13 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
|
||||
quality_changes_containers = self._container_registry.findInstanceContainers(name = quality_changes_name,
|
||||
type = "quality_changes")
|
||||
for container in quality_changes_containers:
|
||||
extruder_definition_id = container.getMetaDataEntry("extruder")
|
||||
if not extruder_definition_id:
|
||||
extruder_position = container.getMetaDataEntry("position")
|
||||
if extruder_position is None:
|
||||
quality_changes_info.global_info.container = container
|
||||
else:
|
||||
extruder_definition_metadata = self._container_registry.findDefinitionContainersMetadata(id = extruder_definition_id)[0]
|
||||
position = extruder_definition_metadata["position"]
|
||||
if position not in quality_changes_info.extruder_info_dict:
|
||||
quality_changes_info.extruder_info_dict[position] = ContainerInfo(None, None, None)
|
||||
container_info = quality_changes_info.extruder_info_dict[position]
|
||||
if extruder_position not in quality_changes_info.extruder_info_dict:
|
||||
quality_changes_info.extruder_info_dict[extruder_position] = ContainerInfo(None, None, None)
|
||||
container_info = quality_changes_info.extruder_info_dict[extruder_position]
|
||||
container_info.container = container
|
||||
|
||||
# If there is no quality changes for any extruder, create one.
|
||||
|
@ -10,7 +10,6 @@ from UM.Logger import Logger
|
||||
from UM.Message import Message
|
||||
from UM.PluginRegistry import PluginRegistry
|
||||
from UM.Resources import Resources
|
||||
from UM.Settings.Validator import ValidatorState #To find if a setting is in an error state. We can't slice then.
|
||||
from UM.Platform import Platform
|
||||
from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
|
||||
from UM.Qt.Duration import DurationFormat
|
||||
@ -32,6 +31,7 @@ import Arcus
|
||||
from UM.i18n import i18nCatalog
|
||||
catalog = i18nCatalog("cura")
|
||||
|
||||
|
||||
class CuraEngineBackend(QObject, Backend):
|
||||
|
||||
backendError = Signal()
|
||||
@ -62,23 +62,26 @@ class CuraEngineBackend(QObject, Backend):
|
||||
default_engine_location = execpath
|
||||
break
|
||||
|
||||
self._application = Application.getInstance()
|
||||
self._multi_build_plate_model = None
|
||||
self._machine_error_checker = None
|
||||
|
||||
if not default_engine_location:
|
||||
raise EnvironmentError("Could not find CuraEngine")
|
||||
|
||||
Logger.log("i", "Found CuraEngine at: %s" %(default_engine_location))
|
||||
Logger.log("i", "Found CuraEngine at: %s", default_engine_location)
|
||||
|
||||
default_engine_location = os.path.abspath(default_engine_location)
|
||||
Preferences.getInstance().addPreference("backend/location", default_engine_location)
|
||||
|
||||
# Workaround to disable layer view processing if layer view is not active.
|
||||
self._layer_view_active = False
|
||||
Application.getInstance().getController().activeViewChanged.connect(self._onActiveViewChanged)
|
||||
Application.getInstance().getMultiBuildPlateModel().activeBuildPlateChanged.connect(self._onActiveViewChanged)
|
||||
self._onActiveViewChanged()
|
||||
|
||||
self._stored_layer_data = []
|
||||
self._stored_optimized_layer_data = {} # key is build plate number, then arrays are stored until they go to the ProcessSlicesLayersJob
|
||||
|
||||
self._scene = Application.getInstance().getController().getScene()
|
||||
self._scene = self._application.getController().getScene()
|
||||
self._scene.sceneChanged.connect(self._onSceneChanged)
|
||||
|
||||
# Triggers for auto-slicing. Auto-slicing is triggered as follows:
|
||||
@ -86,20 +89,10 @@ class CuraEngineBackend(QObject, Backend):
|
||||
# - whenever there is a value change, we start the timer
|
||||
# - sometimes an error check can get scheduled for a value change, in that case, we ONLY want to start the
|
||||
# auto-slicing timer when that error check is finished
|
||||
# If there is an error check, it will set the "_is_error_check_scheduled" flag, stop the auto-slicing timer,
|
||||
# and only wait for the error check to be finished to start the auto-slicing timer again.
|
||||
# If there is an error check, stop the auto-slicing timer, and only wait for the error check to be finished
|
||||
# to start the auto-slicing timer again.
|
||||
#
|
||||
self._global_container_stack = None
|
||||
Application.getInstance().globalContainerStackChanged.connect(self._onGlobalStackChanged)
|
||||
self._onGlobalStackChanged()
|
||||
|
||||
Application.getInstance().stacksValidationFinished.connect(self._onStackErrorCheckFinished)
|
||||
# extruder enable / disable. Actually wanted to use machine manager here, but the initialization order causes it to crash
|
||||
ExtruderManager.getInstance().extrudersChanged.connect(self._extruderChanged)
|
||||
|
||||
# A flag indicating if an error check was scheduled
|
||||
# If so, we will stop the auto-slice timer and start upon the error check
|
||||
self._is_error_check_scheduled = False
|
||||
|
||||
# Listeners for receiving messages from the back-end.
|
||||
self._message_handlers["cura.proto.Layer"] = self._onLayerMessage
|
||||
@ -125,13 +118,6 @@ class CuraEngineBackend(QObject, Backend):
|
||||
self._last_num_objects = defaultdict(int) # Count number of objects to see if there is something changed
|
||||
self._postponed_scene_change_sources = [] # scene change is postponed (by a tool)
|
||||
|
||||
self.backendQuit.connect(self._onBackendQuit)
|
||||
self.backendConnected.connect(self._onBackendConnected)
|
||||
|
||||
# When a tool operation is in progress, don't slice. So we need to listen for tool operations.
|
||||
Application.getInstance().getController().toolOperationStarted.connect(self._onToolOperationStarted)
|
||||
Application.getInstance().getController().toolOperationStopped.connect(self._onToolOperationStopped)
|
||||
|
||||
self._slice_start_time = None
|
||||
|
||||
Preferences.getInstance().addPreference("general/auto_slice", True)
|
||||
@ -146,6 +132,30 @@ class CuraEngineBackend(QObject, Backend):
|
||||
self.determineAutoSlicing()
|
||||
Preferences.getInstance().preferenceChanged.connect(self._onPreferencesChanged)
|
||||
|
||||
self._application.initializationFinished.connect(self.initialize)
|
||||
|
||||
def initialize(self):
|
||||
self._multi_build_plate_model = self._application.getMultiBuildPlateModel()
|
||||
|
||||
self._application.getController().activeViewChanged.connect(self._onActiveViewChanged)
|
||||
self._multi_build_plate_model.activeBuildPlateChanged.connect(self._onActiveViewChanged)
|
||||
|
||||
self._application.globalContainerStackChanged.connect(self._onGlobalStackChanged)
|
||||
self._onGlobalStackChanged()
|
||||
|
||||
# extruder enable / disable. Actually wanted to use machine manager here, but the initialization order causes it to crash
|
||||
ExtruderManager.getInstance().extrudersChanged.connect(self._extruderChanged)
|
||||
|
||||
self.backendQuit.connect(self._onBackendQuit)
|
||||
self.backendConnected.connect(self._onBackendConnected)
|
||||
|
||||
# When a tool operation is in progress, don't slice. So we need to listen for tool operations.
|
||||
self._application.getController().toolOperationStarted.connect(self._onToolOperationStarted)
|
||||
self._application.getController().toolOperationStopped.connect(self._onToolOperationStopped)
|
||||
|
||||
self._machine_error_checker = self._application.getMachineErrorChecker()
|
||||
self._machine_error_checker.errorCheckFinished.connect(self._onStackErrorCheckFinished)
|
||||
|
||||
## Terminate the engine process.
|
||||
#
|
||||
# This function should terminate the engine process.
|
||||
@ -531,11 +541,9 @@ class CuraEngineBackend(QObject, Backend):
|
||||
|
||||
elif property == "validationState":
|
||||
if self._use_timer:
|
||||
self._is_error_check_scheduled = True
|
||||
self._change_timer.stop()
|
||||
|
||||
def _onStackErrorCheckFinished(self):
|
||||
self._is_error_check_scheduled = False
|
||||
if not self._slicing and self._build_plates_to_be_sliced:
|
||||
self.needsSlicing()
|
||||
self._onChanged()
|
||||
@ -561,12 +569,15 @@ class CuraEngineBackend(QObject, Backend):
|
||||
self.processingProgress.emit(message.amount)
|
||||
self.backendStateChange.emit(BackendState.Processing)
|
||||
|
||||
# testing
|
||||
def _invokeSlice(self):
|
||||
if self._use_timer:
|
||||
# if the error check is scheduled, wait for the error check finish signal to trigger auto-slice,
|
||||
# otherwise business as usual
|
||||
if self._is_error_check_scheduled:
|
||||
if self._machine_error_checker is None:
|
||||
self._change_timer.stop()
|
||||
return
|
||||
|
||||
if self._machine_error_checker.needToWaitForResult:
|
||||
self._change_timer.stop()
|
||||
else:
|
||||
self._change_timer.start()
|
||||
@ -632,7 +643,11 @@ class CuraEngineBackend(QObject, Backend):
|
||||
if self._use_timer:
|
||||
# if the error check is scheduled, wait for the error check finish signal to trigger auto-slice,
|
||||
# otherwise business as usual
|
||||
if self._is_error_check_scheduled:
|
||||
if self._machine_error_checker is None:
|
||||
self._change_timer.stop()
|
||||
return
|
||||
|
||||
if self._machine_error_checker.needToWaitForResult:
|
||||
self._change_timer.stop()
|
||||
else:
|
||||
self._change_timer.start()
|
||||
@ -786,7 +801,7 @@ class CuraEngineBackend(QObject, Backend):
|
||||
self._change_timer.start()
|
||||
|
||||
def _extruderChanged(self):
|
||||
for build_plate_number in range(Application.getInstance().getMultiBuildPlateModel().maxBuildPlate + 1):
|
||||
for build_plate_number in range(self._multi_build_plate_model.maxBuildPlate + 1):
|
||||
if build_plate_number not in self._build_plates_to_be_sliced:
|
||||
self._build_plates_to_be_sliced.append(build_plate_number)
|
||||
self._invokeSlice()
|
||||
|
@ -281,7 +281,7 @@ class StartSliceJob(Job):
|
||||
default_extruder_position = int(Application.getInstance().getMachineManager().defaultExtruderPosition)
|
||||
result = {}
|
||||
for key in stack.getAllKeys():
|
||||
setting_type = stack.getProperty(key, "type")
|
||||
setting_type = stack.definition.getProperty(key, "type")
|
||||
value = stack.getProperty(key, "value")
|
||||
if setting_type == "extruder" and value == -1:
|
||||
# replace with the default value
|
||||
|
@ -382,6 +382,11 @@ Cura.MachineAction
|
||||
property string settingKey: "machine_nozzle_size"
|
||||
property string label: catalog.i18nc("@label", "Nozzle size")
|
||||
property string unit: catalog.i18nc("@label", "mm")
|
||||
function afterOnEditingFinished()
|
||||
{
|
||||
// Somehow the machine_nozzle_size dependent settings are not updated otherwise
|
||||
Cura.MachineManager.forceUpdateAllSettings()
|
||||
}
|
||||
property bool isExtruderSetting: true
|
||||
}
|
||||
|
||||
@ -889,4 +894,4 @@ Cura.MachineAction
|
||||
watchedProperties: [ "value" ]
|
||||
storeIndex: manager.containerIndex
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -22,14 +22,7 @@ class MonitorStage(CuraStage):
|
||||
|
||||
def _setActivePrintJob(self, print_job):
|
||||
if self._active_print_job != print_job:
|
||||
if self._active_print_job:
|
||||
self._active_print_job.stateChanged.disconnect(self._updateIconSource)
|
||||
self._active_print_job = print_job
|
||||
if self._active_print_job:
|
||||
self._active_print_job.stateChanged.connect(self._updateIconSource)
|
||||
|
||||
# Ensure that the right icon source is returned.
|
||||
self._updateIconSource()
|
||||
|
||||
def _setActivePrinter(self, printer):
|
||||
if self._active_printer != printer:
|
||||
@ -43,9 +36,6 @@ class MonitorStage(CuraStage):
|
||||
else:
|
||||
self._setActivePrintJob(None)
|
||||
|
||||
# Ensure that the right icon source is returned.
|
||||
self._updateIconSource()
|
||||
|
||||
def _onActivePrintJobChanged(self):
|
||||
self._setActivePrintJob(self._active_printer.activePrintJob)
|
||||
|
||||
@ -58,22 +48,13 @@ class MonitorStage(CuraStage):
|
||||
new_output_device = Application.getInstance().getMachineManager().printerOutputDevices[0]
|
||||
if new_output_device != self._printer_output_device:
|
||||
if self._printer_output_device:
|
||||
self._printer_output_device.acceptsCommandsChanged.disconnect(self._updateIconSource)
|
||||
self._printer_output_device.connectionStateChanged.disconnect(self._updateIconSource)
|
||||
self._printer_output_device.printersChanged.disconnect(self._onActivePrinterChanged)
|
||||
|
||||
self._printer_output_device = new_output_device
|
||||
|
||||
self._printer_output_device.acceptsCommandsChanged.connect(self._updateIconSource)
|
||||
self._printer_output_device.printersChanged.connect(self._onActivePrinterChanged)
|
||||
self._printer_output_device.connectionStateChanged.connect(self._updateIconSource)
|
||||
self._setActivePrinter(self._printer_output_device.activePrinter)
|
||||
|
||||
# Force an update of the icon source
|
||||
self._updateIconSource()
|
||||
except IndexError:
|
||||
#If index error occurs, then the icon on monitor button also should be updated
|
||||
self._updateIconSource()
|
||||
pass
|
||||
|
||||
def _onEngineCreated(self):
|
||||
@ -82,7 +63,6 @@ class MonitorStage(CuraStage):
|
||||
self._onOutputDevicesChanged()
|
||||
self._updateMainOverlay()
|
||||
self._updateSidebar()
|
||||
self._updateIconSource()
|
||||
|
||||
def _updateMainOverlay(self):
|
||||
main_component_path = os.path.join(PluginRegistry.getInstance().getPluginPath("MonitorStage"), "MonitorMainView.qml")
|
||||
@ -92,46 +72,3 @@ class MonitorStage(CuraStage):
|
||||
# TODO: currently the sidebar component for prepare and monitor stages is the same, this will change with the printer output device refactor!
|
||||
sidebar_component_path = os.path.join(Resources.getPath(Application.getInstance().ResourceTypes.QmlFiles), "Sidebar.qml")
|
||||
self.addDisplayComponent("sidebar", sidebar_component_path)
|
||||
|
||||
def _updateIconSource(self):
|
||||
if Application.getInstance().getTheme() is not None:
|
||||
icon_name = self._getActiveOutputDeviceStatusIcon()
|
||||
self.setIconSource(Application.getInstance().getTheme().getIcon(icon_name))
|
||||
|
||||
## Find the correct status icon depending on the active output device state
|
||||
def _getActiveOutputDeviceStatusIcon(self):
|
||||
# We assume that you are monitoring the device with the highest priority.
|
||||
try:
|
||||
output_device = Application.getInstance().getMachineManager().printerOutputDevices[0]
|
||||
except IndexError:
|
||||
return "tab_status_unknown"
|
||||
|
||||
if not output_device.acceptsCommands:
|
||||
return "tab_status_unknown"
|
||||
|
||||
if output_device.activePrinter is None:
|
||||
return "tab_status_connected"
|
||||
|
||||
# TODO: refactor to use enum instead of hardcoded strings?
|
||||
if output_device.activePrinter.state == "maintenance":
|
||||
return "tab_status_busy"
|
||||
|
||||
if output_device.activePrinter.activePrintJob is None:
|
||||
return "tab_status_connected"
|
||||
|
||||
if output_device.activePrinter.activePrintJob.state in ["printing", "pre_print", "pausing", "resuming"]:
|
||||
return "tab_status_busy"
|
||||
|
||||
if output_device.activePrinter.activePrintJob.state == "wait_cleanup":
|
||||
return "tab_status_finished"
|
||||
|
||||
if output_device.activePrinter.activePrintJob.state in ["ready", ""]:
|
||||
return "tab_status_connected"
|
||||
|
||||
if output_device.activePrinter.activePrintJob.state == "paused":
|
||||
return "tab_status_paused"
|
||||
|
||||
if output_device.activePrinter.activePrintJob.state == "error":
|
||||
return "tab_status_stopped"
|
||||
|
||||
return "tab_status_unknown"
|
||||
|
@ -158,9 +158,10 @@ class SimulationView(View):
|
||||
return self._nozzle_node
|
||||
|
||||
def _onSceneChanged(self, node):
|
||||
self.setActivity(False)
|
||||
self.calculateMaxLayers()
|
||||
self.calculateMaxPathsOnLayer(self._current_layer_num)
|
||||
if node.getMeshData() is not None:
|
||||
self.setActivity(False)
|
||||
self.calculateMaxLayers()
|
||||
self.calculateMaxPathsOnLayer(self._current_layer_num)
|
||||
|
||||
def isBusy(self):
|
||||
return self._busy
|
||||
|
@ -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,34 +351,45 @@ 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"]
|
||||
|
||||
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"])
|
||||
printer.updateType(data["machine_variant"])
|
||||
|
||||
# Do not store the buildplate information that comes from connect if the current printer has not buildplate information
|
||||
if "build_plate" in data and machine_definition.getMetaDataEntry("has_variant_buildplates", False):
|
||||
printer.updateBuildplateName(data["build_plate"]["type"])
|
||||
if not data["enabled"]:
|
||||
printer.updateState("disabled")
|
||||
else:
|
||||
@ -416,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
|
||||
|
||||
@ -427,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
|
||||
|
@ -97,6 +97,25 @@ class DiscoverUM3Action(MachineAction):
|
||||
else:
|
||||
return []
|
||||
|
||||
@pyqtSlot(str)
|
||||
def setGroupName(self, group_name):
|
||||
Logger.log("d", "Attempting to set the group name of the active machine to %s", group_name)
|
||||
global_container_stack = Application.getInstance().getGlobalContainerStack()
|
||||
if global_container_stack:
|
||||
meta_data = global_container_stack.getMetaData()
|
||||
if "connect_group_name" in meta_data:
|
||||
previous_connect_group_name = meta_data["connect_group_name"]
|
||||
global_container_stack.setMetaDataEntry("connect_group_name", group_name)
|
||||
# Find all the places where there is the same group name and change it accordingly
|
||||
Application.getInstance().getMachineManager().replaceContainersMetadata(key = "connect_group_name", value = previous_connect_group_name, new_value = group_name)
|
||||
else:
|
||||
global_container_stack.addMetaDataEntry("connect_group_name", group_name)
|
||||
global_container_stack.addMetaDataEntry("hidden", False)
|
||||
|
||||
if self._network_plugin:
|
||||
# Ensure that the connection states are refreshed.
|
||||
self._network_plugin.reCheckConnections()
|
||||
|
||||
@pyqtSlot(str)
|
||||
def setKey(self, key):
|
||||
Logger.log("d", "Attempting to set the network key of the active machine to %s", key)
|
||||
@ -104,11 +123,13 @@ class DiscoverUM3Action(MachineAction):
|
||||
if global_container_stack:
|
||||
meta_data = global_container_stack.getMetaData()
|
||||
if "um_network_key" in meta_data:
|
||||
previous_network_key= meta_data["um_network_key"]
|
||||
global_container_stack.setMetaDataEntry("um_network_key", key)
|
||||
# Delete old authentication data.
|
||||
Logger.log("d", "Removing old authentication id %s for device %s", global_container_stack.getMetaDataEntry("network_authentication_id", None), key)
|
||||
global_container_stack.removeMetaDataEntry("network_authentication_id")
|
||||
global_container_stack.removeMetaDataEntry("network_authentication_key")
|
||||
Application.getInstance().getMachineManager().replaceContainersMetadata(key = "um_network_key", value = previous_network_key, new_value = key)
|
||||
else:
|
||||
global_container_stack.addMetaDataEntry("um_network_key", key)
|
||||
|
||||
|
@ -32,10 +32,12 @@ Cura.MachineAction
|
||||
if(base.selectedDevice && base.completeProperties)
|
||||
{
|
||||
var printerKey = base.selectedDevice.key
|
||||
var printerName = base.selectedDevice.name // TODO To change when the groups have a name
|
||||
if(manager.getStoredKey() != printerKey)
|
||||
{
|
||||
manager.setKey(printerKey);
|
||||
completed();
|
||||
manager.setKey(printerKey)
|
||||
manager.setGroupName(printerName) // TODO To change when the groups have a name
|
||||
completed()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -303,7 +305,7 @@ Cura.MachineAction
|
||||
Button
|
||||
{
|
||||
text: catalog.i18nc("@action:button", "Connect")
|
||||
enabled: (base.selectedDevice && base.completeProperties) ? true : false
|
||||
enabled: (base.selectedDevice && base.completeProperties && base.selectedDevice.clusterSize > 0) ? true : false
|
||||
onClicked: connectToPrinter()
|
||||
}
|
||||
}
|
||||
|
@ -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"}
|
||||
|
@ -277,7 +277,7 @@ class XmlMaterialProfile(InstanceContainer):
|
||||
|
||||
# Compatible is a special case, as it's added as a meta data entry (instead of an instance).
|
||||
material_container = variant_dict["material_container"]
|
||||
compatible = container.getMetaDataEntry("compatible")
|
||||
compatible = material_container.getMetaDataEntry("compatible")
|
||||
if compatible is not None:
|
||||
builder.start("setting", {"key": "hardware compatible"})
|
||||
if compatible:
|
||||
@ -838,15 +838,11 @@ class XmlMaterialProfile(InstanceContainer):
|
||||
if machine_compatibility:
|
||||
new_material_id = container_id + "_" + machine_id
|
||||
|
||||
# The child or derived material container may already exist. This can happen when a material in a
|
||||
# project file and the a material in Cura have the same ID.
|
||||
# In the case if a derived material already exists, override that material container because if
|
||||
# the data in the parent material has been changed, the derived ones should be updated too.
|
||||
found_materials = ContainerRegistry.getInstance().findInstanceContainersMetadata(id = new_material_id)
|
||||
if found_materials:
|
||||
new_material_metadata = found_materials[0]
|
||||
else:
|
||||
new_material_metadata = {}
|
||||
# Do not look for existing container/container metadata with the same ID although they may exist.
|
||||
# In project loading and perhaps some other places, we only want to get information (metadata)
|
||||
# from a file without changing the current state of the system. If we overwrite the existing
|
||||
# metadata here, deserializeMetadata() will not be safe for retrieving information.
|
||||
new_material_metadata = {}
|
||||
|
||||
new_material_metadata.update(base_metadata)
|
||||
new_material_metadata["id"] = new_material_id
|
||||
@ -854,8 +850,7 @@ class XmlMaterialProfile(InstanceContainer):
|
||||
new_material_metadata["machine_manufacturer"] = machine_manufacturer
|
||||
new_material_metadata["definition"] = machine_id
|
||||
|
||||
if len(found_materials) == 0: #This is a new material.
|
||||
result_metadata.append(new_material_metadata)
|
||||
result_metadata.append(new_material_metadata)
|
||||
|
||||
buildplates = machine.iterfind("./um:buildplate", cls.__namespaces)
|
||||
buildplate_map = {}
|
||||
@ -866,12 +861,12 @@ class XmlMaterialProfile(InstanceContainer):
|
||||
if buildplate_id is None:
|
||||
continue
|
||||
|
||||
variant_containers = ContainerRegistry.getInstance().findInstanceContainersMetadata(id = buildplate_id)
|
||||
if not variant_containers:
|
||||
variant_metadata = ContainerRegistry.getInstance().findInstanceContainersMetadata(id = buildplate_id)
|
||||
if not variant_metadata:
|
||||
# It is not really properly defined what "ID" is so also search for variants by name.
|
||||
variant_containers = ContainerRegistry.getInstance().findInstanceContainersMetadata(definition = machine_id, name = buildplate_id)
|
||||
variant_metadata = ContainerRegistry.getInstance().findInstanceContainersMetadata(definition = machine_id, name = buildplate_id)
|
||||
|
||||
if not variant_containers:
|
||||
if not variant_metadata:
|
||||
continue
|
||||
|
||||
settings = buildplate.iterfind("./um:setting", cls.__namespaces)
|
||||
@ -900,12 +895,8 @@ class XmlMaterialProfile(InstanceContainer):
|
||||
|
||||
new_hotend_specific_material_id = container_id + "_" + machine_id + "_" + hotend_name.replace(" ", "_")
|
||||
|
||||
# Same as machine compatibility, keep the derived material containers consistent with the parent material
|
||||
found_materials = ContainerRegistry.getInstance().findInstanceContainersMetadata(id = new_hotend_specific_material_id)
|
||||
if found_materials:
|
||||
new_hotend_material_metadata = found_materials[0]
|
||||
else:
|
||||
new_hotend_material_metadata = {}
|
||||
# Same as above, do not overwrite existing metadata.
|
||||
new_hotend_material_metadata = {}
|
||||
|
||||
new_hotend_material_metadata.update(base_metadata)
|
||||
new_hotend_material_metadata["variant_name"] = hotend_name
|
||||
@ -917,8 +908,7 @@ class XmlMaterialProfile(InstanceContainer):
|
||||
new_hotend_material_metadata["buildplate_compatible"] = buildplate_map["buildplate_compatible"]
|
||||
new_hotend_material_metadata["buildplate_recommended"] = buildplate_map["buildplate_recommended"]
|
||||
|
||||
if len(found_materials) == 0:
|
||||
result_metadata.append(new_hotend_material_metadata)
|
||||
result_metadata.append(new_hotend_material_metadata)
|
||||
|
||||
# there is only one ID for a machine. Once we have reached here, it means we have already found
|
||||
# a workable ID for that machine, so there is no need to continue
|
||||
|
@ -215,7 +215,7 @@
|
||||
{
|
||||
"label": "Number of Extruders that are enabled",
|
||||
"description": "Number of extruder trains that are enabled; automatically set in software",
|
||||
"default_value": "machine_extruder_count",
|
||||
"value": "machine_extruder_count",
|
||||
"minimum_value": "1",
|
||||
"maximum_value": "16",
|
||||
"type": "int",
|
||||
@ -632,6 +632,73 @@
|
||||
"settable_per_extruder": false,
|
||||
"settable_per_meshgroup": false
|
||||
},
|
||||
"machine_steps_per_mm_x":
|
||||
{
|
||||
"label": "Steps per Millimeter (X)",
|
||||
"description": "How many steps of the stepper motor will result in one millimeter of movement in the X direction.",
|
||||
"type": "int",
|
||||
"default_value": 50,
|
||||
"minimum_value": "0.0000001",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true
|
||||
},
|
||||
"machine_steps_per_mm_y":
|
||||
{
|
||||
"label": "Steps per Millimeter (Y)",
|
||||
"description": "How many steps of the stepper motor will result in one millimeter of movement in the Y direction.",
|
||||
"type": "int",
|
||||
"default_value": 50,
|
||||
"minimum_value": "0.0000001",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true
|
||||
},
|
||||
"machine_steps_per_mm_z":
|
||||
{
|
||||
"label": "Steps per Millimeter (Z)",
|
||||
"description": "How many steps of the stepper motor will result in one millimeter of movement in the Z direction.",
|
||||
"type": "int",
|
||||
"default_value": 50,
|
||||
"minimum_value": "0.0000001",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true
|
||||
},
|
||||
"machine_steps_per_mm_e":
|
||||
{
|
||||
"label": "Steps per Millimeter (E)",
|
||||
"description": "How many steps of the stepper motors will result in one millimeter of extrusion.",
|
||||
"type": "int",
|
||||
"default_value": 1600,
|
||||
"minimum_value": "0.0000001",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true
|
||||
},
|
||||
"machine_endstop_positive_direction_x":
|
||||
{
|
||||
"label": "X Endstop in Positive Direction",
|
||||
"description": "Whether the endstop of the X axis is in the positive direction (high X coordinate) or negative (low X coordinate).",
|
||||
"type": "bool",
|
||||
"default_value": false,
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true
|
||||
},
|
||||
"machine_endstop_positive_direction_y":
|
||||
{
|
||||
"label": "Y Endstop in Positive Direction",
|
||||
"description": "Whether the endstop of the Y axis is in the positive direction (high Y coordinate) or negative (low Y coordinate).",
|
||||
"type": "bool",
|
||||
"default_value": false,
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true
|
||||
},
|
||||
"machine_endstop_positive_direction_z":
|
||||
{
|
||||
"label": "Z Endstop in Positive Direction",
|
||||
"description": "Whether the endstop of the Z axis is in the positive direction (high Z coordinate) or negative (low Z coordinate).",
|
||||
"type": "bool",
|
||||
"default_value": true,
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true
|
||||
},
|
||||
"machine_minimum_feedrate":
|
||||
{
|
||||
"label": "Minimum Feedrate",
|
||||
@ -642,6 +709,16 @@
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": false,
|
||||
"settable_per_meshgroup": false
|
||||
},
|
||||
"machine_feeder_wheel_diameter":
|
||||
{
|
||||
"label": "Feeder Wheel Diameter",
|
||||
"description": "The diameter of the wheel that drives the material in the feeder.",
|
||||
"unit": "mm",
|
||||
"type": "float",
|
||||
"default_value": 10.0,
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -25,8 +25,7 @@
|
||||
"default_value": true
|
||||
},
|
||||
"machine_nozzle_size": {
|
||||
"default_value": 0.4,
|
||||
"minimum_value": "0.001"
|
||||
"default_value": 0.4
|
||||
},
|
||||
"machine_head_with_fans_polygon": {
|
||||
"default_value": [
|
||||
@ -36,6 +35,21 @@
|
||||
[ 18, 35 ]
|
||||
]
|
||||
},
|
||||
"machine_max_feedrate_z": {
|
||||
"default_value": 400
|
||||
},
|
||||
"machine_steps_per_mm_x": {
|
||||
"default_value": 93
|
||||
},
|
||||
"machine_steps_per_mm_y": {
|
||||
"default_value": 93
|
||||
},
|
||||
"machine_steps_per_mm_z": {
|
||||
"default_value": 1600
|
||||
},
|
||||
"machine_steps_per_mm_e": {
|
||||
"default_value": 92
|
||||
},
|
||||
"gantry_height": {
|
||||
"default_value": 55
|
||||
},
|
||||
|
38
resources/definitions/printrbot_simple_makers_kit.def.json
Normal file
38
resources/definitions/printrbot_simple_makers_kit.def.json
Normal file
@ -0,0 +1,38 @@
|
||||
{
|
||||
"version": 2,
|
||||
"name": "Printrbot Simple Maker's Kit (1405)",
|
||||
"inherits": "fdmprinter",
|
||||
"metadata": {
|
||||
"visible": true,
|
||||
"author": "Timur Tabi",
|
||||
"manufacturer": "Printrbot",
|
||||
"file_formats": "text/x-gcode"
|
||||
},
|
||||
|
||||
"overrides": {
|
||||
"machine_name": { "default_value": "Printrbot Simple Maker's Kit (1405)" },
|
||||
"machine_heated_bed": { "default_value": false },
|
||||
"machine_width": { "default_value": 100 },
|
||||
"machine_depth": { "default_value": 100 },
|
||||
"machine_height": { "default_value": 115 },
|
||||
"material_diameter": { "default_value": 1.75 },
|
||||
"machine_nozzle_size": { "default_value": 0.4 },
|
||||
"machine_head_with_fans_polygon": {
|
||||
"default_value": [
|
||||
[-40, 1000],
|
||||
[-40, -10],
|
||||
[60, 1000],
|
||||
[60, -10]
|
||||
]
|
||||
},
|
||||
"gantry_height": { "default_value": 1000 },
|
||||
"machine_gcode_flavor": { "default_value": "RepRap (Marlin/Sprinter)" },
|
||||
|
||||
"machine_start_gcode": {
|
||||
"default_value": "G21 ;metric values\nG90 ;absolute positioning\nM82 ;set extruder to absolute mode\nM107 ;start with the fan off\nG28 X0 Y0 ;home X/Y\nG28 Z0 ;home Z\nG92 E0 ;zero the extruded length\nG29 ;initiate auto bed leveling sequence"
|
||||
},
|
||||
"machine_end_gcode": {
|
||||
"default_value": "M104 S0 ;extruder heater off\nM140 S0 ;heated bed heater off (if you have it)\nM106 S0 ;fan off\nG91 ;relative positioning\nG1 E-1 F300 ;retract the filament a bit\nG1 Z+1 E-5 F9000 ;move Z up a bit and retract even more\nG28 X0 Y0 ;home X/Y, so the head is out of the way\nM84 ;steppers off\nG90 ;absolute positioning"
|
||||
}
|
||||
}
|
||||
}
|
@ -126,7 +126,7 @@
|
||||
"retraction_count_max": { "value": "10" },
|
||||
"retraction_extrusion_window": { "value": "1" },
|
||||
"retraction_hop": { "value": "2" },
|
||||
"retraction_hop_enabled": { "value": "True" },
|
||||
"retraction_hop_enabled": { "value": "extruders_enabled_count > 1" },
|
||||
"retraction_hop_only_when_collides": { "value": "True" },
|
||||
"retraction_min_travel": { "value": "5" },
|
||||
"retraction_prime_speed": { "value": "15" },
|
||||
@ -150,7 +150,7 @@
|
||||
"switch_extruder_prime_speed": { "value": "15" },
|
||||
"switch_extruder_retraction_amount": { "value": "8" },
|
||||
"top_bottom_thickness": { "value": "1" },
|
||||
"travel_avoid_distance": { "value": "3" },
|
||||
"travel_avoid_distance": { "value": "3 if extruders_enabled_count > 1 else machine_nozzle_tip_outer_diameter / 2 * 1.5" },
|
||||
"wall_0_inset": { "value": "0" },
|
||||
"wall_line_width_x": { "value": "round(wall_line_width * 0.3 / 0.35, 2)" },
|
||||
"wall_thickness": { "value": "1" },
|
||||
|
@ -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,11 @@ import "Menus"
|
||||
|
||||
ToolButton
|
||||
{
|
||||
text: Cura.MachineManager.activeMachineName
|
||||
id: base
|
||||
property bool isNetworkPrinter: Cura.MachineManager.activeMachineNetworkKey != ""
|
||||
property bool printerConnected: Cura.MachineManager.printerOutputDevices.length != 0
|
||||
property var printerStatus: Cura.MachineManager.printerOutputDevices.length != 0 ? "connected" : "disconnected"
|
||||
text: isNetworkPrinter ? Cura.MachineManager.activeMachineNetworkGroupName : Cura.MachineManager.activeMachineName
|
||||
|
||||
tooltip: Cura.MachineManager.activeMachineName
|
||||
|
||||
@ -22,16 +26,13 @@ ToolButton
|
||||
{
|
||||
color:
|
||||
{
|
||||
if(control.pressed)
|
||||
{
|
||||
if (control.pressed) {
|
||||
return UM.Theme.getColor("sidebar_header_active");
|
||||
}
|
||||
else if(control.hovered)
|
||||
{
|
||||
else if (control.hovered) {
|
||||
return UM.Theme.getColor("sidebar_header_hover");
|
||||
}
|
||||
else
|
||||
{
|
||||
else {
|
||||
return UM.Theme.getColor("sidebar_header_bar");
|
||||
}
|
||||
}
|
||||
@ -50,18 +51,32 @@ ToolButton
|
||||
color: UM.Theme.getColor("text_emphasis")
|
||||
source: UM.Theme.getIcon("arrow_bottom")
|
||||
}
|
||||
|
||||
PrinterStatusIcon
|
||||
{
|
||||
id: printerStatusIcon
|
||||
visible: printerConnected || isNetworkPrinter
|
||||
status: printerStatus
|
||||
anchors
|
||||
{
|
||||
verticalCenter: parent.verticalCenter
|
||||
left: parent.left
|
||||
leftMargin: UM.Theme.getSize("sidebar_margin").width
|
||||
}
|
||||
}
|
||||
|
||||
Label
|
||||
{
|
||||
id: sidebarComboBoxLabel
|
||||
color: UM.Theme.getColor("sidebar_header_text_active")
|
||||
text: control.text;
|
||||
elide: Text.ElideRight;
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: UM.Theme.getSize("default_margin").width * 2
|
||||
anchors.left: isNetworkPrinter ? printerStatusIcon.right : parent.left;
|
||||
anchors.leftMargin: isNetworkPrinter ? UM.Theme.getSize("sidebar_lining").width : UM.Theme.getSize("sidebar_margin").width
|
||||
anchors.right: downArrow.left;
|
||||
anchors.rightMargin: control.rightMargin;
|
||||
anchors.verticalCenter: parent.verticalCenter;
|
||||
font: UM.Theme.getFont("large")
|
||||
font: UM.Theme.getFont("medium_bold")
|
||||
}
|
||||
}
|
||||
label: Label {}
|
||||
|
124
resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml
Normal file
124
resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml
Normal file
@ -0,0 +1,124 @@
|
||||
// Copyright (c) 2018 Ultimaker B.V.
|
||||
// Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import QtQuick 2.7
|
||||
import QtQuick.Controls 2.0
|
||||
|
||||
import UM 1.2 as UM
|
||||
import Cura 1.0 as Cura
|
||||
|
||||
Rectangle
|
||||
{
|
||||
id: configurationItem
|
||||
|
||||
property var configuration: null
|
||||
property var selected: false
|
||||
signal activateConfiguration()
|
||||
|
||||
height: childrenRect.height
|
||||
border.width: UM.Theme.getSize("default_lining").width
|
||||
border.color: updateBorderColor()
|
||||
color: selected ? UM.Theme.getColor("configuration_item_active") : UM.Theme.getColor("configuration_item")
|
||||
property var textColor: selected ? UM.Theme.getColor("configuration_item_text_active") : UM.Theme.getColor("configuration_item_text")
|
||||
|
||||
function updateBorderColor()
|
||||
{
|
||||
border.color = selected ? UM.Theme.getColor("configuration_item_border_active") : UM.Theme.getColor("configuration_item_border")
|
||||
}
|
||||
|
||||
Column
|
||||
{
|
||||
id: contentColumn
|
||||
width: parent.width
|
||||
padding: UM.Theme.getSize("default_margin").width
|
||||
spacing: Math.round(UM.Theme.getSize("default_margin").height / 2)
|
||||
|
||||
Row
|
||||
{
|
||||
id: extruderRow
|
||||
|
||||
width: parent.width - 2 * parent.padding
|
||||
height: childrenRect.height
|
||||
|
||||
spacing: UM.Theme.getSize("default_margin").width
|
||||
|
||||
Repeater
|
||||
{
|
||||
id: repeater
|
||||
height: childrenRect.height
|
||||
model: configuration.extruderConfigurations
|
||||
delegate: PrintCoreConfiguration
|
||||
{
|
||||
width: Math.round(parent.width / 2)
|
||||
printCoreConfiguration: modelData
|
||||
mainColor: textColor
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Buildplate row separator
|
||||
Rectangle
|
||||
{
|
||||
id: separator
|
||||
|
||||
visible: buildplateInformation.visible
|
||||
width: parent.width - 2 * parent.padding
|
||||
height: visible ? Math.round(UM.Theme.getSize("sidebar_lining_thin").height / 2) : 0
|
||||
color: textColor
|
||||
}
|
||||
|
||||
Item
|
||||
{
|
||||
id: buildplateInformation
|
||||
width: parent.width - 2 * parent.padding
|
||||
height: childrenRect.height
|
||||
visible: configuration.buildplateConfiguration != ""
|
||||
|
||||
UM.RecolorImage {
|
||||
id: buildplateIcon
|
||||
anchors.left: parent.left
|
||||
width: UM.Theme.getSize("topbar_button_icon").width
|
||||
height: UM.Theme.getSize("topbar_button_icon").height
|
||||
sourceSize.width: width
|
||||
sourceSize.height: height
|
||||
source: UM.Theme.getIcon("buildplate")
|
||||
color: textColor
|
||||
}
|
||||
|
||||
Label
|
||||
{
|
||||
id: buildplateLabel
|
||||
anchors.left: buildplateIcon.right
|
||||
anchors.verticalCenter: buildplateIcon.verticalCenter
|
||||
anchors.leftMargin: Math.round(UM.Theme.getSize("default_margin").height / 2)
|
||||
text: configuration.buildplateConfiguration
|
||||
color: textColor
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea
|
||||
{
|
||||
id: mouse
|
||||
anchors.fill: parent
|
||||
onClicked: activateConfiguration()
|
||||
hoverEnabled: true
|
||||
onEntered: parent.border.color = UM.Theme.getColor("configuration_item_border_hover")
|
||||
onExited: updateBorderColor()
|
||||
}
|
||||
|
||||
Connections
|
||||
{
|
||||
target: Cura.MachineManager
|
||||
onCurrentConfigurationChanged: {
|
||||
configurationItem.selected = Cura.MachineManager.matchesConfiguration(configuration)
|
||||
updateBorderColor()
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted:
|
||||
{
|
||||
configurationItem.selected = Cura.MachineManager.matchesConfiguration(configuration)
|
||||
updateBorderColor()
|
||||
}
|
||||
}
|
@ -0,0 +1,86 @@
|
||||
// Copyright (c) 2018 Ultimaker B.V.
|
||||
// Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import QtQuick 2.7
|
||||
import QtQuick.Controls 1.4
|
||||
import QtQuick.Controls.Styles 1.4
|
||||
|
||||
import UM 1.2 as UM
|
||||
import Cura 1.0 as Cura
|
||||
|
||||
Column
|
||||
{
|
||||
id: base
|
||||
property var outputDevice: null
|
||||
property var computedHeight: container.height + configurationListHeading.height + 3 * padding
|
||||
height: childrenRect.height + 2 * padding
|
||||
padding: UM.Theme.getSize("default_margin").width
|
||||
spacing: Math.round(UM.Theme.getSize("default_margin").height / 2)
|
||||
|
||||
Label
|
||||
{
|
||||
id: configurationListHeading
|
||||
text: catalog.i18nc("@label:header configurations", "Available configurations")
|
||||
font: UM.Theme.getFont("large")
|
||||
width: parent.width - 2 * parent.padding
|
||||
}
|
||||
|
||||
Component
|
||||
{
|
||||
id: sectionHeading
|
||||
Rectangle
|
||||
{
|
||||
height: childrenRect.height + UM.Theme.getSize("default_margin").height
|
||||
Label
|
||||
{
|
||||
text: section
|
||||
font: UM.Theme.getFont("default_bold")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ScrollView
|
||||
{
|
||||
id: container
|
||||
width: parent.width - parent.padding
|
||||
height: Math.min(configurationList.contentHeight, 350 * screenScaleFactor)
|
||||
|
||||
style: UM.Theme.styles.scrollview
|
||||
__wheelAreaScrollSpeed: 75 // Scroll three lines in one scroll event
|
||||
|
||||
ListView
|
||||
{
|
||||
id: configurationList
|
||||
spacing: Math.round(UM.Theme.getSize("default_margin").height / 2)
|
||||
width: container.width
|
||||
contentHeight: childrenRect.height
|
||||
|
||||
section.property: "modelData.printerType"
|
||||
section.criteria: ViewSection.FullString
|
||||
section.delegate: sectionHeading
|
||||
|
||||
model: (outputDevice != null) ? outputDevice.uniqueConfigurations : []
|
||||
delegate: ConfigurationItem
|
||||
{
|
||||
width: parent.width - UM.Theme.getSize("default_margin").width
|
||||
configuration: modelData
|
||||
onActivateConfiguration:
|
||||
{
|
||||
switchPopupState()
|
||||
Cura.MachineManager.applyRemoteConfiguration(configuration)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Connections
|
||||
{
|
||||
target: outputDevice
|
||||
onUniqueConfigurationsChanged:
|
||||
{
|
||||
// FIXME For now the model should be removed and then created again, otherwise changes in the printer don't automatically update the UI
|
||||
configurationList.model = []
|
||||
configurationList.model = outputDevice.uniqueConfigurations
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,65 @@
|
||||
// Copyright (c) 2018 Ultimaker B.V.
|
||||
// Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import QtQuick 2.7
|
||||
import QtQuick.Controls 2.0
|
||||
import QtQuick.Controls.Styles 1.4
|
||||
|
||||
import UM 1.2 as UM
|
||||
import Cura 1.0 as Cura
|
||||
|
||||
Item
|
||||
{
|
||||
id: configurationSelector
|
||||
property var connectedDevice: Cura.MachineManager.printerOutputDevices.length >= 1 ? Cura.MachineManager.printerOutputDevices[0] : null
|
||||
property var panelWidth: control.width
|
||||
|
||||
function switchPopupState()
|
||||
{
|
||||
popup.visible ? popup.close() : popup.open()
|
||||
}
|
||||
|
||||
SyncButton
|
||||
{
|
||||
id: syncButton
|
||||
onClicked: switchPopupState()
|
||||
outputDevice: connectedDevice
|
||||
}
|
||||
|
||||
Popup
|
||||
{
|
||||
// TODO Change once updating to Qt5.10 - The 'opened' property is in 5.10 but the behavior is now implemented with the visible property
|
||||
id: popup
|
||||
clip: true
|
||||
closePolicy: Popup.CloseOnPressOutsideParent
|
||||
y: configurationSelector.height - UM.Theme.getSize("default_lining").height
|
||||
x: configurationSelector.width - width
|
||||
width: panelWidth
|
||||
visible: false
|
||||
padding: UM.Theme.getSize("default_lining").width
|
||||
transformOrigin: Popup.Top
|
||||
contentItem: ConfigurationListView
|
||||
{
|
||||
id: configList
|
||||
width: panelWidth - 2 * popup.padding
|
||||
outputDevice: connectedDevice
|
||||
}
|
||||
background: Rectangle
|
||||
{
|
||||
color: UM.Theme.getColor("setting_control")
|
||||
border.color: UM.Theme.getColor("setting_control_border")
|
||||
}
|
||||
exit: Transition
|
||||
{
|
||||
// This applies a default NumberAnimation to any changes a state change makes to x or y properties
|
||||
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: visible = false
|
||||
onOpened: visible = true
|
||||
}
|
||||
}
|
@ -0,0 +1,87 @@
|
||||
// Copyright (c) 2018 Ultimaker B.V.
|
||||
// Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import QtQuick 2.7
|
||||
import QtQuick.Controls 2.0
|
||||
|
||||
import UM 1.2 as UM
|
||||
|
||||
|
||||
Column
|
||||
{
|
||||
id: extruderInfo
|
||||
property var printCoreConfiguration
|
||||
property var mainColor: "black"
|
||||
spacing: Math.round(UM.Theme.getSize("default_margin").height / 2)
|
||||
|
||||
height: childrenRect.height
|
||||
|
||||
Item
|
||||
{
|
||||
id: extruder
|
||||
width: parent.width
|
||||
height: childrenRect.height
|
||||
|
||||
Label
|
||||
{
|
||||
id: extruderLabel
|
||||
text: catalog.i18nc("@label:extruder label", "Extruder")
|
||||
elide: Text.ElideRight
|
||||
anchors.left: parent.left
|
||||
font: UM.Theme.getFont("default")
|
||||
color: mainColor
|
||||
}
|
||||
|
||||
// Rounded item to show the extruder number
|
||||
Item
|
||||
{
|
||||
id: extruderIconItem
|
||||
anchors.verticalCenter: extruderLabel.verticalCenter
|
||||
anchors.left: extruderLabel.right
|
||||
anchors.leftMargin: Math.round(UM.Theme.getSize("default_margin").width / 2)
|
||||
|
||||
width: UM.Theme.getSize("section_icon").width
|
||||
height: UM.Theme.getSize("section_icon").height
|
||||
|
||||
UM.RecolorImage {
|
||||
id: mainCircle
|
||||
anchors.fill: parent
|
||||
|
||||
anchors.centerIn: parent
|
||||
sourceSize.width: parent.width
|
||||
sourceSize.height: parent.height
|
||||
source: UM.Theme.getIcon("extruder_button")
|
||||
color: mainColor
|
||||
}
|
||||
|
||||
Label
|
||||
{
|
||||
id: extruderNumberText
|
||||
anchors.centerIn: parent
|
||||
text: printCoreConfiguration.position + 1
|
||||
font: UM.Theme.getFont("default")
|
||||
color: mainColor
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Label
|
||||
{
|
||||
id: materialLabel
|
||||
text: printCoreConfiguration.material.name
|
||||
elide: Text.ElideRight
|
||||
width: parent.width
|
||||
font: UM.Theme.getFont("default_bold")
|
||||
color: mainColor
|
||||
}
|
||||
|
||||
Label
|
||||
{
|
||||
id: printCoreTypeLabel
|
||||
text: printCoreConfiguration.hotendID
|
||||
elide: Text.ElideRight
|
||||
width: parent.width
|
||||
font: UM.Theme.getFont("default")
|
||||
color: mainColor
|
||||
}
|
||||
}
|
102
resources/qml/Menus/ConfigurationMenu/SyncButton.qml
Normal file
102
resources/qml/Menus/ConfigurationMenu/SyncButton.qml
Normal file
@ -0,0 +1,102 @@
|
||||
// Copyright (c) 2018 Ultimaker B.V.
|
||||
// Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import QtQuick 2.7
|
||||
import QtQuick.Controls 1.4
|
||||
import QtQuick.Controls.Styles 1.4
|
||||
|
||||
import UM 1.2 as UM
|
||||
import Cura 1.0 as Cura
|
||||
|
||||
Button
|
||||
{
|
||||
id: base
|
||||
property var outputDevice: null
|
||||
property var matched: updateOnSync()
|
||||
text: matched == true ? "Yes" : "No"
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
|
||||
function updateOnSync()
|
||||
{
|
||||
if (outputDevice != undefined)
|
||||
{
|
||||
for (var index in outputDevice.uniqueConfigurations)
|
||||
{
|
||||
var configuration = outputDevice.uniqueConfigurations[index]
|
||||
if (Cura.MachineManager.matchesConfiguration(configuration))
|
||||
{
|
||||
base.matched = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
base.matched = false;
|
||||
}
|
||||
|
||||
style: ButtonStyle
|
||||
{
|
||||
background: Rectangle
|
||||
{
|
||||
color:
|
||||
{
|
||||
if(control.pressed)
|
||||
{
|
||||
return UM.Theme.getColor("sidebar_header_active");
|
||||
}
|
||||
else if(control.hovered)
|
||||
{
|
||||
return UM.Theme.getColor("sidebar_header_hover");
|
||||
}
|
||||
else
|
||||
{
|
||||
return UM.Theme.getColor("sidebar_header_bar");
|
||||
}
|
||||
}
|
||||
Behavior on color { ColorAnimation { duration: 50; } }
|
||||
|
||||
UM.RecolorImage
|
||||
{
|
||||
id: downArrow
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: UM.Theme.getSize("default_margin").width
|
||||
width: UM.Theme.getSize("standard_arrow").width
|
||||
height: UM.Theme.getSize("standard_arrow").height
|
||||
sourceSize.width: width
|
||||
sourceSize.height: height
|
||||
color: UM.Theme.getColor("text_emphasis")
|
||||
source: UM.Theme.getIcon("arrow_bottom")
|
||||
}
|
||||
UM.RecolorImage {
|
||||
id: sidebarComboBoxLabel
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: UM.Theme.getSize("default_margin").width
|
||||
anchors.verticalCenter: parent.verticalCenter;
|
||||
|
||||
width: UM.Theme.getSize("printer_sync_icon").width
|
||||
height: UM.Theme.getSize("printer_sync_icon").height
|
||||
|
||||
color: control.matched ? UM.Theme.getColor("printer_config_matched") : UM.Theme.getColor("printer_config_mismatch")
|
||||
source: UM.Theme.getIcon("tab_status_connected")
|
||||
sourceSize.width: width
|
||||
sourceSize.height: height
|
||||
}
|
||||
}
|
||||
label: Label {}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: outputDevice
|
||||
onUniqueConfigurationsChanged: {
|
||||
updateOnSync()
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: Cura.MachineManager
|
||||
onCurrentConfigurationChanged: {
|
||||
updateOnSync()
|
||||
}
|
||||
}
|
||||
}
|
23
resources/qml/Menus/LocalPrinterMenu.qml
Normal file
23
resources/qml/Menus/LocalPrinterMenu.qml
Normal file
@ -0,0 +1,23 @@
|
||||
// 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.4
|
||||
|
||||
import UM 1.2 as UM
|
||||
import Cura 1.0 as Cura
|
||||
|
||||
Instantiator {
|
||||
model: UM.ContainerStacksModel {
|
||||
filter: {"type": "machine", "um_network_key": null}
|
||||
}
|
||||
MenuItem {
|
||||
text: model.name;
|
||||
checkable: true;
|
||||
checked: Cura.MachineManager.activeMachineId == model.id
|
||||
exclusiveGroup: group;
|
||||
onTriggered: Cura.MachineManager.setActiveMachine(model.id);
|
||||
}
|
||||
onObjectAdded: menu.insertItem(index, object)
|
||||
onObjectRemoved: menu.removeItem(object)
|
||||
}
|
25
resources/qml/Menus/NetworkPrinterMenu.qml
Normal file
25
resources/qml/Menus/NetworkPrinterMenu.qml
Normal file
@ -0,0 +1,25 @@
|
||||
// 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.4
|
||||
|
||||
import UM 1.2 as UM
|
||||
import Cura 1.0 as Cura
|
||||
|
||||
Instantiator {
|
||||
model: UM.ContainerStacksModel {
|
||||
filter: {"type": "machine", "um_network_key": "*", "hidden": "False"}
|
||||
}
|
||||
MenuItem {
|
||||
// TODO: Use printer_group icon when it's a cluster. Not use it for now since it doesn't look as expected
|
||||
// iconSource: UM.Theme.getIcon("printer_single")
|
||||
text: model.metadata["connect_group_name"]
|
||||
checkable: true;
|
||||
checked: Cura.MachineManager.activeMachineNetworkGroupName == model.metadata["connect_group_name"]
|
||||
exclusiveGroup: group;
|
||||
onTriggered: Cura.MachineManager.setActiveMachine(model.id);
|
||||
}
|
||||
onObjectAdded: menu.insertItem(index, object)
|
||||
onObjectRemoved: menu.removeItem(object)
|
||||
}
|
@ -1,37 +1,60 @@
|
||||
// Copyright (c) 2016 Ultimaker B.V.
|
||||
// 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 QtQuick.Controls 1.4
|
||||
import QtQuick.Controls.Styles 1.4
|
||||
|
||||
import UM 1.2 as UM
|
||||
import Cura 1.0 as Cura
|
||||
|
||||
Menu
|
||||
{
|
||||
id: menu;
|
||||
id: menu
|
||||
// TODO Enable custom style to the menu
|
||||
// style: MenuStyle
|
||||
// {
|
||||
// frame: Rectangle
|
||||
// {
|
||||
// color: "white"
|
||||
// }
|
||||
// }
|
||||
|
||||
Instantiator
|
||||
MenuItem
|
||||
{
|
||||
model: UM.ContainerStacksModel
|
||||
{
|
||||
filter: {"type": "machine"}
|
||||
}
|
||||
MenuItem
|
||||
{
|
||||
text: model.name;
|
||||
checkable: true;
|
||||
checked: Cura.MachineManager.activeMachineId == model.id
|
||||
exclusiveGroup: group;
|
||||
onTriggered: Cura.MachineManager.setActiveMachine(model.id);
|
||||
}
|
||||
onObjectAdded: menu.insertItem(index, object)
|
||||
onObjectRemoved: menu.removeItem(object)
|
||||
text: catalog.i18nc("@label:category menu label", "Network enabled printers")
|
||||
enabled: false
|
||||
visible: networkPrinterMenu.count > 0
|
||||
}
|
||||
|
||||
NetworkPrinterMenu
|
||||
{
|
||||
id: networkPrinterMenu
|
||||
}
|
||||
|
||||
MenuSeparator
|
||||
{
|
||||
visible: networkPrinterMenu.count > 0
|
||||
}
|
||||
|
||||
MenuItem
|
||||
{
|
||||
text: catalog.i18nc("@label:category menu label", "Local printers")
|
||||
enabled: false
|
||||
visible: localPrinterMenu.count > 0
|
||||
}
|
||||
|
||||
LocalPrinterMenu
|
||||
{
|
||||
id: localPrinterMenu
|
||||
}
|
||||
|
||||
ExclusiveGroup { id: group; }
|
||||
|
||||
MenuSeparator { }
|
||||
MenuSeparator
|
||||
{
|
||||
visible: localPrinterMenu.count > 0
|
||||
}
|
||||
|
||||
MenuItem { action: Cura.Actions.addMachine; }
|
||||
MenuItem { action: Cura.Actions.configureMachines; }
|
||||
|
27
resources/qml/Menus/PrinterStatusIcon.qml
Normal file
27
resources/qml/Menus/PrinterStatusIcon.qml
Normal file
@ -0,0 +1,27 @@
|
||||
// Copyright (c) 2017 Ultimaker B.V.
|
||||
// Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import QtQuick 2.2
|
||||
|
||||
import UM 1.2 as UM
|
||||
import Cura 1.0 as Cura
|
||||
|
||||
Item
|
||||
{
|
||||
property var status: "disconnected"
|
||||
width: childrenRect.width
|
||||
height: childrenRect.height
|
||||
UM.RecolorImage
|
||||
{
|
||||
id: statusIcon
|
||||
width: UM.Theme.getSize("printer_status_icon").width
|
||||
height: UM.Theme.getSize("printer_status_icon").height
|
||||
sourceSize.width: width
|
||||
sourceSize.height: width
|
||||
color: UM.Theme.getColor("tab_status_" + parent.status)
|
||||
source: UM.Theme.getIcon(parent.status)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
37
resources/qml/Menus/PrinterTypeMenu.qml
Normal file
37
resources/qml/Menus/PrinterTypeMenu.qml
Normal file
@ -0,0 +1,37 @@
|
||||
// Copyright (c) 2018 Ultimaker B.V.
|
||||
// Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import QtQuick 2.7
|
||||
import QtQuick.Controls 1.4
|
||||
|
||||
import UM 1.3 as UM
|
||||
import Cura 1.0 as Cura
|
||||
|
||||
Menu
|
||||
{
|
||||
id: menu
|
||||
title: "Printer type"
|
||||
property var outputDevice: Cura.MachineManager.printerOutputDevices[0]
|
||||
|
||||
Instantiator
|
||||
{
|
||||
id: printerTypeInstantiator
|
||||
model: outputDevice != null ? outputDevice.connectedPrintersTypeCount : []
|
||||
|
||||
MenuItem
|
||||
{
|
||||
text: modelData.machine_type
|
||||
checkable: true
|
||||
checked: Cura.MachineManager.activeMachineDefinitionName == modelData.machine_type
|
||||
exclusiveGroup: group
|
||||
onTriggered:
|
||||
{
|
||||
Cura.MachineManager.switchPrinterType(modelData.machine_type)
|
||||
}
|
||||
}
|
||||
onObjectAdded: menu.insertItem(index, object)
|
||||
onObjectRemoved: menu.removeItem(object)
|
||||
}
|
||||
|
||||
ExclusiveGroup { id: group }
|
||||
}
|
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 }
|
||||
}
|
@ -14,10 +14,7 @@ UM.ManagementPage
|
||||
id: base;
|
||||
|
||||
title: catalog.i18nc("@title:tab", "Printers");
|
||||
model: UM.ContainerStacksModel
|
||||
{
|
||||
filter: {"type": "machine"}
|
||||
}
|
||||
model: Cura.MachineManagementModel { }
|
||||
|
||||
activeId: Cura.MachineManager.activeMachineId
|
||||
activeIndex: activeMachineIndex()
|
||||
@ -57,7 +54,7 @@ UM.ManagementPage
|
||||
{
|
||||
text: catalog.i18nc("@action:button", "Rename");
|
||||
iconName: "edit-rename";
|
||||
enabled: base.currentItem != null
|
||||
enabled: base.currentItem != null && base.currentItem.metadata.connect_group_name == null
|
||||
onClicked: renameDialog.open();
|
||||
}
|
||||
]
|
||||
|
@ -19,14 +19,17 @@ Item
|
||||
|
||||
UM.I18nCatalog { id: catalog; name: "cura"; }
|
||||
|
||||
Cura.MaterialManagementModel {
|
||||
Cura.MaterialManagementModel
|
||||
{
|
||||
id: materialsModel
|
||||
}
|
||||
|
||||
Label {
|
||||
Label
|
||||
{
|
||||
id: titleLabel
|
||||
|
||||
anchors {
|
||||
anchors
|
||||
{
|
||||
top: parent.top
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
@ -170,22 +173,27 @@ Item
|
||||
Connections
|
||||
{
|
||||
target: materialsModel
|
||||
onItemsChanged: {
|
||||
onItemsChanged:
|
||||
{
|
||||
var currentItemId = base.currentItem == null ? "" : base.currentItem.root_material_id;
|
||||
var position = Cura.ExtruderManager.activeExtruderIndex;
|
||||
|
||||
// try to pick the currently selected item; it may have been moved
|
||||
if (base.newRootMaterialIdToSwitchTo == "") {
|
||||
if (base.newRootMaterialIdToSwitchTo == "")
|
||||
{
|
||||
base.newRootMaterialIdToSwitchTo = currentItemId;
|
||||
}
|
||||
|
||||
for (var idx = 0; idx < materialsModel.rowCount(); ++idx) {
|
||||
for (var idx = 0; idx < materialsModel.rowCount(); ++idx)
|
||||
{
|
||||
var item = materialsModel.getItem(idx);
|
||||
if (item.root_material_id == base.newRootMaterialIdToSwitchTo) {
|
||||
if (item.root_material_id == base.newRootMaterialIdToSwitchTo)
|
||||
{
|
||||
// Switch to the newly created profile if needed
|
||||
materialListView.currentIndex = idx;
|
||||
materialListView.activateDetailsWithIndex(materialListView.currentIndex);
|
||||
if (base.toActivateNewMaterial) {
|
||||
if (base.toActivateNewMaterial)
|
||||
{
|
||||
Cura.MachineManager.setMaterial(position, item.container_node);
|
||||
}
|
||||
base.newRootMaterialIdToSwitchTo = "";
|
||||
@ -196,7 +204,8 @@ Item
|
||||
|
||||
materialListView.currentIndex = 0;
|
||||
materialListView.activateDetailsWithIndex(materialListView.currentIndex);
|
||||
if (base.toActivateNewMaterial) {
|
||||
if (base.toActivateNewMaterial)
|
||||
{
|
||||
Cura.MachineManager.setMaterial(position, materialsModel.getItem(0).container_node);
|
||||
}
|
||||
base.newRootMaterialIdToSwitchTo = "";
|
||||
@ -233,14 +242,17 @@ Item
|
||||
|
||||
messageDialog.title = catalog.i18nc("@title:window", "Import Material");
|
||||
messageDialog.text = catalog.i18nc("@info:status Don't translate the XML tags <filename> or <message>!", "Could not import material <filename>%1</filename>: <message>%2</message>").arg(fileUrl).arg(result.message);
|
||||
if (result.status == "success") {
|
||||
if (result.status == "success")
|
||||
{
|
||||
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);
|
||||
}
|
||||
else if (result.status == "duplicate") {
|
||||
else if (result.status == "duplicate")
|
||||
{
|
||||
messageDialog.icon = StandardIcon.Warning;
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
messageDialog.icon = StandardIcon.Critical;
|
||||
}
|
||||
messageDialog.open();
|
||||
@ -260,12 +272,14 @@ Item
|
||||
var result = Cura.ContainerManager.exportContainer(base.currentItem.root_material_id, selectedNameFilter, fileUrl);
|
||||
|
||||
messageDialog.title = catalog.i18nc("@title:window", "Export Material");
|
||||
if (result.status == "error") {
|
||||
if (result.status == "error")
|
||||
{
|
||||
messageDialog.icon = StandardIcon.Critical;
|
||||
messageDialog.text = catalog.i18nc("@info:status Don't translate the XML tags <filename> and <message>!", "Failed to export material to <filename>%1</filename>: <message>%2</message>").arg(fileUrl).arg(result.message);
|
||||
messageDialog.open();
|
||||
}
|
||||
else if (result.status == "success") {
|
||||
else if (result.status == "success")
|
||||
{
|
||||
messageDialog.icon = StandardIcon.Information;
|
||||
messageDialog.text = catalog.i18nc("@info:status Don't translate the XML tag <filename>!", "Successfully exported material to <filename>%1</filename>").arg(result.path);
|
||||
messageDialog.open();
|
||||
@ -283,7 +297,8 @@ Item
|
||||
Item {
|
||||
id: contentsItem
|
||||
|
||||
anchors {
|
||||
anchors
|
||||
{
|
||||
top: titleLabel.bottom
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
@ -297,7 +312,8 @@ Item
|
||||
|
||||
Item
|
||||
{
|
||||
anchors {
|
||||
anchors
|
||||
{
|
||||
top: buttonRow.bottom
|
||||
topMargin: UM.Theme.getSize("default_margin").height
|
||||
left: parent.left
|
||||
@ -310,12 +326,14 @@ Item
|
||||
Label
|
||||
{
|
||||
id: captionLabel
|
||||
anchors {
|
||||
anchors
|
||||
{
|
||||
top: parent.top
|
||||
left: parent.left
|
||||
}
|
||||
visible: text != ""
|
||||
text: {
|
||||
text:
|
||||
{
|
||||
var caption = catalog.i18nc("@action:label", "Printer") + ": " + Cura.MachineManager.activeMachineName;
|
||||
if (Cura.MachineManager.hasVariants)
|
||||
{
|
||||
@ -330,14 +348,16 @@ Item
|
||||
ScrollView
|
||||
{
|
||||
id: materialScrollView
|
||||
anchors {
|
||||
anchors
|
||||
{
|
||||
top: captionLabel.visible ? captionLabel.bottom : parent.top
|
||||
topMargin: captionLabel.visible ? UM.Theme.getSize("default_margin").height : 0
|
||||
bottom: parent.bottom
|
||||
left: parent.left
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
Rectangle
|
||||
{
|
||||
parent: viewport
|
||||
anchors.fill: parent
|
||||
color: palette.light
|
||||
@ -418,13 +438,15 @@ Item
|
||||
MouseArea
|
||||
{
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
onClicked:
|
||||
{
|
||||
parent.ListView.view.currentIndex = model.index;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function activateDetailsWithIndex(index) {
|
||||
function activateDetailsWithIndex(index)
|
||||
{
|
||||
var model = materialsModel.getItem(index);
|
||||
base.currentItem = model;
|
||||
materialDetailsView.containerId = model.container_id;
|
||||
@ -446,7 +468,8 @@ Item
|
||||
{
|
||||
id: detailsPanel
|
||||
|
||||
anchors {
|
||||
anchors
|
||||
{
|
||||
left: materialScrollView.right
|
||||
leftMargin: UM.Theme.getSize("default_margin").width
|
||||
top: parent.top
|
||||
|
@ -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 { }
|
||||
}
|
||||
}
|
||||
}
|
@ -13,11 +13,17 @@ SettingItem
|
||||
|
||||
property string textBeforeEdit
|
||||
property bool textHasChanged
|
||||
property bool focusGainedByClick: false
|
||||
onFocusReceived:
|
||||
{
|
||||
textHasChanged = false;
|
||||
textBeforeEdit = focusItem.text;
|
||||
focusItem.selectAll();
|
||||
|
||||
if(!focusGainedByClick)
|
||||
{
|
||||
// select all text when tabbing through fields (but not when selecting a field with the mouse)
|
||||
focusItem.selectAll();
|
||||
}
|
||||
}
|
||||
|
||||
contents: Rectangle
|
||||
@ -93,14 +99,6 @@ SettingItem
|
||||
font: UM.Theme.getFont("default")
|
||||
}
|
||||
|
||||
MouseArea
|
||||
{
|
||||
id: mouseArea
|
||||
anchors.fill: parent;
|
||||
//hoverEnabled: true;
|
||||
cursorShape: Qt.IBeamCursor
|
||||
}
|
||||
|
||||
TextInput
|
||||
{
|
||||
id: input
|
||||
@ -142,6 +140,7 @@ SettingItem
|
||||
{
|
||||
base.focusReceived();
|
||||
}
|
||||
base.focusGainedByClick = false;
|
||||
}
|
||||
|
||||
color: !enabled ? UM.Theme.getColor("setting_control_disabled_text") : UM.Theme.getColor("setting_control_text")
|
||||
@ -178,6 +177,22 @@ SettingItem
|
||||
}
|
||||
when: !input.activeFocus
|
||||
}
|
||||
|
||||
MouseArea
|
||||
{
|
||||
id: mouseArea
|
||||
anchors.fill: parent;
|
||||
|
||||
cursorShape: Qt.IBeamCursor
|
||||
|
||||
onPressed: {
|
||||
if(!input.activeFocus) {
|
||||
base.focusGainedByClick = true;
|
||||
input.forceActiveFocus();
|
||||
}
|
||||
mouse.accepted = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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")
|
||||
@ -374,8 +439,6 @@ Item
|
||||
key: model.key ? model.key : ""
|
||||
watchedProperties: [ "value", "enabled", "state", "validationState", "settable_per_extruder", "resolve" ]
|
||||
storeIndex: 0
|
||||
// Due to the way setPropertyValue works, removeUnusedValue gives the correct output in case of resolve
|
||||
removeUnusedValue: model.resolve == undefined
|
||||
}
|
||||
|
||||
Connections
|
||||
@ -493,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
|
||||
{
|
||||
@ -511,7 +582,7 @@ Item
|
||||
return catalog.i18nc("@action:menu", "Keep this setting visible");
|
||||
}
|
||||
}
|
||||
visible: findingSettings;
|
||||
visible: (findingSettings || showingAllSettings);
|
||||
onTriggered:
|
||||
{
|
||||
if (contextMenu.settingVisible)
|
||||
@ -522,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
|
||||
|
@ -8,6 +8,7 @@ import QtQuick.Layouts 1.3
|
||||
import UM 1.2 as UM
|
||||
import Cura 1.0 as Cura
|
||||
import "Menus"
|
||||
import "Menus/ConfigurationMenu"
|
||||
|
||||
Rectangle
|
||||
{
|
||||
@ -18,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
|
||||
@ -85,12 +87,34 @@ Rectangle
|
||||
}
|
||||
}
|
||||
|
||||
MachineSelection {
|
||||
MachineSelection
|
||||
{
|
||||
id: machineSelection
|
||||
width: base.width
|
||||
width: base.width - configSelection.width - separator.width
|
||||
height: UM.Theme.getSize("sidebar_header").height
|
||||
anchors.top: base.top
|
||||
anchors.left: parent.left
|
||||
}
|
||||
|
||||
Rectangle
|
||||
{
|
||||
id: separator
|
||||
visible: configSelection.visible
|
||||
width: visible ? Math.round(UM.Theme.getSize("sidebar_lining_thin").height / 2) : 0
|
||||
height: UM.Theme.getSize("sidebar_header").height
|
||||
color: UM.Theme.getColor("sidebar_lining_thin")
|
||||
anchors.left: machineSelection.right
|
||||
}
|
||||
|
||||
ConfigurationSelection
|
||||
{
|
||||
id: configSelection
|
||||
visible: isNetworkPrinter
|
||||
width: visible ? Math.round(base.width * 0.15) : 0
|
||||
height: UM.Theme.getSize("sidebar_header").height
|
||||
anchors.top: base.top
|
||||
anchors.right: parent.right
|
||||
panelWidth: base.width
|
||||
}
|
||||
|
||||
SidebarHeader {
|
||||
|
@ -16,6 +16,8 @@ Column
|
||||
|
||||
property int currentExtruderIndex: Cura.ExtruderManager.activeExtruderIndex;
|
||||
property bool currentExtruderVisible: extrudersList.visible;
|
||||
property bool printerConnected: Cura.MachineManager.printerOutputDevices.length != 0
|
||||
property bool hasManyPrinterTypes: printerConnected ? Cura.MachineManager.printerOutputDevices[0].connectedPrintersTypeCount.length > 1 : false
|
||||
|
||||
spacing: Math.round(UM.Theme.getSize("sidebar_margin").width * 0.9)
|
||||
|
||||
@ -24,16 +26,66 @@ Column
|
||||
|
||||
Item
|
||||
{
|
||||
id: initialSeparator
|
||||
anchors
|
||||
{
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
}
|
||||
visible: extruderSelectionRow.visible
|
||||
visible: printerTypeSelectionRow.visible || buildplateRow.visible || extruderSelectionRow.visible
|
||||
height: UM.Theme.getSize("default_lining").height
|
||||
width: height
|
||||
}
|
||||
|
||||
// Printer Type Row
|
||||
Item
|
||||
{
|
||||
id: printerTypeSelectionRow
|
||||
height: UM.Theme.getSize("sidebar_setup").height
|
||||
visible: printerConnected && hasManyPrinterTypes && !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
|
||||
}
|
||||
|
||||
Label
|
||||
{
|
||||
id: configurationLabel
|
||||
text: catalog.i18nc("@label", "Printer type");
|
||||
width: Math.round(parent.width * 0.4 - UM.Theme.getSize("default_margin").width)
|
||||
height: parent.height
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
font: UM.Theme.getFont("default");
|
||||
color: UM.Theme.getColor("text");
|
||||
}
|
||||
|
||||
ToolButton
|
||||
{
|
||||
id: printerTypeSelection
|
||||
text: Cura.MachineManager.activeMachineDefinitionName
|
||||
tooltip: Cura.MachineManager.activeMachineDefinitionName
|
||||
height: UM.Theme.getSize("setting_control").height
|
||||
width: Math.round(parent.width * 0.7) + UM.Theme.getSize("sidebar_margin").width
|
||||
anchors.right: parent.right
|
||||
style: UM.Theme.styles.sidebar_header_button
|
||||
activeFocusOnPress: true;
|
||||
|
||||
menu: PrinterTypeMenu { }
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: headerSeparator
|
||||
width: parent.width
|
||||
visible: printerTypeSelectionRow.visible
|
||||
height: visible ? UM.Theme.getSize("sidebar_lining").height : 0
|
||||
color: UM.Theme.getColor("sidebar_lining")
|
||||
}
|
||||
|
||||
// Extruder Row
|
||||
Item
|
||||
{
|
||||
@ -91,6 +143,8 @@ Column
|
||||
exclusiveGroup: extruderMenuGroup
|
||||
checked: base.currentExtruderIndex == index
|
||||
|
||||
property bool extruder_enabled: true
|
||||
|
||||
MouseArea
|
||||
{
|
||||
anchors.fill: parent
|
||||
@ -102,6 +156,7 @@ Column
|
||||
Cura.ExtruderManager.setActiveExtruderIndex(index);
|
||||
break;
|
||||
case Qt.RightButton:
|
||||
extruder_enabled = Cura.MachineManager.getExtruder(model.index).isEnabled
|
||||
extruderMenu.popup();
|
||||
break;
|
||||
}
|
||||
@ -116,13 +171,13 @@ Column
|
||||
MenuItem {
|
||||
text: catalog.i18nc("@action:inmenu", "Enable Extruder")
|
||||
onTriggered: Cura.MachineManager.setExtruderEnabled(model.index, true)
|
||||
visible: !Cura.MachineManager.getExtruder(model.index).isEnabled
|
||||
visible: !extruder_enabled // using an intermediate variable prevents an empty popup that occured now and then
|
||||
}
|
||||
|
||||
MenuItem {
|
||||
text: catalog.i18nc("@action:inmenu", "Disable Extruder")
|
||||
onTriggered: Cura.MachineManager.setExtruderEnabled(model.index, false)
|
||||
visible: Cura.MachineManager.getExtruder(model.index).isEnabled
|
||||
visible: extruder_enabled
|
||||
}
|
||||
}
|
||||
|
||||
@ -258,7 +313,7 @@ Column
|
||||
id: variantRowSpacer
|
||||
height: Math.round(UM.Theme.getSize("sidebar_margin").height / 4)
|
||||
width: height
|
||||
visible: !extruderSelectionRow.visible
|
||||
visible: !extruderSelectionRow.visible && !initialSeparator.visible
|
||||
}
|
||||
|
||||
// Material Row
|
||||
@ -281,6 +336,8 @@ Column
|
||||
id: materialLabel
|
||||
text: catalog.i18nc("@label", "Material");
|
||||
width: Math.round(parent.width * 0.45 - UM.Theme.getSize("default_margin").width)
|
||||
height: parent.height
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
font: UM.Theme.getFont("default");
|
||||
color: UM.Theme.getColor("text");
|
||||
}
|
||||
@ -289,15 +346,9 @@ Column
|
||||
{
|
||||
id: materialSelection
|
||||
|
||||
property var currentRootMaterialName:
|
||||
{
|
||||
var materials = Cura.MachineManager.currentRootMaterialName;
|
||||
var materialName = "";
|
||||
if (base.currentExtruderIndex in materials) {
|
||||
materialName = materials[base.currentExtruderIndex];
|
||||
}
|
||||
return materialName;
|
||||
}
|
||||
property var activeExtruder: Cura.MachineManager.activeStack
|
||||
property var hasActiveExtruder: activeExtruder != null
|
||||
property var currentRootMaterialName: hasActiveExtruder ? activeExtruder.material.name : ""
|
||||
|
||||
text: currentRootMaterialName
|
||||
tooltip: currentRootMaterialName
|
||||
@ -316,7 +367,11 @@ Column
|
||||
property var valueWarning: ! Cura.MachineManager.isActiveQualitySupported
|
||||
|
||||
function isMaterialSupported () {
|
||||
return Cura.ContainerManager.getContainerMetaDataEntry(Cura.MachineManager.activeMaterialId, "compatible") == "True"
|
||||
if (!hasActiveExtruder)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return Cura.ContainerManager.getContainerMetaDataEntry(activeExtruder.material.id, "compatible") == "True"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -341,6 +396,8 @@ Column
|
||||
id: variantLabel
|
||||
text: Cura.MachineManager.activeDefinitionVariantsName;
|
||||
width: Math.round(parent.width * 0.45 - UM.Theme.getSize("default_margin").width)
|
||||
height: parent.height
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
font: UM.Theme.getFont("default");
|
||||
color: UM.Theme.getColor("text");
|
||||
}
|
||||
@ -361,17 +418,14 @@ Column
|
||||
}
|
||||
}
|
||||
|
||||
//Buildplate row separator
|
||||
Rectangle {
|
||||
id: separator
|
||||
|
||||
id: buildplateSeparator
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: UM.Theme.getSize("sidebar_margin").width
|
||||
anchors.rightMargin: UM.Theme.getSize("sidebar_margin").width
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
width: parent.width - 2 * UM.Theme.getSize("sidebar_margin").width
|
||||
visible: buildplateRow.visible
|
||||
width: parent.width - UM.Theme.getSize("sidebar_margin").width * 2
|
||||
height: visible ? Math.floor(UM.Theme.getSize("sidebar_lining_thin").height / 2) : 0
|
||||
color: UM.Theme.getColor("sidebar_lining_thin")
|
||||
height: visible ? UM.Theme.getSize("sidebar_lining_thin").height : 0
|
||||
color: UM.Theme.getColor("sidebar_lining")
|
||||
}
|
||||
|
||||
//Buildplate row
|
||||
@ -394,6 +448,8 @@ Column
|
||||
id: bulidplateLabel
|
||||
text: catalog.i18nc("@label", "Build plate");
|
||||
width: Math.floor(parent.width * 0.45 - UM.Theme.getSize("default_margin").width)
|
||||
height: parent.height
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
font: UM.Theme.getFont("default");
|
||||
color: UM.Theme.getColor("text");
|
||||
}
|
||||
|
@ -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
|
||||
{
|
||||
@ -516,7 +591,12 @@ Item
|
||||
// Update the slider value to represent the rounded value
|
||||
infillSlider.value = roundedSliderValue
|
||||
|
||||
Cura.MachineManager.setSettingForAllExtruders("infill_sparse_density", "value", roundedSliderValue)
|
||||
// Update value only if the Recomended mode is Active,
|
||||
// Otherwise if I change the value in the Custom mode the Recomended view will try to repeat
|
||||
// same operation
|
||||
if (UM.Preferences.getValue("cura/active_mode") == 0) {
|
||||
Cura.MachineManager.setSettingForAllExtruders("infill_sparse_density", "value", roundedSliderValue)
|
||||
}
|
||||
}
|
||||
|
||||
style: SliderStyle
|
||||
|
@ -150,7 +150,7 @@ Rectangle
|
||||
visible: base.width - allItemsWidth - 1 * this.width > 0
|
||||
}
|
||||
|
||||
// #5 Left view
|
||||
// #5 Right view
|
||||
Button
|
||||
{
|
||||
iconSource: UM.Theme.getIcon("view_right")
|
||||
|
@ -101,7 +101,7 @@ UM.Dialog
|
||||
}
|
||||
Label
|
||||
{
|
||||
text: Cura.MachineManager.activeMachine.definition.name
|
||||
text: (Cura.MachineManager.activeMachine == null) ? "" : Cura.MachineManager.activeMachine.definition.name
|
||||
width: (parent.width / 3) | 0
|
||||
}
|
||||
}
|
||||
@ -173,7 +173,7 @@ UM.Dialog
|
||||
}
|
||||
Label
|
||||
{
|
||||
text: Cura.MachineManager.activeVariantNames[modelData] + ", " + Cura.MachineManager.currentRootMaterialName[modelData]
|
||||
text: Cura.MachineManager.activeVariantNames[modelData] + ", " + Cura.MachineManager.getExtruder(modelData).material.name
|
||||
width: (parent.width / 3) | 0
|
||||
}
|
||||
}
|
||||
|
@ -34,7 +34,6 @@ raft_airgap = 0.25
|
||||
raft_interface_thickness = =max(layer_height * 1.5, 0.225)
|
||||
retraction_count_max = 80
|
||||
retraction_hop = 2
|
||||
retraction_hop_enabled = True
|
||||
retraction_hop_only_when_collides = True
|
||||
retraction_min_travel = 0.8
|
||||
retraction_prime_speed = 15
|
||||
|
@ -38,7 +38,6 @@ retraction_count_max = 45
|
||||
retraction_extra_prime_amount = 0.2
|
||||
retraction_extrusion_window = 6.5
|
||||
retraction_hop = 2
|
||||
retraction_hop_enabled = True
|
||||
retraction_hop_only_when_collides = True
|
||||
retraction_min_travel = 0.8
|
||||
retraction_prime_speed = 13
|
||||
|
@ -44,7 +44,6 @@ raft_interface_thickness = =max(layer_height * 1.5, 0.225)
|
||||
retraction_count_max = 80
|
||||
retraction_extrusion_window = 1
|
||||
retraction_hop = 2
|
||||
retraction_hop_enabled = True
|
||||
retraction_hop_only_when_collides = True
|
||||
retraction_min_travel = 0.8
|
||||
retraction_prime_speed = 15
|
||||
|
@ -43,7 +43,6 @@ raft_interface_thickness = =max(layer_height * 1.5, 0.225)
|
||||
retraction_count_max = 80
|
||||
retraction_extrusion_window = 1
|
||||
retraction_hop = 2
|
||||
retraction_hop_enabled = True
|
||||
retraction_hop_only_when_collides = True
|
||||
retraction_min_travel = 0.8
|
||||
retraction_prime_speed = 15
|
||||
|
@ -44,7 +44,6 @@ raft_interface_thickness = =max(layer_height * 1.5, 0.225)
|
||||
retraction_count_max = 80
|
||||
retraction_extrusion_window = 1
|
||||
retraction_hop = 2
|
||||
retraction_hop_enabled = True
|
||||
retraction_hop_only_when_collides = True
|
||||
retraction_min_travel = 0.8
|
||||
retraction_prime_speed = 15
|
||||
|
@ -41,7 +41,6 @@ raft_interface_thickness = =max(layer_height * 1.5, 0.225)
|
||||
retraction_count_max = 80
|
||||
retraction_extrusion_window = 1
|
||||
retraction_hop = 2
|
||||
retraction_hop_enabled = True
|
||||
retraction_hop_only_when_collides = True
|
||||
retraction_min_travel = 0.8
|
||||
retraction_prime_speed = 15
|
||||
|
@ -45,7 +45,6 @@ retraction_count_max = 12
|
||||
retraction_extra_prime_amount = 0.8
|
||||
retraction_extrusion_window = 1
|
||||
retraction_hop = 2
|
||||
retraction_hop_enabled = True
|
||||
retraction_hop_only_when_collides = True
|
||||
retraction_min_travel = 0.8
|
||||
retraction_prime_speed = 18
|
||||
@ -61,7 +60,6 @@ support_angle = 50
|
||||
switch_extruder_prime_speed = 15
|
||||
switch_extruder_retraction_amount = 20
|
||||
switch_extruder_retraction_speeds = 35
|
||||
travel_avoid_distance = 3
|
||||
wall_0_inset = 0
|
||||
wall_line_width_x = =line_width
|
||||
wall_thickness = =line_width * 3
|
||||
|
@ -44,7 +44,6 @@ retraction_count_max = 12
|
||||
retraction_extra_prime_amount = 0.8
|
||||
retraction_extrusion_window = 1
|
||||
retraction_hop = 2
|
||||
retraction_hop_enabled = True
|
||||
retraction_hop_only_when_collides = True
|
||||
retraction_min_travel = 0.8
|
||||
retraction_prime_speed = 18
|
||||
@ -61,7 +60,6 @@ switch_extruder_prime_speed = 15
|
||||
switch_extruder_retraction_amount = 20
|
||||
switch_extruder_retraction_speeds = 35
|
||||
top_bottom_thickness = 1.1
|
||||
travel_avoid_distance = 3
|
||||
wall_0_inset = 0
|
||||
wall_line_width_x = =line_width
|
||||
wall_thickness = =line_width * 3
|
||||
|
@ -43,7 +43,6 @@ retraction_count_max = 12
|
||||
retraction_extra_prime_amount = 0.8
|
||||
retraction_extrusion_window = 1
|
||||
retraction_hop = 2
|
||||
retraction_hop_enabled = True
|
||||
retraction_hop_only_when_collides = True
|
||||
retraction_min_travel = 0.8
|
||||
retraction_prime_speed = 18
|
||||
@ -60,7 +59,6 @@ switch_extruder_prime_speed = 15
|
||||
switch_extruder_retraction_amount = 20
|
||||
switch_extruder_retraction_speeds = 35
|
||||
top_bottom_thickness = 1
|
||||
travel_avoid_distance = 3
|
||||
wall_0_inset = 0
|
||||
wall_line_width_x = =line_width
|
||||
wall_thickness = =line_width * 3
|
||||
|
@ -43,7 +43,6 @@ retraction_count_max = 12
|
||||
retraction_extra_prime_amount = 0.8
|
||||
retraction_extrusion_window = 1
|
||||
retraction_hop = 1.5
|
||||
retraction_hop_enabled = True
|
||||
retraction_hop_only_when_collides = True
|
||||
retraction_min_travel = =line_width * 2
|
||||
retraction_prime_speed = 15
|
||||
|
@ -44,7 +44,6 @@ retraction_count_max = 12
|
||||
retraction_extra_prime_amount = 0.8
|
||||
retraction_extrusion_window = 1
|
||||
retraction_hop = 1.5
|
||||
retraction_hop_enabled = True
|
||||
retraction_hop_only_when_collides = True
|
||||
retraction_min_travel = =line_width * 2
|
||||
retraction_prime_speed = 15
|
||||
|
@ -41,7 +41,6 @@ retraction_count_max = 12
|
||||
retraction_extra_prime_amount = 0.8
|
||||
retraction_extrusion_window = 1
|
||||
retraction_hop = 1.5
|
||||
retraction_hop_enabled = True
|
||||
retraction_hop_only_when_collides = True
|
||||
retraction_min_travel = =line_width * 2
|
||||
retraction_prime_speed = 15
|
||||
|
@ -36,4 +36,3 @@ support_bottom_distance = =support_z_distance
|
||||
support_line_width = =round(line_width * 0.6 / 0.7, 2)
|
||||
support_z_distance = =layer_height
|
||||
top_bottom_thickness = 1.2
|
||||
travel_avoid_distance = 1.5
|
||||
|
@ -37,4 +37,3 @@ support_bottom_distance = =support_z_distance
|
||||
support_line_width = =round(line_width * 0.6 / 0.7, 2)
|
||||
support_z_distance = =layer_height
|
||||
top_bottom_thickness = 1.2
|
||||
travel_avoid_distance = 1.5
|
||||
|
@ -37,4 +37,3 @@ support_bottom_distance = =support_z_distance
|
||||
support_line_width = =round(line_width * 0.6 / 0.7, 2)
|
||||
support_z_distance = =layer_height
|
||||
top_bottom_thickness = 1.2
|
||||
travel_avoid_distance = 1.5
|
||||
|
@ -29,4 +29,3 @@ speed_topbottom = =math.ceil(speed_print * 25 / 50)
|
||||
speed_wall = =math.ceil(speed_print * 40 / 50)
|
||||
speed_wall_0 = =math.ceil(speed_wall * 30 / 40)
|
||||
support_line_width = =round(line_width * 0.6 / 0.7, 2)
|
||||
travel_avoid_distance = 3
|
||||
|
@ -29,4 +29,3 @@ speed_topbottom = =math.ceil(speed_print * 25 / 50)
|
||||
speed_wall = =math.ceil(speed_print * 40 / 50)
|
||||
speed_wall_0 = =math.ceil(speed_wall * 30 / 40)
|
||||
support_line_width = =round(line_width * 0.6 / 0.7, 2)
|
||||
travel_avoid_distance = 3
|
||||
|
@ -30,4 +30,3 @@ speed_topbottom = =math.ceil(speed_print * 25 / 50)
|
||||
speed_wall = =math.ceil(speed_print * 40 / 50)
|
||||
speed_wall_0 = =math.ceil(speed_wall * 30 / 40)
|
||||
support_line_width = =round(line_width * 0.6 / 0.7, 2)
|
||||
travel_avoid_distance = 3
|
||||
|
17
resources/themes/cura-light/icons/buildplate.svg
Normal file
17
resources/themes/cura-light/icons/buildplate.svg
Normal file
@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="14px" height="14px" viewBox="0 0 14 14" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<!-- Generator: Sketch 49 (51002) - http://www.bohemiancoding.com/sketch -->
|
||||
<title>icn_buildplate</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<defs></defs>
|
||||
<g id="Visual" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round">
|
||||
<g id="Printer-status-icon" transform="translate(-33.000000, -366.000000)" stroke="#000000">
|
||||
<g id="icn_buildplate" transform="translate(33.000000, 367.000000)">
|
||||
<polyline id="Stroke-6823" points="0 5 7 8 14 5"></polyline>
|
||||
<polyline id="Stroke-6823-Copy" points="0 7 7 10 14 7"></polyline>
|
||||
<polyline id="Stroke-6823-Copy" points="0 9 7 12 14 9"></polyline>
|
||||
<polygon id="Stroke-6824" points="0 2.75 7 0 14 2.75 7 5.5"></polygon>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.0 KiB |
5
resources/themes/cura-light/icons/connected.svg
Normal file
5
resources/themes/cura-light/icons/connected.svg
Normal file
@ -0,0 +1,5 @@
|
||||
<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<polygon id="Polygon" points="15 8 16 9 9 16 8 15"></polygon>
|
||||
<circle id="Oval-2" cx="6" cy="18" r="4"></circle>
|
||||
<circle id="Oval-2-Copy" cx="18" cy="6" r="4"></circle>
|
||||
</svg>
|
After Width: | Height: | Size: 332 B |
6
resources/themes/cura-light/icons/disconnected.svg
Normal file
6
resources/themes/cura-light/icons/disconnected.svg
Normal file
@ -0,0 +1,6 @@
|
||||
<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<circle id="Oval-2" stroke="#FFFFFF" cx="4" cy="20" r="3.5"></circle>
|
||||
<circle id="Oval-2-Copy" stroke="#FFFFFF" cx="20" cy="4" r="3.5"></circle>
|
||||
<polygon id="Polygon" fill="#FFFFFF" points="11 12 12 13 9 16 8 15"></polygon>
|
||||
<polygon id="Polygon-Copy" fill="#FFFFFF" points="15 8 16 9 13 12 12 11"></polygon>
|
||||
</svg>
|
After Width: | Height: | Size: 475 B |
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 |
12
resources/themes/cura-light/icons/printer_group.svg
Normal file
12
resources/themes/cura-light/icons/printer_group.svg
Normal file
@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="32px" height="16px" viewBox="0 0 32 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<!-- Generator: Sketch 49 (51002) - http://www.bohemiancoding.com/sketch -->
|
||||
<title>icn_groupPrinters</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<defs></defs>
|
||||
<g id="Visual" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="Printer-status-icon" transform="translate(-24.000000, -176.000000)" fill="#000000">
|
||||
<path d="M48,188 L54,188 L54,190 L48,190 L48,188 L48,188 Z M48,180 L54,180 L54,182 L48,182 L48,180 L48,180 Z M54,189 L56,189 L56,191 L54,191 L54,189 L54,189 Z M54,177 L56,177 L56,189 L54,189 L54,177 L54,177 Z M48,182 L51,182 L51,183 L48,183 L48,182 L48,182 Z M49,183 L50,183 L50,185 L49,185 L49,183 L49,183 Z M48,177 L54,177 L54,179 L48,179 L48,177 L48,177 Z M34,176 L46,176 L48,176 L48,187 L53,187 L53,188 L48,188 L48,190 L48,192 L46,192 L46,191 L34,191 L34,192 L32,192 L32,190 L26,190 L26,191 L24,191 L24,189 L24,177 L26,177 L32,177 L32,176 L34,176 Z M34,178 L34,179 L46,179 L46,178 L34,178 Z M41,182 L41,184 L39,184 L39,182 L38,182 L38,181 L42,181 L42,182 L41,182 Z M46,189 L46,181 L34,181 L34,189 L46,189 Z M32,179 L26,179 L26,180 L32,180 L32,179 Z M32,183 L31,183 L31,185 L30,185 L30,183 L29,183 L29,182 L26,182 L26,188 L27,188 L27,187 L32,187 L32,183 Z M35,188 L45,188 L45,189 L35,189 L35,188 Z" id="icn_groupPrinters"></path>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.5 KiB |
14
resources/themes/cura-light/icons/printer_single.svg
Normal file
14
resources/themes/cura-light/icons/printer_single.svg
Normal file
@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<!-- Generator: Sketch 49 (51002) - http://www.bohemiancoding.com/sketch -->
|
||||
<title>icn_singlePrinter</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<defs></defs>
|
||||
<g id="Visual" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="Printer-status-icon" transform="translate(-217.000000, -176.000000)" fill="#000000">
|
||||
<g id="icn_singlePrinter" transform="translate(217.000000, 176.000000)">
|
||||
<path d="M2,13 L14,13 L14,15 L2,15 L2,13 L2,13 Z M2,3 L14,3 L14,5 L2,5 L2,3 L2,3 Z M0,14 L2,14 L2,16 L0,16 L0,14 L0,14 Z M14,14 L16,14 L16,16 L14,16 L14,14 L14,14 Z M0,0 L2,0 L2,14 L0,14 L0,0 L0,0 Z M14,0 L16,0 L16,14 L14,14 L14,0 L14,0 Z M6,5 L10,5 L10,6 L6,6 L6,5 L6,5 Z M7,6 L9,6 L9,8 L7,8 L7,6 L7,6 Z M2,0 L14,0 L14,2 L2,2 L2,0 L2,0 Z M3,12 L13,12 L13,13 L3,13 L3,12 L3,12 Z" id="Rectangle-185-Copy-5-Copy-4"></path>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.1 KiB |
@ -14,6 +14,16 @@
|
||||
"weight": 50,
|
||||
"family": "Noto Sans"
|
||||
},
|
||||
"medium": {
|
||||
"size": 1.16,
|
||||
"weight": 50,
|
||||
"family": "Noto Sans"
|
||||
},
|
||||
"medium_bold": {
|
||||
"size": 1.16,
|
||||
"weight": 63,
|
||||
"family": "Noto Sans"
|
||||
},
|
||||
"default": {
|
||||
"size": 1.0,
|
||||
"weight": 50,
|
||||
@ -289,7 +299,21 @@
|
||||
"layerview_move_combing": [0, 0, 255, 255],
|
||||
"layerview_move_retraction": [128, 128, 255, 255],
|
||||
"layerview_support_interface": [64, 192, 255, 255],
|
||||
"layerview_nozzle": [181, 166, 66, 50]
|
||||
"layerview_nozzle": [181, 166, 66, 50],
|
||||
|
||||
"configuration_item": [255, 255, 255, 0],
|
||||
"configuration_item_active": [12, 169, 227, 32],
|
||||
"configuration_item_text": [0, 0, 0, 255],
|
||||
"configuration_item_text_active": [0, 0, 0, 255],
|
||||
"configuration_item_border": [127, 127, 127, 255],
|
||||
"configuration_item_border_active": [12, 169, 227, 32],
|
||||
"configuration_item_border_hover": [12, 169, 227, 255],
|
||||
|
||||
"tab_status_connected": [12, 169, 227, 255],
|
||||
"tab_status_disconnected": [200, 200, 200, 255],
|
||||
|
||||
"printer_config_matched": [12, 169, 227, 255],
|
||||
"printer_config_mismatch": [127, 127, 127, 255]
|
||||
},
|
||||
|
||||
"sizes": {
|
||||
@ -342,6 +366,9 @@
|
||||
"small_button": [2, 2],
|
||||
"small_button_icon": [1.5, 1.5],
|
||||
|
||||
"printer_status_icon": [1.8, 1.8],
|
||||
"printer_sync_icon": [1.2, 1.2],
|
||||
|
||||
"topbar_logo_right_margin": [3, 0],
|
||||
"topbar_button": [8, 4],
|
||||
"topbar_button_icon": [1.2, 1.2],
|
||||
|
@ -45,7 +45,6 @@ retraction_amount = 6.5
|
||||
retraction_count_max = 25
|
||||
retraction_extrusion_window = 1
|
||||
retraction_hop = 2
|
||||
retraction_hop_enabled = True
|
||||
retraction_hop_only_when_collides = True
|
||||
skin_overlap = 5
|
||||
speed_equalize_flow_enabled = True
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user