Merge branch 'master' into feature_accelerations_and_jerk_per_feature_settigns_rework

This commit is contained in:
Tim Kuipers 2016-05-30 14:56:44 +02:00
commit 2932558a90
3 changed files with 97 additions and 72 deletions

View File

@ -1,13 +1,12 @@
# Copyright (c) 2015 Ultimaker B.V. # Copyright (c) 2015 Ultimaker B.V.
# Cura is released under the terms of the AGPLv3 or higher. # Cura is released under the terms of the AGPLv3 or higher.
from UM.Backend.Backend import Backend from UM.Backend.Backend import Backend, BackendState
from UM.Application import Application from UM.Application import Application
from UM.Scene.SceneNode import SceneNode from UM.Scene.SceneNode import SceneNode
from UM.Preferences import Preferences from UM.Preferences import Preferences
from UM.Signal import Signal from UM.Signal import Signal
from UM.Logger import Logger from UM.Logger import Logger
from UM.Qt.Bindings.BackendProxy import BackendState #To determine the state of the slicing job.
from UM.Message import Message from UM.Message import Message
from UM.PluginRegistry import PluginRegistry from UM.PluginRegistry import PluginRegistry
from UM.Resources import Resources from UM.Resources import Resources
@ -56,8 +55,9 @@ class CuraEngineBackend(Backend):
self._stored_layer_data = [] self._stored_layer_data = []
#Triggers for when to (re)start slicing: #Triggers for when to (re)start slicing:
if Application.getInstance().getGlobalContainerStack(): self._global_container_stack = None
Application.getInstance().getGlobalContainerStack().propertyChanged.connect(self._onSettingChanged) #Note: Only starts slicing when the value changed. Application.getInstance().globalContainerStackChanged.connect(self._onGlobalStackChanged)
self._onGlobalStackChanged()
#When you update a setting and other settings get changed through inheritance, many propertyChanged signals are fired. #When you update a setting and other settings get changed through inheritance, many propertyChanged signals are fired.
#This timer will group them up, and only slice for the last setting changed signal. #This timer will group them up, and only slice for the last setting changed signal.
@ -82,7 +82,7 @@ class CuraEngineBackend(Backend):
self._always_restart = True #Always restart the engine when starting a new slice. Don't keep the process running. TODO: Fix engine statelessness. self._always_restart = True #Always restart the engine when starting a new slice. Don't keep the process running. TODO: Fix engine statelessness.
self._process_layers_job = None #The currently active job to process layers, or None if it is not processing layers. self._process_layers_job = None #The currently active job to process layers, or None if it is not processing layers.
self._message = None #Pop-up message that shows the slicing progress bar (or an error message). self._error_message = None #Pop-up message that shows errors.
self.backendQuit.connect(self._onBackendQuit) self.backendQuit.connect(self._onBackendQuit)
self.backendConnected.connect(self._onBackendConnected) self.backendConnected.connect(self._onBackendConnected)
@ -124,7 +124,7 @@ class CuraEngineBackend(Backend):
def slice(self): def slice(self):
self._stored_layer_data = [] self._stored_layer_data = []
if not self._enabled: #We shouldn't be slicing. if not self._enabled or not self._global_container_stack: #We shouldn't be slicing.
return return
if self._slicing: #We were already slicing. Stop the old job. if self._slicing: #We were already slicing. Stop the old job.
@ -134,34 +134,11 @@ class CuraEngineBackend(Backend):
self._process_layers_job.abort() self._process_layers_job.abort()
self._process_layers_job = None self._process_layers_job = None
# #Don't slice if there is a setting with an error value. if self._error_message:
# stack = Application.getInstance().getGlobalContainerStack() self._error_message.hide()
# for key in stack.getAllKeys():
# validation_state = stack.getProperty(key, "validationState")
# #Only setting instances have a validation state, so settings which
# #are not overwritten by any instance will have none. The property
# #then, and only then, evaluates to None. We make the assumption that
# #the definition defines the setting with a default value that is
# #valid. Therefore we can allow both ValidatorState.Valid and None as
# #allowable validation states.
# #TODO: This assumption is wrong! If the definition defines an inheritance function that through inheritance evaluates to a disallowed value, a setting is still invalid even though it's default!
# #TODO: Therefore we must also validate setting definitions.
# if validation_state != None and validation_state != ValidatorState.Valid:
# Logger.log("w", "Setting %s is not valid, but %s. Aborting slicing.", key, validation_state)
# if self._message: #Hide any old message before creating a new one.
# self._message.hide()
# self._message = None
# self._message = Message(catalog.i18nc("@info:status", "Unable to slice. Please check your setting values for errors."))
# self._message.show()
# return
self.processingProgress.emit(0.0) self.processingProgress.emit(0.0)
self.backendStateChange.emit(BackendState.NOT_STARTED) self.backendStateChange.emit(BackendState.NotStarted)
if self._message:
self._message.setProgress(-1)
else:
self._message = Message(catalog.i18nc("@info:status", "Slicing..."), 0, False, -1)
self._message.show()
self._scene.gcode_list = [] self._scene.gcode_list = []
self._slicing = True self._slicing = True
@ -193,10 +170,6 @@ class CuraEngineBackend(Backend):
except Exception as e: # terminating a process that is already terminating causes an exception, silently ignore this. except Exception as e: # terminating a process that is already terminating causes an exception, silently ignore this.
Logger.log("d", "Exception occurred while trying to kill the engine %s", str(e)) Logger.log("d", "Exception occurred while trying to kill the engine %s", str(e))
if self._message:
self._message.hide()
self._message = None
## Event handler to call when the job to initiate the slicing process is ## Event handler to call when the job to initiate the slicing process is
# completed. # completed.
# #
@ -209,15 +182,31 @@ class CuraEngineBackend(Backend):
# Note that cancelled slice jobs can still call this method. # Note that cancelled slice jobs can still call this method.
if self._start_slice_job is job: if self._start_slice_job is job:
self._start_slice_job = None self._start_slice_job = None
if job.isCancelled() or job.getError() or job.getResult() != True:
if self._message: if job.isCancelled() or job.getError() or job.getResult() == StartSliceJob.StartJobResult.Error:
self._message.hide()
self._message = None
return return
else:
# Preparation completed, send it to the backend. if job.getResult() == StartSliceJob.StartJobResult.SettingError:
self._socket.sendMessage(job.getSettingsMessage()) if Application.getInstance().getPlatformActivity:
self._socket.sendMessage(job.getSliceMessage()) self._error_message = Message(catalog.i18nc("@info:status", "Unable to slice. Please check your setting values for errors."), lifetime = 10)
self._error_message.show()
self.backendStateChange.emit(BackendState.Error)
else:
self.backendStateChange.emit(BackendState.NotStarted)
return
if job.getResult() == StartSliceJob.StartJobResult.NothingToSlice:
if Application.getInstance().getPlatformActivity:
self._error_message = Message(catalog.i18nc("@info:status", "Unable to slice. No suitable objects found."), lifetime = 10)
self._error_message.show()
self.backendStateChange.emit(BackendState.Error)
else:
self.backendStateChange.emit(BackendState.NotStarted)
return
# Preparation completed, send it to the backend.
self._socket.sendMessage(job.getSettingsMessage())
self._socket.sendMessage(job.getSliceMessage())
## Listener for when the scene has changed. ## Listener for when the scene has changed.
# #
@ -270,26 +259,18 @@ class CuraEngineBackend(Backend):
# #
# \param message The protobuf message containing the slicing progress. # \param message The protobuf message containing the slicing progress.
def _onProgressMessage(self, message): def _onProgressMessage(self, message):
if self._message:
self._message.setProgress(round(message.amount * 100))
self.processingProgress.emit(message.amount) self.processingProgress.emit(message.amount)
self.backendStateChange.emit(BackendState.PROCESSING) self.backendStateChange.emit(BackendState.Processing)
## Called when the engine sends a message that slicing is finished. ## Called when the engine sends a message that slicing is finished.
# #
# \param message The protobuf message signalling that slicing is finished. # \param message The protobuf message signalling that slicing is finished.
def _onSlicingFinishedMessage(self, message): def _onSlicingFinishedMessage(self, message):
self.backendStateChange.emit(BackendState.DONE) self.backendStateChange.emit(BackendState.Done)
self.processingProgress.emit(1.0) self.processingProgress.emit(1.0)
self._slicing = False self._slicing = False
if self._message:
self._message.setProgress(100)
self._message.hide()
self._message = None
if self._layer_view_active and (self._process_layers_job is None or not self._process_layers_job.isRunning()): if self._layer_view_active and (self._process_layers_job is None or not self._process_layers_job.isRunning()):
self._process_layers_job = ProcessSlicedLayersJob.ProcessSlicedLayersJob(self._stored_layer_data) self._process_layers_job = ProcessSlicedLayersJob.ProcessSlicedLayersJob(self._stored_layer_data)
self._process_layers_job.start() self._process_layers_job.start()
@ -326,7 +307,7 @@ class CuraEngineBackend(Backend):
## Called when anything has changed to the stuff that needs to be sliced. ## Called when anything has changed to the stuff that needs to be sliced.
# #
# This indicates that we should probably re-slice soon. # This indicates that we should probably re-slice soon.
def _onChanged(self): def _onChanged(self, *args, **kwargs):
self._change_timer.start() self._change_timer.start()
## Called when the back-end connects to the front-end. ## Called when the back-end connects to the front-end.
@ -376,3 +357,16 @@ class CuraEngineBackend(Backend):
Logger.log("d", "Backend quit with return code %s. Resetting process and socket.", self._process.wait()) Logger.log("d", "Backend quit with return code %s. Resetting process and socket.", self._process.wait())
self._process = None self._process = None
self._createSocket() self._createSocket()
## Called when the global container stack changes
def _onGlobalStackChanged(self):
if self._global_container_stack:
self._global_container_stack.propertyChanged.disconnect(self._onSettingChanged)
self._global_container_stack.containersChanged.disconnect(self._onChanged)
self._global_container_stack = Application.getInstance().getGlobalContainerStack()
if self._global_container_stack:
self._global_container_stack.propertyChanged.connect(self._onSettingChanged) #Note: Only starts slicing when the value changed.
self._global_container_stack.containersChanged.connect(self._onChanged)
self._onChanged()

View File

@ -4,6 +4,7 @@
import numpy import numpy
from string import Formatter from string import Formatter
import traceback import traceback
from enum import IntEnum
from UM.Job import Job from UM.Job import Job
from UM.Application import Application from UM.Application import Application
@ -12,8 +13,15 @@ from UM.Logger import Logger
from UM.Scene.SceneNode import SceneNode from UM.Scene.SceneNode import SceneNode
from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
from UM.Settings.Validator import ValidatorState
from cura.OneAtATimeIterator import OneAtATimeIterator from cura.OneAtATimeIterator import OneAtATimeIterator
class StartJobResult(IntEnum):
Finished = 1
Error = 2
SettingError = 3
NothingToSlice = 4
## Formatter class that handles token expansion in start/end gcod ## Formatter class that handles token expansion in start/end gcod
class GcodeStartEndFormatter(Formatter): class GcodeStartEndFormatter(Formatter):
@ -48,9 +56,19 @@ class StartSliceJob(Job):
def run(self): def run(self):
stack = Application.getInstance().getGlobalContainerStack() stack = Application.getInstance().getGlobalContainerStack()
if not stack: if not stack:
self.setResult(False) self.setResult(StartJobResult.Error)
return return
#Don't slice if there is a setting with an error value.
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 %s is not valid, but %s. Aborting slicing.", key, validation_state)
self.setResult(StartJobResult.SettingError)
return
Job.yieldThread()
with self._scene.getSceneLock(): with self._scene.getSceneLock():
# Remove old layer data. # Remove old layer data.
for node in DepthFirstIterator(self._scene.getRoot()): for node in DepthFirstIterator(self._scene.getRoot()):
@ -91,6 +109,7 @@ class StartSliceJob(Job):
object_groups.append(temp_list) object_groups.append(temp_list)
if not object_groups: if not object_groups:
self.setResult(StartJobResult.NothingToSlice)
return return
self._buildGlobalSettingsMessage(stack) self._buildGlobalSettingsMessage(stack)
@ -116,7 +135,7 @@ class StartSliceJob(Job):
Job.yieldThread() Job.yieldThread()
self.setResult(True) self.setResult(StartJobResult.Finished)
def cancel(self): def cancel(self):
super().cancel() super().cancel()
@ -131,7 +150,7 @@ class StartSliceJob(Job):
fmt = GcodeStartEndFormatter() fmt = GcodeStartEndFormatter()
return str(fmt.format(value, **settings)).encode("utf-8") return str(fmt.format(value, **settings)).encode("utf-8")
except: except:
Logger.log("w", "Unabled to do token replacement on start/end gcode %s", traceback.format_exc()) Logger.logException("w", "Unable to do token replacement on start/end gcode")
return str(value).encode("utf-8") return str(value).encode("utf-8")
## Sends all global settings to the engine. ## Sends all global settings to the engine.

