Convert doxygen to rst for POS, MonitorStage, PostProcessing

This commit is contained in:
Nino van Hooff 2020-05-08 16:28:07 +02:00
parent 553b09b6cf
commit a4fe3d7685
9 changed files with 180 additions and 141 deletions

View file

@ -5,8 +5,8 @@ from UM.Application import Application
from cura.Stages.CuraStage import CuraStage from cura.Stages.CuraStage import CuraStage
## Stage for monitoring a 3D printing while it's printing.
class MonitorStage(CuraStage): class MonitorStage(CuraStage):
"""Stage for monitoring a 3D printing while it's printing."""
def __init__(self, parent = None): def __init__(self, parent = None):
super().__init__(parent) super().__init__(parent)

View file

@ -15,9 +15,11 @@ from cura.Settings.ExtruderManager import ExtruderManager #To get global-inherit
from cura.Settings.SettingOverrideDecorator import SettingOverrideDecorator from cura.Settings.SettingOverrideDecorator import SettingOverrideDecorator
## The per object setting visibility handler ensures that only setting
# definitions that have a matching instance Container are returned as visible.
class PerObjectSettingVisibilityHandler(UM.Settings.Models.SettingVisibilityHandler.SettingVisibilityHandler): class PerObjectSettingVisibilityHandler(UM.Settings.Models.SettingVisibilityHandler.SettingVisibilityHandler):
"""The per object setting visibility handler ensures that only setting
definitions that have a matching instance Container are returned as visible.
"""
def __init__(self, parent = None, *args, **kwargs): def __init__(self, parent = None, *args, **kwargs):
super().__init__(parent = parent, *args, **kwargs) super().__init__(parent = parent, *args, **kwargs)

View file

@ -12,9 +12,11 @@ from UM.Settings.SettingInstance import SettingInstance
from UM.Event import Event from UM.Event import Event
## 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.
class PerObjectSettingsTool(Tool): class PerObjectSettingsTool(Tool):
"""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.
"""
def __init__(self): def __init__(self):
super().__init__() super().__init__()
self._model = None self._model = None
@ -48,26 +50,31 @@ class PerObjectSettingsTool(Tool):
except AttributeError: except AttributeError:
return "" return ""
## Gets the active extruder of the currently selected object.
#
# \return The active extruder of the currently selected object.
def getSelectedActiveExtruder(self): def getSelectedActiveExtruder(self):
"""Gets the active extruder of the currently selected object.
:return: The active extruder of the currently selected object.
"""
selected_object = Selection.getSelectedObject(0) selected_object = Selection.getSelectedObject(0)
return selected_object.callDecoration("getActiveExtruder") return selected_object.callDecoration("getActiveExtruder")
## Changes the active extruder of the currently selected object.
#
# \param extruder_stack_id The ID of the extruder to print the currently
# selected object with.
def setSelectedActiveExtruder(self, extruder_stack_id): def setSelectedActiveExtruder(self, extruder_stack_id):
"""Changes the active extruder of the currently selected object.
:param extruder_stack_id: The ID of the extruder to print the currently
selected object with.
"""
selected_object = Selection.getSelectedObject(0) selected_object = Selection.getSelectedObject(0)
stack = selected_object.callDecoration("getStack") #Don't try to get the active extruder since it may be None anyway. stack = selected_object.callDecoration("getStack") #Don't try to get the active extruder since it may be None anyway.
if not stack: if not stack:
selected_object.addDecorator(SettingOverrideDecorator()) selected_object.addDecorator(SettingOverrideDecorator())
selected_object.callDecoration("setActiveExtruder", extruder_stack_id) selected_object.callDecoration("setActiveExtruder", extruder_stack_id)
## Returns True when the mesh_type was changed, False when current mesh_type == mesh_type
def setMeshType(self, mesh_type: str) -> bool: def setMeshType(self, mesh_type: str) -> bool:
"""Returns True when the mesh_type was changed, False when current mesh_type == mesh_type"""
old_mesh_type = self.getMeshType() old_mesh_type = self.getMeshType()
if old_mesh_type == mesh_type: if old_mesh_type == mesh_type:
return False return False

View file

