Merge remote-tracking branch 'origin/feature_intent' into CURA-6840_intent_profile_visibility

This commit is contained in:
Lipu Fei 2019-10-04 15:00:21 +02:00
commit 3c4d29a814
20 changed files with 404 additions and 312 deletions

View File

@ -142,6 +142,7 @@ class MachineNode(ContainerNode):
parent = CuraApplication.getInstance())
elif groups_by_name[name].intent_category == "default": # Intent category should be stored as "default" if everything is default or as the intent if any of the extruder have an actual intent.
groups_by_name[name].intent_category = quality_changes.get("intent_category", "default")
if "position" in quality_changes: # An extruder profile.
groups_by_name[name].metadata_per_extruder[int(quality_changes["position"])] = quality_changes
else: # Global profile.

View File

@ -87,11 +87,23 @@ class QualityManagementModel(ListModel):
application = cura.CuraApplication.CuraApplication.getInstance()
container_registry = application.getContainerRegistry()
new_name = container_registry.uniqueName(new_name)
global_container = cast(InstanceContainer, container_registry.findContainers(id = quality_changes_group.metadata_for_global["id"])[0])
global_container.setName(new_name)
# CURA-6842
# FIXME: setName() will trigger metaDataChanged signal that are connected with type Qt.AutoConnection. In this
# case, setName() will trigger direct connections which in turn causes the quality changes group and the models
# to update. Because multiple containers need to be renamed, and every time a container gets renamed, updates
# gets triggered and this results in partial updates. For example, if we rename the global quality changes
# container first, the rest of the system still thinks that I have selected "my_profile" instead of
# "my_new_profile", but an update already gets triggered, and the quality changes group that's selected will
# have no container for the global stack, because "my_profile" just got renamed to "my_new_profile". This results
# in crashes because the rest of the system assumes that all data in a QualityChangesGroup will be correct.
#
# Renaming the container for the global stack in the end seems to be ok, because the assumption is mostly based
# on the quality changes container for the global stack.
for metadata in quality_changes_group.metadata_per_extruder.values():
extruder_container = cast(InstanceContainer, container_registry.findContainers(id = metadata["id"])[0])
extruder_container.setName(new_name)
global_container = cast(InstanceContainer, container_registry.findContainers(id=quality_changes_group.metadata_for_global["id"])[0])
global_container.setName(new_name)
quality_changes_group.name = new_name

View File

@ -3,7 +3,7 @@
from typing import Any, Dict, Optional
from PyQt5.QtCore import QObject, pyqtProperty
from PyQt5.QtCore import QObject, pyqtProperty, pyqtSignal
## Data struct to group several quality changes instance containers together.
@ -22,7 +22,14 @@ class QualityChangesGroup(QObject):
self.metadata_for_global = {} # type: Dict[str, Any]
self.metadata_per_extruder = {} # type: Dict[int, Dict[str, Any]]
@pyqtProperty(str, constant = True)
nameChanged = pyqtSignal()
def setName(self, name: str) -> None:
if self._name != name:
self._name = name
self.nameChanged.emit()
@pyqtProperty(str, fset = setName, notify = nameChanged)
def name(self) -> str:
return self._name

View File