View File

@ -14,21 +14,33 @@ Rectangle {
property real progress: UM.Backend.progress; property real progress: UM.Backend.progress;
property int backendState: UM.Backend.state; property int backendState: UM.Backend.state;
property bool activity: Printer.getPlatformActivity; property bool activity: Printer.getPlatformActivity;
//Behavior on progress { NumberAnimation { duration: 250; } } //Behavior on progress { NumberAnimation { duration: 250; } }
property int totalHeight: childrenRect.height + UM.Theme.getSize("default_margin").height property int totalHeight: childrenRect.height + UM.Theme.getSize("default_margin").height
property string fileBaseName property string fileBaseName
property string statusText: { property string statusText:
if(base.backendState == 0) { {
if(!activity) { if(!activity)
return catalog.i18nc("@label:PrintjobStatus","Please load a 3d model"); {
} else { return catalog.i18nc("@label:PrintjobStatus", "Please load a 3d model");
return catalog.i18nc("@label:PrintjobStatus","Preparing to slice..."); }
}
} else if(base.backendState == 1) { if(base.backendState == 1)
return catalog.i18nc("@label:PrintjobStatus","Slicing..."); {
} else { return catalog.i18nc("@label:PrintjobStatus", "Preparing to slice...");
return catalog.i18nc("@label:PrintjobStatus","Ready to ") + UM.OutputDeviceManager.activeDeviceShortDescription; }
else if(base.backendState == 2)
{
return catalog.i18nc("@label:PrintjobStatus", "Slicing...");
}
else if(base.backendState == 3)
{
return catalog.i18nc("@label:PrintjobStatus %1 is target operation","Ready to %1").arg(UM.OutputDeviceManager.activeDeviceShortDescription);
}
else if(base.backendState == 4)
{
return catalog.i18nc("@label:PrintjobStatus", "Unable to Slice")
} }
} }
@ -60,7 +72,7 @@ Rectangle {
height: parent.height height: parent.height
color: UM.Theme.getColor("progressbar_control") color: UM.Theme.getColor("progressbar_control")
radius: UM.Theme.getSize("progressbar_radius").width radius: UM.Theme.getSize("progressbar_radius").width
visible: base.backendState == 1 ? true : false visible: base.backendState == 2 ? true : false
} }
} }
@ -76,7 +88,7 @@ Rectangle {
id: saveToButton id: saveToButton
tooltip: UM.OutputDeviceManager.activeDeviceDescription; tooltip: UM.OutputDeviceManager.activeDeviceDescription;
enabled: base.backendState == 2 && base.activity == true enabled: base.backendState == 3 && base.activity == true
height: UM.Theme.getSize("save_button_save_to_button").height height: UM.Theme.getSize("save_button_save_to_button").height
anchors.top: parent.top anchors.top: parent.top
@ -127,7 +139,7 @@ Rectangle {
anchors.rightMargin: UM.Theme.getSize("default_margin").width anchors.rightMargin: UM.Theme.getSize("default_margin").width
width: UM.Theme.getSize("save_button_save_to_button").height width: UM.Theme.getSize("save_button_save_to_button").height
height: UM.Theme.getSize("save_button_save_to_button").height height: UM.Theme.getSize("save_button_save_to_button").height
enabled: base.backendState == 2 && base.activity == true enabled: base.backendState == 3 && base.activity == true
visible: devicesModel.deviceCount > 1 visible: devicesModel.deviceCount > 1