Merge branch 'master' of ssh://github.com/Ultimaker/Cura

This commit is contained in:
Ghostkeeper 2020-03-02 14:14:51 +01:00
commit 0e564abbea
No known key found for this signature in database
GPG key ID: 37E2020986774393
11 changed files with 114 additions and 27 deletions

View file

@ -173,7 +173,10 @@ class Arrange:
def bestSpot(self, shape_arr, start_prio = 0, step = 1):
start_idx_list = numpy.where(self._priority_unique_values == start_prio)
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:
start_idx = 0
for priority in self._priority_unique_values[start_idx::step]:

View file

@ -2,6 +2,7 @@
# Cura is released under the terms of the LGPLv3 or higher.
from PyQt5.QtCore import pyqtProperty, pyqtSignal, Qt
from typing import Set
import cura.CuraApplication
from UM.Logger import Logger
@ -23,7 +24,7 @@ class QualitySettingsModel(ListModel):
GLOBAL_STACK_POSITION = -1
def __init__(self, parent = None):
def __init__(self, parent = None) -> None:
super().__init__(parent = parent)
self.addRoleName(self.KeyRole, "key")
@ -38,7 +39,9 @@ class QualitySettingsModel(ListModel):
self._application = cura.CuraApplication.CuraApplication.getInstance()
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._i18n_catalog = None
@ -47,14 +50,14 @@ class QualitySettingsModel(ListModel):
selectedPositionChanged = pyqtSignal()
selectedQualityItemChanged = pyqtSignal()
def setSelectedPosition(self, selected_position):
def setSelectedPosition(self, selected_position: int) -> None:
if selected_position != self._selected_position:
self._selected_position = selected_position
self.selectedPositionChanged.emit()
self._update()
@pyqtProperty(int, fset = setSelectedPosition, notify = selectedPositionChanged)
def selectedPosition(self):
def selectedPosition(self) -> int:
return self._selected_position
def setSelectedQualityItem(self, selected_quality_item):
@ -67,7 +70,7 @@ class QualitySettingsModel(ListModel):
def selectedQualityItem(self):
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__))
if not self._selected_quality_item:
@ -83,7 +86,7 @@ class QualitySettingsModel(ListModel):
quality_changes_group = self._selected_quality_item["quality_changes_group"]
quality_node = None
settings_keys = set()
settings_keys = set() # type: Set[str]
if quality_group:
if self._selected_position == self.GLOBAL_STACK_POSITION:
quality_node = quality_group.node_for_global

View file

@ -36,8 +36,10 @@ class ConvexHullDecorator(SceneNodeDecorator):
# Make sure the timer is created on the main thread
self._recompute_convex_hull_timer = None # type: Optional[QTimer]
self._timer_scheduled_to_be_created = False
from cura.CuraApplication import CuraApplication
if CuraApplication.getInstance() is not None:
self._timer_scheduled_to_be_created = True
CuraApplication.getInstance().callLater(self.createRecomputeConvexHullTimer)
self._raft_thickness = 0.0
@ -171,7 +173,12 @@ class ConvexHullDecorator(SceneNodeDecorator):
if self._recompute_convex_hull_timer is not None:
self._recompute_convex_hull_timer.start()
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:
controller = Application.getInstance().getController()

View file

@ -58,7 +58,10 @@ class CuraStackBuilder:
# Create ExtruderStacks
extruder_dict = machine_definition.getMetaDataEntry("machine_extruder_trains")
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.
registry.addContainer(new_extruder)

View file

@ -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.
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()
## 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
@ -336,9 +347,9 @@ class MachineManager(QObject):
return cast(GlobalStack, machine)
return None
@pyqtSlot(str)
@pyqtSlot(str, str)
def addMachine(self, definition_id: str, name: Optional[str] = None) -> None:
@pyqtSlot(str, result=bool)
@pyqtSlot(str, str, result = bool)
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)
if name is None:
definitions = CuraContainerRegistry.getInstance().findDefinitionContainers(id = definition_id)
@ -353,6 +364,8 @@ class MachineManager(QObject):
self.setActiveMachine(new_stack.getId())
else:
Logger.log("w", "Failed creating a new machine!")
return False
return True
def _checkStacksHaveErrors(self) -> bool:
time_start = time.time()
@ -1507,7 +1520,13 @@ class MachineManager(QObject):
if quality_id == empty_quality_container.getId():
extruder.intent = empty_intent_container
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():
if intent_node.intent_category == intent_category: # Found an intent with the correct category.

View file

@ -73,6 +73,8 @@ class ModelChecker(QObject, Extension):
# Check node material shrinkage and bounding box size
for node in self.sliceableNodes():
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
# has not done yet.

View file

@ -484,15 +484,53 @@ UM.Dialog
onClicked: dialog.accept()
}
Cura.SecondaryButton
Item
{
objectName: "postProcessingSaveAreaButton"
visible: activeScriptsList.count > 0
height: UM.Theme.getSize("action_button").height
width: height
tooltip: catalog.i18nc("@info:tooltip", "Change active post-processing scripts")
onClicked: dialog.show()
iconSource: "postprocessing.svg"
fixedWidthMode: true
Cura.SecondaryButton
{
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
}
}
}

View file

@ -42,7 +42,7 @@ Item
rightMargin: UM.Theme.getSize("wide_margin").width
}
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()
}

View file

@ -133,7 +133,19 @@ Button
Cura.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

View file

@ -151,9 +151,10 @@ Item
// Create a local printer
const localPrinterItem = addLocalPrinterDropDown.contentItem.currentItem
const printerName = addLocalPrinterDropDown.contentItem.printerName
Cura.MachineManager.addMachine(localPrinterItem.id, printerName)
base.showNextPage()
if(Cura.MachineManager.addMachine(localPrinterItem.id, printerName))
{
base.showNextPage()
}
}
}
}

View file

@ -6,6 +6,7 @@ from UM.Settings.InstanceContainer import InstanceContainer
from cura.Machines.QualityGroup import QualityGroup
from cura.Settings.CuraStackBuilder import CuraStackBuilder
@pytest.fixture
def global_variant():
container = InstanceContainer(container_id="global_variant")
@ -13,6 +14,7 @@ def global_variant():
return container
@pytest.fixture
def material_instance_container():
container = InstanceContainer(container_id="material container")
@ -20,6 +22,7 @@ def material_instance_container():
return container
@pytest.fixture
def 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,
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.container = global_variant
variant_manager.getDefaultVariantNode = MagicMock(return_value = global_variant_node)
quality_group = QualityGroup(name = "zomg", quality_type = "normal")
quality_group.node_for_global = MagicMock(name = "Node for global")
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.empty_material_container = material_instance_container