@ -1,149 +1,127 @@
# Copyright (c) 2018 Ultimaker B.V.
# Copyright (c) 2019 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
import sys
from typing import List
from shapely import affinity
from shapely.geometry import Polygon
from UM.Scene.Iterator.Iterator import Iterator
from UM.Scene.Iterator import Iterator
from UM.Scene.SceneNode import SceneNode
from functools import cmp_to_key
# Iterator that determines the object print order when one-at a time mode is enabled.
#
# In one-at-a-time mode, only one extruder can be enabled to print. In order to maximize the number of objects we can
# print, we need to print from the corner that's closest to the extruder that's being used. Here is an illustration:
#
# +--------------------------------+
# | |
# | |
# | | - Rectangle represents the complete print head including fans, etc.
# | X X | y - X's are the nozzles
# | (1) (2) | ^
# | | |
# +--------------------------------+ +--> x
#
# In this case, the nozzles are symmetric, nozzle (1) is closer to the bottom left corner while (2) is closer to the
# bottom right. If we use nozzle (1) to print, then we better off printing from the bottom left corner so the print
# head will not collide into an object on its top-right side, which is a very large unused area. Following the same
# logic, if we are printing with nozzle (2), then it's better to print from the bottom-right side.
#
# This iterator determines the print order following the rules above.
#
class OneAtATimeIterator(Iterator):
def __init__(self, scene_node):
from cura.CuraApplication import CuraApplication
self._global_stack = CuraApplication.getInstance().getGlobalContainerStack()
self._original_node_list = []
## Iterator that returns a list of nodes in the order that they need to be printed
# If there is no solution an empty list is returned.
# Take note that the list of nodes can have children (that may or may not contain mesh data)
class OneAtATimeIterator(Iterator.Iterator):
def __init__(self, scene_node) -> None:
super().__init__(scene_node) # Call super to make multiple inheritance work.
self._hit_map = [[]] # type: List[List[bool]] # For each node, which other nodes this hits. A grid of booleans on which nodes hit which.
self._original_node_list = [] # type: List[SceneNode] # The nodes that need to be checked for collisions.
def getMachineNearestCornerToExtruder(self, global_stack):
head_and_fans_coordinates = global_stack.getHeadAndFansCoordinates()
used_extruder = None
for extruder in global_stack.extruders.values():
if extruder.isEnabled:
used_extruder = extruder
break
extruder_offsets = [used_extruder.getProperty("machine_nozzle_offset_x", "value"),
used_extruder.getProperty("machine_nozzle_offset_y", "value")]
# find the corner that's closest to the origin
min_distance2 = sys.maxsize
min_coord = None
for coord in head_and_fans_coordinates:
x = coord[0] - extruder_offsets[0]
y = coord[1] - extruder_offsets[1]
distance2 = x**2 + y**2
if distance2 <= min_distance2:
min_distance2 = distance2
min_coord = coord
return min_coord
def _checkForCollisions(self) -> bool:
all_nodes = []
for node in self._scene_node.getChildren():
if not issubclass(type(node), SceneNode):
continue
convex_hull = node.callDecoration("getConvexHullHead")
if not convex_hull:
continue
bounding_box = node.getBoundingBox()
if not bounding_box:
continue
from UM.Math.Polygon import Polygon
bounding_box_polygon = Polygon([[bounding_box.left, bounding_box.front],
[bounding_box.left, bounding_box.back],
[bounding_box.right, bounding_box.back],
[bounding_box.right, bounding_box.front]])
all_nodes.append({"node": node,
"bounding_box": bounding_box_polygon,
"convex_hull": convex_hull})
has_collisions = False
for i, node_dict in enumerate(all_nodes):
for j, other_node_dict in enumerate(all_nodes):
if i == j:
continue
if node_dict["bounding_box"].intersectsPolygon(other_node_dict["convex_hull"]):
has_collisions = True
break
if has_collisions:
break
return has_collisions
def _fillStack(self):
min_coord = self.getMachineNearestCornerToExtruder(self._global_stack)
transform_x = -int(round(min_coord[0] / abs(min_coord[0])))
transform_y = -int(round(min_coord[1] / abs(min_coord[1])))
machine_size = [self._global_stack.getProperty("machine_width", "value"),
self._global_stack.getProperty("machine_depth", "value")]
def flip_x(polygon):
tm2 = [-1, 0, 0, 1, 0, 0]
return affinity.affine_transform(affinity.translate(polygon, xoff = -machine_size[0]), tm2)
def flip_y(polygon):
tm2 = [1, 0, 0, -1, 0, 0]
return affinity.affine_transform(affinity.translate(polygon, yoff = -machine_size[1]), tm2)
if self._checkForCollisions():
self._node_stack = []
return
## Fills the ``_node_stack`` with a list of scene nodes that need to be
# printed in order.
def _fillStack(self) -> None:
node_list = []
for node in self._scene_node.getChildren():
if not issubclass(type(node), SceneNode):
continue
convex_hull = node.callDecoration("getConvexHull")
if convex_hull:
xmin = min(x for x, _ in convex_hull._points)
xmax = max(x for x, _ in convex_hull._points)
ymin = min(y for _, y in convex_hull._points)
ymax = max(y for _, y in convex_hull._points)
if node.callDecoration("getConvexHull"):
node_list.append(node)
convex_hull_polygon = Polygon.from_bounds(xmin, ymin, xmax, ymax)
if transform_x < 0:
convex_hull_polygon = flip_x(convex_hull_polygon)
if transform_y < 0:
convex_hull_polygon = flip_y(convex_hull_polygon)
node_list.append({"node": node,
"min_coord": [convex_hull_polygon.bounds[0], convex_hull_polygon.bounds[1]],
})
if len(node_list) < 2:
self._node_stack = node_list[:]
return
node_list = sorted(node_list, key = lambda d: d["min_coord"])
# Copy the list
self._original_node_list = node_list[:]
self._node_stack = [d["node"] for d in node_list]
## Initialise the hit map (pre-compute all hits between all objects)
self._hit_map = [[self._checkHit(i,j) for i in node_list] for j in node_list]
# Check if we have to files that block each other. If this is the case, there is no solution!
for a in range(0, len(node_list)):
for b in range(0, len(node_list)):
if a != b and self._hit_map[a][b] and self._hit_map[b][a]:
return
# Sort the original list so that items that block the most other objects are at the beginning.
# This does not decrease the worst case running time, but should improve it in most cases.
sorted(node_list, key = cmp_to_key(self._calculateScore))
todo_node_list = [_ObjectOrder([], node_list)]
while len(todo_node_list) > 0:
current = todo_node_list.pop()
for node in current.todo:
# Check if the object can be placed with what we have and still allows for a solution in the future
if not self._checkHitMultiple(node, current.order) and not self._checkBlockMultiple(node, current.todo):
# We found a possible result. Create new todo & order list.
new_todo_list = current.todo[:]
new_todo_list.remove(node)
new_order = current.order[:] + [node]
if len(new_todo_list) == 0:
# We have no more nodes to check, so quit looking.
self._node_stack = new_order
return
todo_node_list.append(_ObjectOrder(new_order, new_todo_list))
self._node_stack = [] #No result found!
# Check if first object can be printed before the provided list (using the hit map)
def _checkHitMultiple(self, node: SceneNode, other_nodes: List[SceneNode]) -> bool:
node_index = self._original_node_list.index(node)
for other_node in other_nodes:
other_node_index = self._original_node_list.index(other_node)
if self._hit_map[node_index][other_node_index]:
return True
return False
## Check for a node whether it hits any of the other nodes.
# \param node The node to check whether it collides with the other nodes.
# \param other_nodes The nodes to check for collisions.
def _checkBlockMultiple(self, node: SceneNode, other_nodes: List[SceneNode]) -> bool:
node_index = self._original_node_list.index(node)
for other_node in other_nodes:
other_node_index = self._original_node_list.index(other_node)
if self._hit_map[other_node_index][node_index] and node_index != other_node_index:
return True
return False
## Calculate score simply sums the number of other objects it 'blocks'
def _calculateScore(self, a: SceneNode, b: SceneNode) -> int:
score_a = sum(self._hit_map[self._original_node_list.index(a)])
score_b = sum(self._hit_map[self._original_node_list.index(b)])
return score_a - score_b
## Checks if A can be printed before B
def _checkHit(self, a: SceneNode, b: SceneNode) -> bool:
if a == b:
return False
a_hit_hull = a.callDecoration("getConvexHullBoundary")
b_hit_hull = b.callDecoration("getConvexHullHeadFull")
overlap = a_hit_hull.intersectsPolygon(b_hit_hull)
if overlap:
return True
# Adhesion areas must never overlap, regardless of printing order
# This would cause over-extrusion
a_hit_hull = a.callDecoration("getAdhesionArea")
b_hit_hull = b.callDecoration("getAdhesionArea")
overlap = a_hit_hull.intersectsPolygon(b_hit_hull)
if overlap:
return True
else:
return False
## Internal object used to keep track of a possible order in which to print objects.
class _ObjectOrder:
## Creates the _ObjectOrder instance.
# \param order List of indices in which to print objects, ordered by printing
# order.
# \param todo: List of indices which are not yet inserted into the order list.
def __init__(self, order: List[SceneNode], todo: List[SceneNode]):
self.order = order
self.todo = todo

View File

@ -76,7 +76,19 @@ class ConvexHullDecorator(SceneNodeDecorator):
def __deepcopy__(self, memo):
return ConvexHullDecorator()
## Get the unmodified 2D projected convex hull of the node (if any)
## The polygon representing the 2D adhesion area.
# If no adhesion is used, the regular convex hull is returned
def getAdhesionArea(self) -> Optional[Polygon]:
if self._node is None:
return None
hull = self._compute2DConvexHull()
if hull is None:
return None
return self._add2DAdhesionMargin(hull)
## Get the unmodified 2D projected convex hull with 2D adhesion area of the node (if any)
def getConvexHull(self) -> Optional[Polygon]:
if self._node is None:
return None
@ -266,10 +278,14 @@ class ConvexHullDecorator(SceneNodeDecorator):
return offset_hull
def _getHeadAndFans(self) -> Polygon:
if self._global_stack:
return Polygon(numpy.array(self._global_stack.getHeadAndFansCoordinates(), numpy.float32))
if not self._global_stack:
return Polygon()
polygon = Polygon(numpy.array(self._global_stack.getHeadAndFansCoordinates(), numpy.float32))
offset_x = self._getSettingProperty("machine_nozzle_offset_x", "value")
offset_y = self._getSettingProperty("machine_nozzle_offset_y", "value")
return polygon.translate(-offset_x, -offset_y)
def _compute2DConvexHeadFull(self) -> Optional[Polygon]:
convex_hull = self._compute2DConvexHull()
if convex_hull:

View File

@ -21,6 +21,7 @@ from UM.Message import Message
from UM.Platform import Platform
from UM.PluginRegistry import PluginRegistry # For getting the possible profile writers to write with.
from UM.Resources import Resources
from UM.Util import parseBool
from cura.ReaderWriters.ProfileWriter import ProfileWriter
from . import ExtruderStack
@ -238,7 +239,8 @@ class CuraContainerRegistry(ContainerRegistry):
# Get the expected machine definition.
# i.e.: We expect gcode for a UM2 Extended to be defined as normal UM2 gcode...
profile_definition = container_tree.machines[machine_definition.getId()].quality_definition
has_machine_quality = parseBool(machine_definition.getMetaDataEntry("has_machine_quality", "false"))
profile_definition = machine_definition.getMetaDataEntry("quality_definition", machine_definition.getId()) if has_machine_quality else "fdmprinter"
expected_machine_definition = container_tree.machines[global_stack.definition.getId()].quality_definition
# And check if the profile_definition matches either one (showing error if not):
@ -293,6 +295,7 @@ class CuraContainerRegistry(ContainerRegistry):
profile_or_list.append(profile)
# Import all profiles
profile_ids_added = [] # type: List[str]
for profile_index, profile in enumerate(profile_or_list):
if profile_index == 0:
# This is assumed to be the global profile
@ -313,11 +316,15 @@ class CuraContainerRegistry(ContainerRegistry):
result = self._configureProfile(profile, profile_id, new_name, expected_machine_definition)
if result is not None:
# Remove any profiles that did got added.
for profile_id in profile_ids_added:
self.removeContainer(profile_id)
return {"status": "error", "message": catalog.i18nc(
"@info:status Don't translate the XML tag <filename>!",
"Failed to import profile from <filename>{0}</filename>:",
file_name) + " " + result}
profile_ids_added.append(profile.getId())
return {"status": "ok", "message": catalog.i18nc("@info:status", "Successfully imported profile {0}", profile_or_list[0].getName())}
# This message is throw when the profile reader doesn't find any profile in the file