@ -27,9 +27,8 @@ if TYPE_CHECKING:
from .Script import Script from .Script import Script
## The post processing plugin is an Extension type plugin that enables pre-written scripts to post process generated
# g-code files.
class PostProcessingPlugin(QObject, Extension): class PostProcessingPlugin(QObject, Extension):
"""Extension type plugin that enables pre-written scripts to post process g-code files."""
def __init__(self, parent = None) -> None: def __init__(self, parent = None) -> None:
QObject.__init__(self, parent) QObject.__init__(self, parent)
Extension.__init__(self) Extension.__init__(self)
@ -69,8 +68,9 @@ class PostProcessingPlugin(QObject, Extension):
except IndexError: except IndexError:
return "" return ""
## Execute all post-processing scripts on the gcode.
def execute(self, output_device) -> None: def execute(self, output_device) -> None:
"""Execute all post-processing scripts on the gcode."""
scene = Application.getInstance().getController().getScene() scene = Application.getInstance().getController().getScene()
# If the scene does not have a gcode, do nothing # If the scene does not have a gcode, do nothing
if not hasattr(scene, "gcode_dict"): if not hasattr(scene, "gcode_dict"):
@ -119,9 +119,10 @@ class PostProcessingPlugin(QObject, Extension):
self.selectedIndexChanged.emit() #Ensure that settings are updated self.selectedIndexChanged.emit() #Ensure that settings are updated
self._propertyChanged() self._propertyChanged()
## Remove a script from the active script list by index.
@pyqtSlot(int) @pyqtSlot(int)
def removeScriptByIndex(self, index: int) -> None: def removeScriptByIndex(self, index: int) -> None:
"""Remove a script from the active script list by index."""
self._script_list.pop(index) self._script_list.pop(index)
if len(self._script_list) - 1 < self._selected_script_index: if len(self._script_list) - 1 < self._selected_script_index:
self._selected_script_index = len(self._script_list) - 1 self._selected_script_index = len(self._script_list) - 1
@ -129,10 +130,12 @@ class PostProcessingPlugin(QObject, Extension):
self.selectedIndexChanged.emit() # Ensure that settings are updated self.selectedIndexChanged.emit() # Ensure that settings are updated
self._propertyChanged() self._propertyChanged()
## Load all scripts from all paths where scripts can be found.
#
# This should probably only be done on init.
def loadAllScripts(self) -> None: def loadAllScripts(self) -> None:
"""Load all scripts from all paths where scripts can be found.
This should probably only be done on init.
"""
if self._loaded_scripts: # Already loaded. if self._loaded_scripts: # Already loaded.
return return
@ -152,10 +155,12 @@ class PostProcessingPlugin(QObject, Extension):
self.loadScripts(path) self.loadScripts(path)
## Load all scripts from provided path.
# This should probably only be done on init.
# \param path Path to check for scripts.
def loadScripts(self, path: str) -> None: def loadScripts(self, path: str) -> None:
"""Load all scripts from provided path.
This should probably only be done on init.
:param path: Path to check for scripts.
"""
if ApplicationMetadata.IsEnterpriseVersion: if ApplicationMetadata.IsEnterpriseVersion:
# Delete all __pycache__ not in installation folder, as it may present a security risk. # Delete all __pycache__ not in installation folder, as it may present a security risk.
@ -173,8 +178,8 @@ class PostProcessingPlugin(QObject, Extension):
if not is_in_installation_path: if not is_in_installation_path:
TrustBasics.removeCached(path) TrustBasics.removeCached(path)
## Load all scripts in the scripts folders
scripts = pkgutil.iter_modules(path = [path]) scripts = pkgutil.iter_modules(path = [path])
"""Load all scripts in the scripts folders"""
for loader, script_name, ispkg in scripts: for loader, script_name, ispkg in scripts:
# Iterate over all scripts. # Iterate over all scripts.
if script_name not in sys.modules: if script_name not in sys.modules:
@ -278,9 +283,8 @@ class PostProcessingPlugin(QObject, Extension):
self.scriptListChanged.emit() self.scriptListChanged.emit()
self._propertyChanged() self._propertyChanged()
## When the global container stack is changed, swap out the list of active
# scripts.
def _onGlobalContainerStackChanged(self) -> None: def _onGlobalContainerStackChanged(self) -> None:
"""When the global container stack is changed, swap out the list of active scripts."""
if self._global_container_stack: if self._global_container_stack:
self._global_container_stack.metaDataChanged.disconnect(self._restoreScriptInforFromMetadata) self._global_container_stack.metaDataChanged.disconnect(self._restoreScriptInforFromMetadata)
@ -323,8 +327,12 @@ class PostProcessingPlugin(QObject, Extension):
# We do want to listen to other events. # We do want to listen to other events.
self._global_container_stack.metaDataChanged.connect(self._restoreScriptInforFromMetadata) self._global_container_stack.metaDataChanged.connect(self._restoreScriptInforFromMetadata)
## Creates the view used by show popup. The view is saved because of the fairly aggressive garbage collection.
def _createView(self) -> None: def _createView(self) -> None:
"""Creates the view used by show popup.
The view is saved because of the fairly aggressive garbage collection.
"""
Logger.log("d", "Creating post processing plugin view.") Logger.log("d", "Creating post processing plugin view.")
self.loadAllScripts() self.loadAllScripts()
@ -340,8 +348,9 @@ class PostProcessingPlugin(QObject, Extension):
# Create the save button component # Create the save button component
CuraApplication.getInstance().addAdditionalComponent("saveButton", self._view.findChild(QObject, "postProcessingSaveAreaButton")) CuraApplication.getInstance().addAdditionalComponent("saveButton", self._view.findChild(QObject, "postProcessingSaveAreaButton"))
## Show the (GUI) popup of the post processing plugin.
def showPopup(self) -> None: def showPopup(self) -> None:
"""Show the (GUI) popup of the post processing plugin."""
if self._view is None: if self._view is None:
self._createView() self._createView()
if self._view is None: if self._view is None:
@ -349,11 +358,13 @@ class PostProcessingPlugin(QObject, Extension):
return return
self._view.show() self._view.show()
## Property changed: trigger re-slice
# To do this we use the global container stack propertyChanged.
# Re-slicing is necessary for setting changes in this plugin, because the changes
# are applied only once per "fresh" gcode
def _propertyChanged(self) -> None: def _propertyChanged(self) -> None:
"""Property changed: trigger re-slice
To do this we use the global container stack propertyChanged.
Re-slicing is necessary for setting changes in this plugin, because the changes
are applied only once per "fresh" gcode
"""
global_container_stack = Application.getInstance().getGlobalContainerStack() global_container_stack = Application.getInstance().getGlobalContainerStack()
if global_container_stack is not None: if global_container_stack is not None:
global_container_stack.propertyChanged.emit("post_processing_plugin", "value") global_container_stack.propertyChanged.emit("post_processing_plugin", "value")

View file

@ -23,9 +23,10 @@ if TYPE_CHECKING:
from UM.Settings.Interfaces import DefinitionContainerInterface from UM.Settings.Interfaces import DefinitionContainerInterface
## Base class for scripts. All scripts should inherit the script class.
@signalemitter @signalemitter
class Script: class Script:
"""Base class for scripts. All scripts should inherit the script class."""
def __init__(self) -> None: def __init__(self) -> None:
super().__init__() super().__init__()
self._stack = None # type: Optional[ContainerStack] self._stack = None # type: Optional[ContainerStack]
@ -78,13 +79,15 @@ class Script:
if global_container_stack is not None: if global_container_stack is not None:
global_container_stack.propertyChanged.emit(key, property_name) global_container_stack.propertyChanged.emit(key, property_name)
## Needs to return a dict that can be used to construct a settingcategory file.
# See the example script for an example.
# It follows the same style / guides as the Uranium settings.
# Scripts can either override getSettingData directly, or use getSettingDataString
# to return a string that will be parsed as json. The latter has the benefit over
# returning a dict in that the order of settings is maintained.
def getSettingData(self) -> Dict[str, Any]: def getSettingData(self) -> Dict[str, Any]:
"""Needs to return a dict that can be used to construct a settingcategory file.
See the example script for an example.
It follows the same style / guides as the Uranium settings.
Scripts can either override getSettingData directly, or use getSettingDataString
to return a string that will be parsed as json. The latter has the benefit over
returning a dict in that the order of settings is maintained.
"""
setting_data_as_string = self.getSettingDataString() setting_data_as_string = self.getSettingDataString()
setting_data = json.loads(setting_data_as_string, object_pairs_hook = collections.OrderedDict) setting_data = json.loads(setting_data_as_string, object_pairs_hook = collections.OrderedDict)
return setting_data return setting_data
@ -104,15 +107,18 @@ class Script:
return self._stack.getId() return self._stack.getId()
return None return None
## Convenience function that retrieves value of a setting from the stack.
def getSettingValueByKey(self, key: str) -> Any: def getSettingValueByKey(self, key: str) -> Any:
"""Convenience function that retrieves value of a setting from the stack."""
if self._stack is not None: if self._stack is not None:
return self._stack.getProperty(key, "value") return self._stack.getProperty(key, "value")
return None return None
## Convenience function that finds the value in a line of g-code.
# When requesting key = x from line "G1 X100" the value 100 is returned.
def getValue(self, line: str, key: str, default = None) -> Any: def getValue(self, line: str, key: str, default = None) -> Any:
"""Convenience function that finds the value in a line of g-code.
When requesting key = x from line "G1 X100" the value 100 is returned.
"""
if not key in line or (';' in line and line.find(key) > line.find(';')): if not key in line or (';' in line and line.find(key) > line.find(';')):
return default return default
sub_part = line[line.find(key) + 1:] sub_part = line[line.find(key) + 1:]
@ -127,20 +133,23 @@ class Script:
except ValueError: #Not a number at all. except ValueError: #Not a number at all.
return default return default
## Convenience function to produce a line of g-code.
#
# You can put in an original g-code line and it'll re-use all the values
# in that line.
# All other keyword parameters are put in the result in g-code's format.
# For instance, if you put ``G=1`` in the parameters, it will output
# ``G1``. If you put ``G=1, X=100`` in the parameters, it will output
# ``G1 X100``. The parameters G and M will always be put first. The
# parameters T and S will be put second (or first if there is no G or M).
# The rest of the parameters will be put in arbitrary order.
# \param line The original g-code line that must be modified. If not
# provided, an entirely new g-code line will be produced.
# \return A line of g-code with the desired parameters filled in.
def putValue(self, line: str = "", **kwargs) -> str: def putValue(self, line: str = "", **kwargs) -> str:
"""Convenience function to produce a line of g-code.
You can put in an original g-code line and it'll re-use all the values
in that line.
All other keyword parameters are put in the result in g-code's format.
For instance, if you put ``G=1`` in the parameters, it will output
``G1``. If you put ``G=1, X=100`` in the parameters, it will output
``G1 X100``. The parameters G and M will always be put first. The
parameters T and S will be put second (or first if there is no G or M).
The rest of the parameters will be put in arbitrary order.
:param line: The original g-code line that must be modified. If not
provided, an entirely new g-code line will be produced.
:return: A line of g-code with the desired parameters filled in.
"""
#Strip the comment. #Strip the comment.
comment = "" comment = ""
if ";" in line: if ";" in line:
@ -179,7 +188,9 @@ class Script:
return result return result
## This is called when the script is executed.
# It gets a list of g-code strings and needs to return a (modified) list.
def execute(self, data: List[str]) -> List[str]: def execute(self, data: List[str]) -> List[str]:
"""This is called when the script is executed.
It gets a list of g-code strings and needs to return a (modified) list.
"""
raise NotImplementedError() raise NotImplementedError()

View file

@ -63,10 +63,12 @@ class FilamentChange(Script):
} }
}""" }"""
## Inserts the filament change g-code at specific layer numbers.
# \param data A list of layers of g-code.
# \return A similar list, with filament change commands inserted.
def execute(self, data: List[str]): def execute(self, data: List[str]):
"""Inserts the filament change g-code at specific layer numbers.
:param data: A list of layers of g-code.
:return: A similar list, with filament change commands inserted.
"""
layer_nums = self.getSettingValueByKey("layer_number") layer_nums = self.getSettingValueByKey("layer_number")
initial_retract = self.getSettingValueByKey("initial_retract") initial_retract = self.getSettingValueByKey("initial_retract")
later_retract = self.getSettingValueByKey("later_retract") later_retract = self.getSettingValueByKey("later_retract")

View file

@ -134,9 +134,8 @@ class PauseAtHeight(Script):
} }
}""" }"""
## Get the X and Y values for a layer (will be used to get X and Y of the
# layer after the pause).
def getNextXY(self, layer: str) -> Tuple[float, float]: def getNextXY(self, layer: str) -> Tuple[float, float]:
"""Get the X and Y values for a layer (will be used to get X and Y of the layer after the pause)."""
lines = layer.split("\n") lines = layer.split("\n")
for line in lines: for line in lines:
if self.getValue(line, "X") is not None and self.getValue(line, "Y") is not None: if self.getValue(line, "X") is not None and self.getValue(line, "Y") is not None:
@ -145,10 +144,12 @@ class PauseAtHeight(Script):
return x, y return x, y
return 0, 0 return 0, 0
## Inserts the pause commands.
# \param data: List of layers.
# \return New list of layers.
def execute(self, data: List[str]) -> List[str]: def execute(self, data: List[str]) -> List[str]:
"""Inserts the pause commands.
:param data: List of layers.
:return: New list of layers.
"""
pause_at = self.getSettingValueByKey("pause_at") pause_at = self.getSettingValueByKey("pause_at")
pause_height = self.getSettingValueByKey("pause_height") pause_height = self.getSettingValueByKey("pause_height")
pause_layer = self.getSettingValueByKey("pause_layer") pause_layer = self.getSettingValueByKey("pause_layer")

View file

@ -5,8 +5,10 @@ import math
from ..Script import Script from ..Script import Script
## Continues retracting during all travel moves.
class RetractContinue(Script): class RetractContinue(Script):
"""Continues retracting during all travel moves."""
def getSettingDataString(self): def getSettingDataString(self):
return """{ return """{
"name": "Retract Continue", "name": "Retract Continue",

View file

@ -5,11 +5,14 @@ import re #To perform the search and replace.
from ..Script import Script from ..Script import Script
## Performs a search-and-replace on all g-code.
#
# Due to technical limitations, the search can't cross the border between
# layers.
class SearchAndReplace(Script): class SearchAndReplace(Script):
"""Performs a search-and-replace on all g-code.
Due to technical limitations, the search can't cross the border between
layers.
"""
def getSettingDataString(self): def getSettingDataString(self):
return """{ return """{
"name": "Search and Replace", "name": "Search and Replace",