mirror of
https://github.com/Ultimaker/Cura.git
synced 2025-07-24 23:23:57 -06:00
Merge branch 'master' of ssh://github.com/Ultimaker/Cura
This commit is contained in:
commit
0e564abbea
11 changed files with 114 additions and 27 deletions
|
@ -173,7 +173,10 @@ class Arrange:
|
||||||
def bestSpot(self, shape_arr, start_prio = 0, step = 1):
|
def bestSpot(self, shape_arr, start_prio = 0, step = 1):
|
||||||
start_idx_list = numpy.where(self._priority_unique_values == start_prio)
|
start_idx_list = numpy.where(self._priority_unique_values == start_prio)
|
||||||
if start_idx_list:
|
if start_idx_list:
|
||||||
start_idx = start_idx_list[0][0]
|
try:
|
||||||
|
start_idx = start_idx_list[0][0]
|
||||||
|
except IndexError:
|
||||||
|
start_idx = 0
|
||||||
else:
|
else:
|
||||||
start_idx = 0
|
start_idx = 0
|
||||||
for priority in self._priority_unique_values[start_idx::step]:
|
for priority in self._priority_unique_values[start_idx::step]:
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
# Cura is released under the terms of the LGPLv3 or higher.
|
# Cura is released under the terms of the LGPLv3 or higher.
|
||||||
|
|
||||||
from PyQt5.QtCore import pyqtProperty, pyqtSignal, Qt
|
from PyQt5.QtCore import pyqtProperty, pyqtSignal, Qt
|
||||||
|
from typing import Set
|
||||||
|
|
||||||
import cura.CuraApplication
|
import cura.CuraApplication
|
||||||
from UM.Logger import Logger
|
from UM.Logger import Logger
|
||||||
|
@ -23,7 +24,7 @@ class QualitySettingsModel(ListModel):
|
||||||
|
|
||||||
GLOBAL_STACK_POSITION = -1
|
GLOBAL_STACK_POSITION = -1
|
||||||
|
|
||||||
def __init__(self, parent = None):
|
def __init__(self, parent = None) -> None:
|
||||||
super().__init__(parent = parent)
|
super().__init__(parent = parent)
|
||||||
|
|
||||||
self.addRoleName(self.KeyRole, "key")
|
self.addRoleName(self.KeyRole, "key")
|
||||||
|
@ -38,7 +39,9 @@ class QualitySettingsModel(ListModel):
|
||||||
self._application = cura.CuraApplication.CuraApplication.getInstance()
|
self._application = cura.CuraApplication.CuraApplication.getInstance()
|
||||||
self._application.getMachineManager().activeStackChanged.connect(self._update)
|
self._application.getMachineManager().activeStackChanged.connect(self._update)
|
||||||
|
|
||||||
self._selected_position = self.GLOBAL_STACK_POSITION #Must be either GLOBAL_STACK_POSITION or an extruder position (0, 1, etc.)
|
# Must be either GLOBAL_STACK_POSITION or an extruder position (0, 1, etc.)
|
||||||
|
self._selected_position = self.GLOBAL_STACK_POSITION
|
||||||
|
|
||||||
self._selected_quality_item = None # The selected quality in the quality management page
|
self._selected_quality_item = None # The selected quality in the quality management page
|
||||||
self._i18n_catalog = None
|
self._i18n_catalog = None
|
||||||
|
|
||||||
|
@ -47,14 +50,14 @@ class QualitySettingsModel(ListModel):
|
||||||
selectedPositionChanged = pyqtSignal()
|
selectedPositionChanged = pyqtSignal()
|
||||||
selectedQualityItemChanged = pyqtSignal()
|
selectedQualityItemChanged = pyqtSignal()
|
||||||
|
|
||||||
def setSelectedPosition(self, selected_position):
|
def setSelectedPosition(self, selected_position: int) -> None:
|
||||||
if selected_position != self._selected_position:
|
if selected_position != self._selected_position:
|
||||||
self._selected_position = selected_position
|
self._selected_position = selected_position
|
||||||
self.selectedPositionChanged.emit()
|
self.selectedPositionChanged.emit()
|
||||||
self._update()
|
self._update()
|
||||||
|
|
||||||
@pyqtProperty(int, fset = setSelectedPosition, notify = selectedPositionChanged)
|
@pyqtProperty(int, fset = setSelectedPosition, notify = selectedPositionChanged)
|
||||||
def selectedPosition(self):
|
def selectedPosition(self) -> int:
|
||||||
return self._selected_position
|
return self._selected_position
|
||||||
|
|
||||||
def setSelectedQualityItem(self, selected_quality_item):
|
def setSelectedQualityItem(self, selected_quality_item):
|
||||||
|
@ -67,7 +70,7 @@ class QualitySettingsModel(ListModel):
|
||||||
def selectedQualityItem(self):
|
def selectedQualityItem(self):
|
||||||
return self._selected_quality_item
|
return self._selected_quality_item
|
||||||
|
|
||||||
def _update(self):
|
def _update(self) -> None:
|
||||||
Logger.log("d", "Updating {model_class_name}.".format(model_class_name = self.__class__.__name__))
|
Logger.log("d", "Updating {model_class_name}.".format(model_class_name = self.__class__.__name__))
|
||||||
|
|
||||||
if not self._selected_quality_item:
|
if not self._selected_quality_item:
|
||||||
|
@ -83,7 +86,7 @@ class QualitySettingsModel(ListModel):
|
||||||
quality_changes_group = self._selected_quality_item["quality_changes_group"]
|
quality_changes_group = self._selected_quality_item["quality_changes_group"]
|
||||||
|
|
||||||
quality_node = None
|
quality_node = None
|
||||||
settings_keys = set()
|
settings_keys = set() # type: Set[str]
|
||||||
if quality_group:
|
if quality_group:
|
||||||
if self._selected_position == self.GLOBAL_STACK_POSITION:
|
if self._selected_position == self.GLOBAL_STACK_POSITION:
|
||||||
quality_node = quality_group.node_for_global
|
quality_node = quality_group.node_for_global
|
||||||
|
|
|
@ -36,8 +36,10 @@ class ConvexHullDecorator(SceneNodeDecorator):
|
||||||
|
|
||||||
# Make sure the timer is created on the main thread
|
# Make sure the timer is created on the main thread
|
||||||
self._recompute_convex_hull_timer = None # type: Optional[QTimer]
|
self._recompute_convex_hull_timer = None # type: Optional[QTimer]
|
||||||
|
self._timer_scheduled_to_be_created = False
|
||||||
from cura.CuraApplication import CuraApplication
|
from cura.CuraApplication import CuraApplication
|
||||||
if CuraApplication.getInstance() is not None:
|
if CuraApplication.getInstance() is not None:
|
||||||
|
self._timer_scheduled_to_be_created = True
|
||||||
CuraApplication.getInstance().callLater(self.createRecomputeConvexHullTimer)
|
CuraApplication.getInstance().callLater(self.createRecomputeConvexHullTimer)
|
||||||
|
|
||||||
self._raft_thickness = 0.0
|
self._raft_thickness = 0.0
|
||||||
|
@ -171,7 +173,12 @@ class ConvexHullDecorator(SceneNodeDecorator):
|
||||||
if self._recompute_convex_hull_timer is not None:
|
if self._recompute_convex_hull_timer is not None:
|
||||||
self._recompute_convex_hull_timer.start()
|
self._recompute_convex_hull_timer.start()
|
||||||
else:
|
else:
|
||||||
self.recomputeConvexHull()
|
from cura.CuraApplication import CuraApplication
|
||||||
|
if not self._timer_scheduled_to_be_created:
|
||||||
|
# The timer is not created and we never scheduled it. Time to create it now!
|
||||||
|
CuraApplication.getInstance().callLater(self.createRecomputeConvexHullTimer)
|
||||||
|
# Now we know for sure that the timer has been scheduled for creation, so we can try this again.
|
||||||
|
CuraApplication.getInstance().callLater(self.recomputeConvexHullDelayed)
|
||||||
|
|
||||||
def recomputeConvexHull(self) -> None:
|
def recomputeConvexHull(self) -> None:
|
||||||
controller = Application.getInstance().getController()
|
controller = Application.getInstance().getController()
|
||||||
|
|
|
@ -58,7 +58,10 @@ class CuraStackBuilder:
|
||||||
# Create ExtruderStacks
|
# Create ExtruderStacks
|
||||||
extruder_dict = machine_definition.getMetaDataEntry("machine_extruder_trains")
|
extruder_dict = machine_definition.getMetaDataEntry("machine_extruder_trains")
|
||||||
for position in extruder_dict:
|
for position in extruder_dict:
|
||||||
cls.createExtruderStackWithDefaultSetup(new_global_stack, position)
|
try:
|
||||||
|
cls.createExtruderStackWithDefaultSetup(new_global_stack, position)
|
||||||
|
except IndexError:
|
||||||
|
return None
|
||||||
|
|
||||||
for new_extruder in new_global_stack.extruders.values(): # Only register the extruders if we're sure that all of them are correct.
|
for new_extruder in new_global_stack.extruders.values(): # Only register the extruders if we're sure that all of them are correct.
|
||||||
registry.addContainer(new_extruder)
|
registry.addContainer(new_extruder)
|
||||||
|
|
|
@ -320,8 +320,19 @@ class MachineManager(QObject):
|
||||||
# This signal might not have been emitted yet (if it didn't change) but we still want the models to update that depend on it because we changed the contents of the containers too.
|
# This signal might not have been emitted yet (if it didn't change) but we still want the models to update that depend on it because we changed the contents of the containers too.
|
||||||
extruder_manager.activeExtruderChanged.emit()
|
extruder_manager.activeExtruderChanged.emit()
|
||||||
|
|
||||||
|
# Validate if the machine has the correct variants
|
||||||
|
# It can happen that a variant is empty, even though the machine has variants. This will ensure that that
|
||||||
|
# that situation will be fixed (and not occur again, since it switches it out to the preferred variant instead!)
|
||||||
|
machine_node = ContainerTree.getInstance().machines[global_stack.definition.getId()]
|
||||||
|
for extruder in self._global_container_stack.extruderList:
|
||||||
|
variant_name = self._global_container_stack.variant.getName()
|
||||||
|
variant_node = machine_node.variants.get(variant_name)
|
||||||
|
if variant_node is None:
|
||||||
|
Logger.log("w", "An extruder has an unknown variant, switching it to the preferred variant")
|
||||||
|
self.setVariantByName(extruder.getMetaDataEntry("position"), machine_node.preferred_variant_name)
|
||||||
self.__emitChangedSignals()
|
self.__emitChangedSignals()
|
||||||
|
|
||||||
|
|
||||||
## Given a definition id, return the machine with this id.
|
## 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
|
# 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 definition_id \type{str} definition id that needs to look for
|
||||||
|
@ -336,9 +347,9 @@ class MachineManager(QObject):
|
||||||
return cast(GlobalStack, machine)
|
return cast(GlobalStack, machine)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@pyqtSlot(str)
|
@pyqtSlot(str, result=bool)
|
||||||
@pyqtSlot(str, str)
|
@pyqtSlot(str, str, result = bool)
|
||||||
def addMachine(self, definition_id: str, name: Optional[str] = None) -> None:
|
def addMachine(self, definition_id: str, name: Optional[str] = None) -> bool:
|
||||||
Logger.log("i", "Trying to add a machine with the definition id [%s]", definition_id)
|
Logger.log("i", "Trying to add a machine with the definition id [%s]", definition_id)
|
||||||
if name is None:
|
if name is None:
|
||||||
definitions = CuraContainerRegistry.getInstance().findDefinitionContainers(id = definition_id)
|
definitions = CuraContainerRegistry.getInstance().findDefinitionContainers(id = definition_id)
|
||||||
|
@ -353,6 +364,8 @@ class MachineManager(QObject):
|
||||||
self.setActiveMachine(new_stack.getId())
|
self.setActiveMachine(new_stack.getId())
|
||||||
else:
|
else:
|
||||||
Logger.log("w", "Failed creating a new machine!")
|
Logger.log("w", "Failed creating a new machine!")
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
def _checkStacksHaveErrors(self) -> bool:
|
def _checkStacksHaveErrors(self) -> bool:
|
||||||
time_start = time.time()
|
time_start = time.time()
|
||||||
|
@ -1507,7 +1520,13 @@ class MachineManager(QObject):
|
||||||
if quality_id == empty_quality_container.getId():
|
if quality_id == empty_quality_container.getId():
|
||||||
extruder.intent = empty_intent_container
|
extruder.intent = empty_intent_container
|
||||||
continue
|
continue
|
||||||
quality_node = container_tree.machines[definition_id].variants[variant_name].materials[material_base_file].qualities[quality_id]
|
|
||||||
|
# Yes, we can find this in a single line of code. This makes it easier to read and it has the benefit
|
||||||
|
# that it doesn't lump key errors together for the crashlogs
|
||||||
|
machine_node = container_tree.machines[definition_id]
|
||||||
|
variant_node = machine_node.variants[variant_name]
|
||||||
|
material_node = variant_node.materials[material_base_file]
|
||||||
|
quality_node = material_node.qualities[quality_id]
|
||||||
|
|
||||||
for intent_node in quality_node.intents.values():
|
for intent_node in quality_node.intents.values():
|
||||||
if intent_node.intent_category == intent_category: # Found an intent with the correct category.
|
if intent_node.intent_category == intent_category: # Found an intent with the correct category.
|
||||||
|
|
|
@ -73,6 +73,8 @@ class ModelChecker(QObject, Extension):
|
||||||
# Check node material shrinkage and bounding box size
|
# Check node material shrinkage and bounding box size
|
||||||
for node in self.sliceableNodes():
|
for node in self.sliceableNodes():
|
||||||
node_extruder_position = node.callDecoration("getActiveExtruderPosition")
|
node_extruder_position = node.callDecoration("getActiveExtruderPosition")
|
||||||
|
if node_extruder_position is None:
|
||||||
|
continue
|
||||||
|
|
||||||
# This function can be triggered in the middle of a machine change, so do not proceed if the machine change
|
# This function can be triggered in the middle of a machine change, so do not proceed if the machine change
|
||||||
# has not done yet.
|
# has not done yet.
|
||||||
|
|
|
@ -484,15 +484,53 @@ UM.Dialog
|
||||||
onClicked: dialog.accept()
|
onClicked: dialog.accept()
|
||||||
}
|
}
|
||||||
|
|
||||||
Cura.SecondaryButton
|
Item
|
||||||
{
|
{
|
||||||
objectName: "postProcessingSaveAreaButton"
|
objectName: "postProcessingSaveAreaButton"
|
||||||
visible: activeScriptsList.count > 0
|
visible: activeScriptsList.count > 0
|
||||||
height: UM.Theme.getSize("action_button").height
|
height: UM.Theme.getSize("action_button").height
|
||||||
width: height
|
width: height
|
||||||
tooltip: catalog.i18nc("@info:tooltip", "Change active post-processing scripts")
|
|
||||||
onClicked: dialog.show()
|
Cura.SecondaryButton
|
||||||
iconSource: "postprocessing.svg"
|
{
|
||||||
fixedWidthMode: true
|
height: UM.Theme.getSize("action_button").height
|
||||||
|
tooltip:
|
||||||
|
{
|
||||||
|
var tipText = catalog.i18nc("@info:tooltip", "Change active post-processing scripts.");
|
||||||
|
if (activeScriptsList.count > 0)
|
||||||
|
{
|
||||||
|
tipText += "<br><br>" + catalog.i18ncp("@info:tooltip",
|
||||||
|
"The following script is active:",
|
||||||
|
"The following scripts are active:",
|
||||||
|
activeScriptsList.count
|
||||||
|
) + "<ul>";
|
||||||
|
for(var i = 0; i < activeScriptsList.count; i++)
|
||||||
|
{
|
||||||
|
tipText += "<li>" + manager.getScriptLabelByKey(manager.scriptList[i]) + "</li>";
|
||||||
|
}
|
||||||
|
tipText += "</ul>";
|
||||||
|
}
|
||||||
|
return tipText
|
||||||
|
}
|
||||||
|
toolTipContentAlignment: Cura.ToolTip.ContentAlignment.AlignLeft
|
||||||
|
onClicked: dialog.show()
|
||||||
|
iconSource: "postprocessing.svg"
|
||||||
|
fixedWidthMode: false
|
||||||
|
}
|
||||||
|
|
||||||
|
Cura.NotificationIcon
|
||||||
|
{
|
||||||
|
id: activeScriptCountIcon
|
||||||
|
visible: activeScriptsList.count > 0
|
||||||
|
anchors
|
||||||
|
{
|
||||||
|
top: parent.top
|
||||||
|
right: parent.right
|
||||||
|
rightMargin: (-0.5 * width) | 0
|
||||||
|
topMargin: (-0.5 * height) | 0
|
||||||
|
}
|
||||||
|
|
||||||
|
labelText: activeScriptsList.count
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -42,7 +42,7 @@ Item
|
||||||
rightMargin: UM.Theme.getSize("wide_margin").width
|
rightMargin: UM.Theme.getSize("wide_margin").width
|
||||||
}
|
}
|
||||||
height: UM.Theme.getSize("toolbox_footer_button").height
|
height: UM.Theme.getSize("toolbox_footer_button").height
|
||||||
text: catalog.i18nc("@info:button", "Quit Cura")
|
text: catalog.i18nc("@info:button", "Quit Ultimaker Cura")
|
||||||
onClicked: toolbox.restart()
|
onClicked: toolbox.restart()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -133,7 +133,19 @@ Button
|
||||||
Cura.ToolTip
|
Cura.ToolTip
|
||||||
{
|
{
|
||||||
id: tooltip
|
id: tooltip
|
||||||
visible: button.hovered && buttonTextMetrics.elidedText != buttonText.text
|
visible:
|
||||||
|
{
|
||||||
|
if (!button.hovered)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (tooltipText == button.text)
|
||||||
|
{
|
||||||
|
return buttonTextMetrics.elidedText != buttonText.text;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
targetPoint: Qt.point(parent.x, Math.round(parent.y + parent.height / 2))
|
||||||
}
|
}
|
||||||
|
|
||||||
BusyIndicator
|
BusyIndicator
|
||||||
|
|
|
@ -151,9 +151,10 @@ Item
|
||||||
// Create a local printer
|
// Create a local printer
|
||||||
const localPrinterItem = addLocalPrinterDropDown.contentItem.currentItem
|
const localPrinterItem = addLocalPrinterDropDown.contentItem.currentItem
|
||||||
const printerName = addLocalPrinterDropDown.contentItem.printerName
|
const printerName = addLocalPrinterDropDown.contentItem.printerName
|
||||||
Cura.MachineManager.addMachine(localPrinterItem.id, printerName)
|
if(Cura.MachineManager.addMachine(localPrinterItem.id, printerName))
|
||||||
|
{
|
||||||
base.showNextPage()
|
base.showNextPage()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ from UM.Settings.InstanceContainer import InstanceContainer
|
||||||
from cura.Machines.QualityGroup import QualityGroup
|
from cura.Machines.QualityGroup import QualityGroup
|
||||||
from cura.Settings.CuraStackBuilder import CuraStackBuilder
|
from cura.Settings.CuraStackBuilder import CuraStackBuilder
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def global_variant():
|
def global_variant():
|
||||||
container = InstanceContainer(container_id="global_variant")
|
container = InstanceContainer(container_id="global_variant")
|
||||||
|
@ -13,6 +14,7 @@ def global_variant():
|
||||||
|
|
||||||
return container
|
return container
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def material_instance_container():
|
def material_instance_container():
|
||||||
container = InstanceContainer(container_id="material container")
|
container = InstanceContainer(container_id="material container")
|
||||||
|
@ -20,6 +22,7 @@ def material_instance_container():
|
||||||
|
|
||||||
return container
|
return container
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def quality_container():
|
def quality_container():
|
||||||
container = InstanceContainer(container_id="quality container")
|
container = InstanceContainer(container_id="quality container")
|
||||||
|
@ -54,16 +57,12 @@ def test_createMachineWithUnknownDefinition(application, container_registry):
|
||||||
|
|
||||||
def test_createMachine(application, container_registry, definition_container, global_variant, material_instance_container,
|
def test_createMachine(application, container_registry, definition_container, global_variant, material_instance_container,
|
||||||
quality_container, intent_container, quality_changes_container):
|
quality_container, intent_container, quality_changes_container):
|
||||||
variant_manager = MagicMock(name = "Variant Manager")
|
|
||||||
quality_manager = MagicMock(name = "Quality Manager")
|
|
||||||
global_variant_node = MagicMock(name = "global variant node")
|
global_variant_node = MagicMock(name = "global variant node")
|
||||||
global_variant_node.container = global_variant
|
global_variant_node.container = global_variant
|
||||||
|
|
||||||
variant_manager.getDefaultVariantNode = MagicMock(return_value = global_variant_node)
|
|
||||||
quality_group = QualityGroup(name = "zomg", quality_type = "normal")
|
quality_group = QualityGroup(name = "zomg", quality_type = "normal")
|
||||||
quality_group.node_for_global = MagicMock(name = "Node for global")
|
quality_group.node_for_global = MagicMock(name = "Node for global")
|
||||||
quality_group.node_for_global.container = quality_container
|
quality_group.node_for_global.container = quality_container
|
||||||
quality_manager.getQualityGroups = MagicMock(return_value = {"normal": quality_group})
|
|
||||||
|
|
||||||
application.getContainerRegistry = MagicMock(return_value=container_registry)
|
application.getContainerRegistry = MagicMock(return_value=container_registry)
|
||||||
application.empty_material_container = material_instance_container
|
application.empty_material_container = material_instance_container
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue