Merge branch 'master' into feature_sync_button

This commit is contained in:
Diego Prado Gesto 2018-03-06 16:10:44 +01:00
commit 5280d21c26
16 changed files with 101 additions and 145 deletions

View File

@ -6,7 +6,7 @@ Before filing, PLEASE check if the issue already exists (either open or closed)
Also, please note the application version in the title of the issue. For example: "[3.2.1] Cannot connect to 3rd-party printer". Please do not write thigns like "Request:" or "[BUG]" in the title; this is what labels are for.
It is also helpful to attach a project (.3mf or .curaproject) file and Cura log file so we can debug issues quicker.
Information about how to find the log file can be found at https://github.com/Ultimaker/Cura/wiki/Cura-Preferences-and-Settings-Locations. To upload a project, we recommend http://wetransfer.com, but other file hosts like Google Drive or Dropbox work well too.
Information about how to find the log file can be found at https://github.com/Ultimaker/Cura/wiki/Cura-Preferences-and-Settings-Locations. To upload a project, try changing the extension to e.g. .curaproject.3mf.zip so that github accepts uploading the file. Otherwise we recommend http://wetransfer.com, but other file hosts like Google Drive or Dropbox work well too.
Thank you for using Cura!
-->
@ -26,6 +26,9 @@ Thank you for using Cura!
**Display Driver**
<!-- Video driver name and version -->
**Printer**
<!-- Which printer was selected in Cura. Please attach project file as .curaproject.3mf.zip -->
**Steps to Reproduce**
<!-- Add the steps needed that lead up to the issue (replace this text) -->

View File

@ -3,6 +3,7 @@
from PyQt5.QtCore import Qt, pyqtSignal, pyqtProperty
from UM.Application import Application
from UM.Qt.ListModel import ListModel
@ -25,6 +26,8 @@ class BaseMaterialsModel(ListModel):
def __init__(self, parent = None):
super().__init__(parent)
self._application = Application.getInstance()
self._machine_manager = self._application.getMachineManager()
self.addRoleName(self.RootMaterialIdRole, "root_material_id")
self.addRoleName(self.IdRole, "id")
@ -35,12 +38,33 @@ class BaseMaterialsModel(ListModel):
self.addRoleName(self.ContainerNodeRole, "container_node")
self._extruder_position = 0
self._extruder_stack = None
self._machine_manager.globalContainerChanged.connect(self._updateExtruderStack)
def _updateExtruderStack(self):
global_stack = self._machine_manager.activeMachine
if global_stack is None:
return
if self._extruder_stack is not None:
self._extruder_stack.pyqtContainersChanged.disconnect(self._update)
self._extruder_stack = global_stack.extruders.get(str(self._extruder_position))
if self._extruder_stack is not None:
self._extruder_stack.pyqtContainersChanged.connect(self._update)
def setExtruderPosition(self, position: int):
if self._extruder_position != position:
self._extruder_position = position
self._updateExtruderStack()
self.extruderPositionChanged.emit()
@pyqtProperty(int, fset = setExtruderPosition, notify = extruderPositionChanged)
def extruderPosition(self) -> int:
return self._extruder_positoin
return self._extruder_position
#
# This is an abstract method that needs to be implemented by
#
def _update(self):
pass

View File

@ -120,12 +120,19 @@ class BrandMaterialsModel(ListModel):
material_type_item = {"name": material_type,
"colors": BaseMaterialsModel(self)}
material_type_item["colors"].clear()
# Sort materials by name
material_list = sorted(material_list, key = lambda x: x["name"])
material_type_item["colors"].setItems(material_list)
material_type_item_list.append(material_type_item)
# Sort material type by name
material_type_item_list = sorted(material_type_item_list, key = lambda x: x["name"])
brand_item["materials"].setItems(material_type_item_list)
brand_item_list.append(brand_item)
# Sort brand by name
brand_item_list = sorted(brand_item_list, key = lambda x: x["name"])
self.setItems(brand_item_list)

View File

@ -19,8 +19,6 @@ class ObjectsModel(ListModel):
self._build_plate_number = -1
self._stacks_have_errors = None # type:Optional[bool]
def setActiveBuildPlate(self, nr):
self._build_plate_number = nr
self._update()
@ -69,11 +67,3 @@ class ObjectsModel(ListModel):
@staticmethod
def createObjectsModel():
return ObjectsModel()
## Check if none of the model's stacks contain error states
# The setting applied for the settings per model
def stacksHaveErrors(self) -> bool:
return bool(self._stacks_have_errors)
def setStacksHaveErrors(self, value):
self._stacks_have_errors = value

View File

@ -968,30 +968,38 @@ class MachineManager(QObject):
## Update current quality type and machine after setting material
def _updateQualityWithMaterial(self):
current_quality = None
Logger.log("i", "Updating quality/quality_changes due to material change")
current_quality_type = None
if self._current_quality_group:
current_quality = self._current_quality_group.quality_type
quality_manager = Application.getInstance()._quality_manager
candidate_quality_groups = quality_manager.getQualityGroups(self._global_container_stack)
current_quality_type = self._current_quality_group.quality_type
candidate_quality_groups = self._quality_manager.getQualityGroups(self._global_container_stack)
available_quality_types = {qt for qt, g in candidate_quality_groups.items() if g.is_available}
Logger.log("d", "Current quality type = [%s]", current_quality_type)
if not self.activeMaterialsCompatible():
Logger.log("i", "Active materials are not compatible, setting all qualities to empty (Not Supported).")
self._setEmptyQuality()
return
if not available_quality_types:
Logger.log("i", "No available quality types found, setting all qualities to empty (Not Supported).")
self._setEmptyQuality()
return
if current_quality in available_quality_types:
self._setQualityGroup(candidate_quality_groups[current_quality], empty_quality_changes = False)
if current_quality_type in available_quality_types:
Logger.log("i", "Current available quality type [%s] is available, applying changes.", current_quality_type)
self._setQualityGroup(candidate_quality_groups[current_quality_type], empty_quality_changes = False)
return
# The current quality type is not available so we use the preferred quality type if it's available,
# otherwise use one of the available quality types.
quality_type = sorted(list(available_quality_types))[0]
preferred_quality_type = self._global_container_stack.getMetaDataEntry("preferred_quality_type")
if preferred_quality_type in available_quality_types:
quality_type = preferred_quality_type
Logger.log("i", "The current quality type [%s] is not available, switching to [%s] instead",
current_quality_type, quality_type)
self._setQualityGroup(candidate_quality_groups[quality_type], empty_quality_changes = True)
def _updateMaterialWithVariant(self, position: Optional[str]):
@ -1006,9 +1014,8 @@ class MachineManager(QObject):
current_material_base_name = extruder.material.getMetaDataEntry("base_file")
current_variant_name = extruder.variant.getMetaDataEntry("name")
material_manager = Application.getInstance()._material_manager
material_diameter = self._global_container_stack.getProperty("material_diameter", "value")
candidate_materials = material_manager.getAvailableMaterials(
candidate_materials = self._material_manager.getAvailableMaterials(
self._global_container_stack.definition.getId(),
current_variant_name,
material_diameter)
@ -1065,7 +1072,7 @@ class MachineManager(QObject):
# See if we need to show the Discard or Keep changes screen
if self.hasUserSettings and Preferences.getInstance().getValue("cura/active_mode") == 1:
Application.getInstance().discardOrKeepProfileChanges()
self._application.discardOrKeepProfileChanges()
@pyqtProperty(QObject, fset = setQualityGroup, notify = activeQualityGroupChanged)
def activeQualityGroup(self):
@ -1079,7 +1086,7 @@ class MachineManager(QObject):
# See if we need to show the Discard or Keep changes screen
if self.hasUserSettings and Preferences.getInstance().getValue("cura/active_mode") == 1:
Application.getInstance().discardOrKeepProfileChanges()
self._application.discardOrKeepProfileChanges()
@pyqtProperty(QObject, fset = setQualityChangesGroup, notify = activeQualityChangesGroupChanged)
def activeQualityChangesGroup(self):

View File

@ -9,8 +9,7 @@ from UM.Signal import Signal, signalemitter
from UM.Settings.InstanceContainer import InstanceContainer
from UM.Settings.ContainerRegistry import ContainerRegistry
from UM.Logger import Logger
from UM.Settings.Validator import ValidatorState
from PyQt5.QtCore import QTimer
from UM.Application import Application
from cura.Settings.PerObjectContainerStack import PerObjectContainerStack
@ -40,10 +39,6 @@ class SettingOverrideDecorator(SceneNodeDecorator):
self._extruder_stack = ExtruderManager.getInstance().getExtruderStack(0).getId()
self._is_non_printing_mesh = False
self._error_check_timer = QTimer()
self._error_check_timer.setInterval(250)
self._error_check_timer.setSingleShot(True)
self._error_check_timer.timeout.connect(self._checkStackForErrors)
self._stack.propertyChanged.connect(self._onSettingChanged)
@ -99,22 +94,10 @@ class SettingOverrideDecorator(SceneNodeDecorator):
# Trigger slice/need slicing if the value has changed.
if property_name == "value":
self._is_non_printing_mesh = any(bool(self._stack.getProperty(setting, "value")) for setting in self._non_printing_mesh_settings)
if not self._is_non_printing_mesh:
# self._error_check_timer.start()
self._checkStackForErrors()
Application.getInstance().getBackend().needsSlicing()
Application.getInstance().getBackend().tickle()
def _checkStackForErrors(self):
hasErrors = False;
for key in self._stack.getAllKeys():
validation_state = self._stack.getProperty(key, "validationState")
if validation_state in (ValidatorState.Exception, ValidatorState.MaximumError, ValidatorState.MinimumError):
Logger.log("w", "Setting Per Object %s is not valid.", key)
hasErrors = True
break
Application.getInstance().getObjectsModel().setStacksHaveErrors(hasErrors)
## Makes sure that the stack upon which the container stack is placed is
# kept up to date.
def _updateNextStack(self):

View File

@ -136,14 +136,10 @@ class StartSliceJob(Job):
self.setResult(StartJobResult.MaterialIncompatible)
return
# Validate settings per selectable model
if Application.getInstance().getObjectsModel().stacksHaveErrors():
self.setResult(StartJobResult.ObjectSettingError)
return
# Don't slice if there is a per object setting with an error value.
for node in DepthFirstIterator(self._scene.getRoot()):
if node.isSelectable():
if type(node) is not CuraSceneNode or not node.isSelectable():
continue
if self._checkStackForErrors(node.callDecoration("getStack")):

View File

@ -24,21 +24,8 @@ UM.TooltipArea
checked: addedSettingsModel.getVisible(model.key)
onClicked:
{
// Important first set visible and then subscribe
// otherwise the setting is not yet in list
// For unsubscribe is important first remove the subscription and then
// set as invisible
if(checked)
{
addedSettingsModel.setVisible(model.key, checked);
UM.ActiveTool.triggerActionWithData("subscribeForSettingValidation", model.key)
}
else
{
UM.ActiveTool.triggerActionWithData("unsubscribeForSettingValidation", model.key)
addedSettingsModel.setVisible(model.key, checked);
}
UM.ActiveTool.forceUpdate();
}
}

View File

@ -240,10 +240,7 @@ Item {
width: Math.round(UM.Theme.getSize("setting").height / 2)
height: UM.Theme.getSize("setting").height
onClicked: {
addedSettingsModel.setVisible(model.key, false)
UM.ActiveTool.triggerActionWithData("unsubscribeForSettingValidation", model.key)
}
onClicked: addedSettingsModel.setVisible(model.key, false)
style: ButtonStyle
{

View File

@ -10,10 +10,7 @@ from cura.Settings.SettingOverrideDecorator import SettingOverrideDecorator
from cura.Settings.ExtruderManager import ExtruderManager
from UM.Settings.SettingInstance import SettingInstance
from UM.Event import Event
from UM.Settings.Validator import ValidatorState
from UM.Logger import Logger
from PyQt5.QtCore import QTimer
## This tool allows the user to add & change settings per node in the scene.
# The settings per object are kept in a ContainerStack, which is linked to a node by decorator.
@ -37,12 +34,6 @@ class PerObjectSettingsTool(Tool):
self._onGlobalContainerChanged()
Selection.selectionChanged.connect(self._updateEnabled)
self._scene = Application.getInstance().getController().getScene()
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)
def event(self, event):
super().event(event)
@ -151,65 +142,3 @@ class PerObjectSettingsTool(Tool):
else:
self._single_model_selected = True
Application.getInstance().getController().toolEnabledChanged.emit(self._plugin_id, self._advanced_mode and self._single_model_selected)
def _onPropertyChanged(self, key: str, property_name: str) -> None:
if property_name == "validationState":
# self._error_check_timer.start()
return
def _updateStacksHaveErrors(self) -> None:
return
# self._checkStacksHaveErrors()
def _checkStacksHaveErrors(self):
for node in DepthFirstIterator(self._scene.getRoot()):
# valdiate only objects which can be selected because the settings per object
# can be applied only for them
if not node.isSelectable():
continue
hasErrors = self._checkStackForErrors(node.callDecoration("getStack"))
Application.getInstance().getObjectsModel().setStacksHaveErrors(hasErrors)
#If any of models has an error then no reason check next objects on the build plate
if hasErrors:
break
def _checkStackForErrors(self, stack):
print("checking for errors")
if stack is None:
return False
for key in stack.getAllKeys():
validation_state = stack.getProperty(key, "validationState")
if validation_state in (ValidatorState.Exception, ValidatorState.MaximumError, ValidatorState.MinimumError):
Logger.log("w", "Setting Per Object %s is not valid.", key)
return True
return False
def subscribeForSettingValidation(self, setting_name):
selected_object = Selection.getSelectedObject(0)
stack = selected_object.callDecoration("getStack") # Don't try to get the active extruder since it may be None anyway.
if not stack:
return ""
settings = stack.getTop()
setting_instance = settings.getInstance(setting_name)
if setting_instance:
setting_instance.propertyChanged.connect(self._onPropertyChanged)
def unsubscribeForSettingValidation(self, setting_name):
selected_object = Selection.getSelectedObject(0)
stack = selected_object.callDecoration("getStack") # Don't try to get the active extruder since it may be None anyway.
if not stack:
return ""
settings = stack.getTop()
setting_instance = settings.getInstance(setting_name)
if setting_instance:
setting_instance.propertyChanged.disconnect(self._onPropertyChanged)

View File

@ -194,6 +194,7 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
# the "reply" should be disconnected
if self._latest_reply_handler:
self._latest_reply_handler.disconnect()
self._latest_reply_handler = None
@pyqtSlot()

View File

@ -1,4 +1,4 @@
# Copyright (c) 2015 Ultimaker B.V.
# Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
import os.path
@ -10,7 +10,7 @@ from UM.View.RenderPass import RenderPass
from UM.View.RenderBatch import RenderBatch
from UM.View.GL.OpenGL import OpenGL
from UM.Scene.SceneNode import SceneNode
from cura.Scene.CuraSceneNode import CuraSceneNode
from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
class XRayPass(RenderPass):
@ -27,7 +27,7 @@ class XRayPass(RenderPass):
batch = RenderBatch(self._shader, type = RenderBatch.RenderType.NoType, backface_cull = False, blend_mode = RenderBatch.BlendMode.Additive)
for node in DepthFirstIterator(self._scene.getRoot()):
if type(node) is SceneNode and node.getMeshData() and node.isVisible():
if isinstance(node, CuraSceneNode) and node.getMeshData() and node.isVisible():
batch.addItem(node.getWorldTransformation(), node.getMeshData())
self.bind()

View File

@ -13,9 +13,9 @@ vertex =
}
fragment =
uniform sampler2D u_layer0;
uniform sampler2D u_layer1;
uniform sampler2D u_layer2;
uniform sampler2D u_layer0; //Default pass.
uniform sampler2D u_layer1; //Selection pass.
uniform sampler2D u_layer2; //X-ray pass.
uniform vec2 u_offset[9];
@ -83,9 +83,9 @@ vertex41core =
fragment41core =
#version 410
uniform sampler2D u_layer0;
uniform sampler2D u_layer1;
uniform sampler2D u_layer2;
uniform sampler2D u_layer0; //Default pass.
uniform sampler2D u_layer1; //Selection pass.
uniform sampler2D u_layer2; //X-ray pass.
uniform vec2 u_offset[9];

View File

@ -52,6 +52,7 @@ Menu
{
text: model.name
checkable: model.available
enabled: model.available
checked: Cura.MachineManager.activeQualityOrQualityChangesName == model.name
exclusiveGroup: group
onTriggered: Cura.MachineManager.setQualityChangesGroup(model.quality_changes_group)

View File

@ -47,6 +47,19 @@ TabView
return Math.round(diameter);
}
// This trick makes sure to make all fields lose focus so their onEditingFinished will be triggered
// and modified values will be saved. This can happen when a user changes a value and then closes the
// dialog directly.
//
// Please note that somehow this callback is ONLY triggered when visible is false.
onVisibleChanged:
{
if (!visible)
{
base.focus = false;
}
}
Tab
{
title: catalog.i18nc("@title", "Information")

View File

@ -52,6 +52,24 @@ Item
return base.currentItem.root_material_id == root_material_id;
}
Component.onCompleted:
{
// Select the activated material when this page shows up
const extruder_position = Cura.ExtruderManager.activeExtruderIndex;
const active_root_material_id = Cura.MachineManager.currentRootMaterialId[extruder_position];
var itemIndex = -1;
for (var i = 0; i < materialsModel.rowCount(); ++i)
{
var item = materialsModel.getItem(i);
if (item.root_material_id == active_root_material_id)
{
itemIndex = i;
break;
}
}
materialListView.currentIndex = itemIndex;
}
Row // Button Row
{
id: buttonRow