View File

@ -123,6 +123,14 @@ class MachineManager(QObject):
self.globalContainerChanged.connect(self.printerConnectedStatusChanged)
self.outputDevicesChanged.connect(self.printerConnectedStatusChanged)
# For updating active quality display name
self.activeQualityChanged.connect(self.activeQualityDisplayNameChanged)
self.activeIntentChanged.connect(self.activeQualityDisplayNameChanged)
self.activeQualityGroupChanged.connect(self.activeQualityDisplayNameChanged)
self.activeQualityChangesGroupChanged.connect(self.activeQualityDisplayNameChanged)
activeQualityDisplayNameChanged = pyqtSignal()
activeQualityGroupChanged = pyqtSignal()
activeQualityChangesGroupChanged = pyqtSignal()
@ -640,13 +648,14 @@ class MachineManager(QObject):
active_intent_category = self.activeIntentCategory
result = []
for extruder in global_container_stack.extruderList:
if not extruder.isEnabled:
continue
category = extruder.intent.getMetaDataEntry("intent_category", "default")
if category != active_intent_category:
result.append(str(int(extruder.getMetaDataEntry("position")) + 1))
return result
## Returns whether there is anything unsupported in the current set-up.
#
# The current set-up signifies the global stack and all extruder stacks,
@ -1044,6 +1053,7 @@ class MachineManager(QObject):
self.forceUpdateAllSettings()
# Also trigger the build plate compatibility to update
self.activeMaterialChanged.emit()
self.activeIntentChanged.emit()
def _onMachineNameChanged(self) -> None:
self.globalContainerChanged.emit()
@ -1357,11 +1367,7 @@ class MachineManager(QObject):
# If we can keep the current material after the switch, try to do so.
nozzle_node = ContainerTree.getInstance().machines[self._global_container_stack.definition.getId()].variants[current_nozzle_name]
candidate_materials = nozzle_node.materials
old_approximate_material_diameter = None # type: Optional[float]
if candidate_materials:
candidate_material = list(candidate_materials.values())[0]
default_material_diameter = "2.85"
old_approximate_material_diameter = int(round(float(candidate_material.container.getMetaDataEntry("properties/diameter", default_material_diameter))))
old_approximate_material_diameter = int(extruder.material.getMetaDataEntry("approximate_diameter", default = 3))
new_approximate_material_diameter = int(self._global_container_stack.extruderList[int(position_item)].getApproximateMaterialDiameter())
# Only switch to the old candidate material if the approximate material diameter of the extruder stays the
@ -1583,6 +1589,34 @@ class MachineManager(QObject):
if not no_dialog and self.hasUserSettings and self._application.getPreferences().getValue("cura/active_mode") == 1:
self._application.discardOrKeepProfileChanges()
# The display name of currently active quality.
# This display name is:
# - For built-in qualities (quality/intent): the quality type name, such as "Fine", "Normal", etc.
# - For custom qualities: <custom_quality_name> - <intent_name> - <quality_type_name>
# Examples:
# - "my_profile - Fine" (only based on a default quality, no intent involved)
# - "my_profile - Engineering - Fine" (based on an intent)
@pyqtProperty(str, notify = activeQualityDisplayNameChanged)
def activeQualityDisplayName(self) -> str:
global_stack = cura.CuraApplication.CuraApplication.getInstance().getGlobalContainerStack()
if global_stack is None:
return ""
# Not a custom quality
display_name = self.activeQualityOrQualityChangesName
if global_stack.qualityChanges == empty_quality_changes_container:
return display_name
# A custom quality
intent_category = self.activeIntentCategory
if intent_category != "default":
from cura.Machines.Models.IntentCategoryModel import IntentCategoryModel
intent_display_name = IntentCategoryModel.name_translation.get(intent_category, catalog.i18nc("@label", "Unknown"))
display_name += " - {intent_name}".format(intent_name = intent_display_name)
display_name += " - {quality_level_name}".format(quality_level_name = global_stack.quality.getName())
return display_name
## Change the intent category of the current printer.
#
# All extruders can change their profiles. If an intent profile is
@ -1672,6 +1706,13 @@ class MachineManager(QObject):
global_container_stack = cura.CuraApplication.CuraApplication.getInstance().getGlobalContainerStack()
return (not global_container_stack is None) and global_container_stack.quality == empty_quality_container and global_container_stack.qualityChanges == empty_quality_changes_container
@pyqtProperty(bool, notify = activeQualityGroupChanged)
def isActiveQualityCustom(self) -> bool:
global_stack = cura.CuraApplication.CuraApplication.getInstance().getGlobalContainerStack()
if global_stack is None:
return False
return global_stack.qualityChanges != empty_quality_changes_container
def _updateUponMaterialMetadataChange(self) -> None:
if self._global_container_stack is None:
return

View File

@ -97,7 +97,7 @@ class CuraProfileReader(ProfileReader):
if global_stack is None:
return None
active_quality_definition = ContainerTree.getInstance().machines[global_stack.definition.container_id].quality_definition
active_quality_definition = ContainerTree.getInstance().machines[global_stack.definition.getId()].quality_definition
if profile.getMetaDataEntry("definition") != active_quality_definition:
profile.setMetaDataEntry("definition", active_quality_definition)
return profile

View File

@ -1888,7 +1888,7 @@
"unit": "mm",
"type": "float",
"default_value": 0.1,
"minimum_value": "resolveOrValue('layer_height') if infill_line_distance > 0 else -999999",
"minimum_value": "resolveOrValue('layer_height') / 2 if infill_line_distance > 0 else -999999",
"maximum_value_warning": "0.75 * machine_nozzle_size",
"maximum_value": "resolveOrValue('layer_height') * (1.45 if spaghetti_infill_enabled else 8) if infill_line_distance > 0 else 999999",
"value": "resolveOrValue('layer_height')",

View File

@ -7,7 +7,7 @@ import QtQuick.Layouts 1.3
import UM 1.2 as UM
// The labelBar shows a set of labels that are evenly spaced from oneother.
// The labelBar shows a set of labels that are evenly spaced from one another.
// The first item is aligned to the left, the last is aligned to the right.
// It's intended to be used together with RadioCheckBar. As such, it needs
// to know what the used itemSize is, so it can ensure the labels are aligned correctly.

View File

@ -38,14 +38,28 @@ Tab
property var setting: qualitySettings.getItem(styleData.row)
height: childrenRect.height
width: (parent != null) ? parent.width : 0
text: (styleData.value.substr(0,1) == "=") ? styleData.value : ""
text:
{
if (styleData.value === undefined)
{
return ""
}
return (styleData.value.substr(0,1) == "=") ? styleData.value : ""
}
Label
{
anchors.left: parent.left
anchors.leftMargin: UM.Theme.getSize("default_margin").width
anchors.right: parent.right
text: (styleData.value.substr(0,1) == "=") ? catalog.i18nc("@info:status", "Calculated") : styleData.value
text:
{
if (styleData.value === undefined)
{
return ""
}
return (styleData.value.substr(0,1) == "=") ? catalog.i18nc("@info:status", "Calculated") : styleData.value
}
font.strikeout: styleData.column == 1 && setting.user_value != "" && base.isQualityItemCurrentlyActivated
font.italic: setting.profile_value_source == "quality_changes" || (setting.user_value != "" && base.isQualityItemCurrentlyActivated)
opacity: font.strikeout ? 0.5 : 1

View File

@ -7,7 +7,7 @@ import QtQuick.Controls 1.4 as OldControls
import UM 1.3 as UM
import Cura 1.6 as Cura
import ".."
Item
{
@ -50,6 +50,18 @@ Item
verticalAlignment: Text.AlignVCenter
}
NoIntentIcon
{
affected_extruders: Cura.MachineManager.extruderPositionsWithNonActiveIntent
intent_type: Cura.MachineManager.activeIntentCategory
anchors.right: intentSelection.left
anchors.rightMargin: UM.Theme.getSize("narrow_margin").width
width: Math.round(profileLabel.height * 0.5)
anchors.verticalCenter: parent.verticalCenter
height: width
visible: affected_extruders.length
}
Button
{
id: intentSelection
@ -88,14 +100,8 @@ Item
function generateActiveQualityText()
{
var result = Cura.MachineManager.activeQualityDisplayName
var result = ""
if(Cura.MachineManager.activeIntentCategory != "default")
{
result += Cura.MachineManager.activeIntentCategory + " - "
}
result += Cura.MachineManager.activeQualityOrQualityChangesName
if (Cura.MachineManager.isActiveQualityExperimental)
{
result += " (Experimental)"

View File

@ -49,6 +49,6 @@ Button
anchors.leftMargin: UM.Theme.getSize("wide_margin").width
renderType: Text.NativeRendering
font: UM.Theme.getFont("default")
color: UM.Theme.getColor("text")
color: button.enabled ? UM.Theme.getColor("text") :UM.Theme.getColor("text_inactive")
}
}

View File

@ -41,6 +41,18 @@ Popup
contentItem: Column
{
// This repeater adds the intent labels
ScrollView
{
property real maximumHeight: screenScaleFactor * 400
height: Math.min(contentHeight, maximumHeight)
clip: true
ScrollBar.vertical.policy: height == maximumHeight ? ScrollBar.AlwaysOn: ScrollBar.AlwaysOff
Column
{
width: parent.width
Repeater
{
model: dataModel
@ -51,11 +63,7 @@ Popup
property variant subItemModel: model.qualities
height: childrenRect.height
anchors
{
left: parent.left
right: parent.right
}
width: popup.contentWidth
Label
{
@ -116,7 +124,6 @@ Popup
}
}
}
//Another "intent category" for custom profiles.
Item
{
@ -134,7 +141,7 @@ Popup
renderType: Text.NativeRendering
height: visible ? contentHeight: 0
enabled: false
visible: profilesList.visibleChildren.length > 0
visible: profilesList.visibleChildren.length > 1
anchors.left: parent.left
anchors.leftMargin: UM.Theme.getSize("default_margin").width
}
@ -156,7 +163,7 @@ Popup
target: parent
property: "height"
value: parent.childrenRect.height
when: parent.visibleChildren.length > 0
when: parent.visibleChildren.length > 1
}
//Add all the custom profiles.
@ -186,6 +193,8 @@ Popup
}
}
}
}
}
Rectangle
{
@ -238,9 +247,10 @@ Popup
Cura.ContainerManager.clearUserContainers()
}
}
Rectangle
{
height: 1
height: UM.Theme.getSize("default_lining").width
anchors.left: parent.left
anchors.right: parent.right
color: borderColor
@ -260,7 +270,9 @@ Popup
contentItem: Item
{
width: manageProfilesButton.width
width: parent.width
height: childrenRect.height
Label
{
id: textLabel

View File

@ -20,13 +20,8 @@ RowLayout
{
if (Cura.MachineManager.activeStack)
{
var text = ""
if(Cura.MachineManager.activeIntentCategory != "default")
{
text += Cura.MachineManager.activeIntentCategory + " - "
}
var text = Cura.MachineManager.activeQualityDisplayName
text += Cura.MachineManager.activeQualityOrQualityChangesName
if (!Cura.MachineManager.hasNotSupportedQuality)
{
text += " - " + layerHeight.properties.value + "mm"

View File

@ -1,4 +1,4 @@
// Copyright (c) 2018 Ultimaker B.V.
// Copyright (c) 2019 Ultimaker B.V.
// Cura is released under the terms of the LGPLv3 or higher.
import QtQuick 2.10
@ -9,6 +9,7 @@ import QtQuick.Controls.Styles 1.4
import UM 1.2 as UM
import Cura 1.6 as Cura
import ".."
Item
{
id: qualityRow

View File

@ -19,7 +19,7 @@ Item
property int barSize: UM.Theme.getSize("slider_groove_radius").height
property var isCheckedFunction // Function that accepts the modelItem and returns if the item should be active.
implicitWidth: 200
implicitWidth: 200 * screenScaleFactor
implicitHeight: checkboxSize
property var dataModel: null
@ -62,7 +62,7 @@ Item
Layout.fillHeight: true
// The last item of the repeater needs to be shorter, as we don't need another part to fit
// the horizontal bar. The others should essentially not be limited.
Layout.maximumWidth: index + 1 === repeater.count ? activeComponent.width: 200000000
Layout.maximumWidth: index + 1 === repeater.count ? activeComponent.width : 200000000
property bool isEnabled: model.available
// The horizontal bar between the checkable options.
@ -140,7 +140,6 @@ Item
{
anchors
{
margins: 3
fill: parent
}
radius: Math.round(width / 2)

View File

@ -86,7 +86,11 @@ Item
{
id: machineList
cacheBuffer: 1000000 // Set a large cache to effectively just cache every list item.
// CURA-6793
// Enabling the buffer seems to cause the blank items issue. When buffer is enabled, if the ListView's
// individual item has a dynamic change on its visibility, the ListView doesn't redraw itself.
// The default value of cacheBuffer is platform-dependent, so we explicitly disable it here.
cacheBuffer: 0
model: UM.DefinitionContainersModel
{

View File

@ -55,7 +55,6 @@ def test_metadataProperties(container_registry):
# Check if each of the metadata entries got stored properly.
assert not node.has_materials
assert node.has_variants
assert node.has_machine_materials
assert node.has_machine_quality
assert node.quality_definition == metadata_dict["quality_definition"]
assert node.exclude_materials == metadata_dict["exclude_materials"]

View File

@ -13,13 +13,13 @@ material_node_added_test_data = [({"type": "Not a material"}, ["material_1", "ma
({"type": "material", "base_file": "material_3"}, ["material_1", "material_2"]), # material_3 is on the "NOPE" list.
({"type": "material", "base_file": "material_4", "definition": "machine_3"}, ["material_1", "material_2"]), # Wrong machine
({"type": "material", "base_file": "material_4", "definition": "machine_1"}, ["material_1", "material_2"]), # No variant
({"type": "material", "base_file": "material_4", "definition": "machine_1", "variant": "Variant Three"}, ["material_1", "material_2"]), # Wrong variant
({"type": "material", "base_file": "material_4", "definition": "machine_1", "variant": "Variant One"}, ["material_1", "material_2", "material_4"])
({"type": "material", "base_file": "material_4", "definition": "machine_1", "variant_name": "Variant Three"}, ["material_1", "material_2"]), # Wrong variant
({"type": "material", "base_file": "material_4", "definition": "machine_1", "variant_name": "Variant One"}, ["material_1", "material_2", "material_4"])
]
material_node_update_test_data = [({"type": "material", "base_file": "material_1", "definition": "machine_1", "variant": "Variant One"}, ["material_1"], ["material_2"]),
({"type": "material", "base_file": "material_1", "definition": "fdmprinter", "variant": "Variant One"}, [], ["material_2", "material_1"]), # Too generic
({"type": "material", "base_file": "material_1", "definition": "machine_2", "variant": "Variant One"}, [], ["material_2", "material_1"]) # Wrong definition
material_node_update_test_data = [({"type": "material", "base_file": "material_1", "definition": "machine_1", "variant_name": "Variant One"}, ["material_1"], ["material_2"]),
({"type": "material", "base_file": "material_1", "definition": "fdmprinter", "variant_name": "Variant One"}, [], ["material_2", "material_1"]), # Too generic
({"type": "material", "base_file": "material_1", "definition": "machine_2", "variant_name": "Variant One"}, [], ["material_2", "material_1"]) # Wrong definition
]
metadata_dict = {}