diff --git a/plugins/CuraProfileReader/CuraProfileReader.py b/plugins/CuraProfileReader/CuraProfileReader.py index d4e5d393b2..fa65033691 100644 --- a/plugins/CuraProfileReader/CuraProfileReader.py +++ b/plugins/CuraProfileReader/CuraProfileReader.py @@ -13,23 +13,30 @@ from cura.ReaderWriters.ProfileReader import ProfileReader import zipfile -## A plugin that reads profile data from Cura profile files. -# -# It reads a profile from a .curaprofile file, and returns it as a profile -# instance. + class CuraProfileReader(ProfileReader): - ## Initialises the cura profile reader. - # This does nothing since the only other function is basically stateless. + """A plugin that reads profile data from Cura profile files. + + It reads a profile from a .curaprofile file, and returns it as a profile + instance. + """ + def __init__(self) -> None: + """Initialises the cura profile reader. + + This does nothing since the only other function is basically stateless. + """ super().__init__() - ## Reads a cura profile from a file and returns it. - # - # \param file_name The file to read the cura profile from. - # \return The cura profiles that were in the file, if any. If the file - # could not be read or didn't contain a valid profile, ``None`` is - # returned. def read(self, file_name: str) -> List[Optional[InstanceContainer]]: + """Reads a cura profile from a file and returns it. + + :param file_name: The file to read the cura profile from. + :return: The cura profiles that were in the file, if any. If the file + could not be read or didn't contain a valid profile, ``None`` is + returned. + """ + try: with zipfile.ZipFile(file_name, "r") as archive: results = [] # type: List[Optional[InstanceContainer]] @@ -50,13 +57,14 @@ class CuraProfileReader(ProfileReader): serialized_bytes = fhandle.read() return [self._loadProfile(serialized, profile_id) for serialized, profile_id in self._upgradeProfile(serialized_bytes, file_name)] - ## Convert a profile from an old Cura to this Cura if needed. - # - # \param serialized The profile data to convert in the serialized on-disk - # format. - # \param profile_id The name of the profile. - # \return List of serialized profile strings and matching profile names. def _upgradeProfile(self, serialized: str, profile_id: str) -> List[Tuple[str, str]]: + """Convert a profile from an old Cura to this Cura if needed. + + :param serialized: The profile data to convert in the serialized on-disk format. + :param profile_id: The name of the profile. + :return: List of serialized profile strings and matching profile names. + """ + parser = configparser.ConfigParser(interpolation = None) parser.read_string(serialized) @@ -75,12 +83,14 @@ class CuraProfileReader(ProfileReader): else: return [(serialized, profile_id)] - ## Load a profile from a serialized string. - # - # \param serialized The profile data to read. - # \param profile_id The name of the profile. - # \return The profile that was stored in the string. def _loadProfile(self, serialized: str, profile_id: str) -> Optional[InstanceContainer]: + """Load a profile from a serialized string. + + :param serialized: The profile data to read. + :param profile_id: The name of the profile. + :return: The profile that was stored in the string. + """ + # Create an empty profile. profile = InstanceContainer(profile_id) profile.setMetaDataEntry("type", "quality_changes") @@ -102,13 +112,15 @@ class CuraProfileReader(ProfileReader): profile.setMetaDataEntry("definition", active_quality_definition) return profile - ## Upgrade a serialized profile to the current profile format. - # - # \param serialized The profile data to convert. - # \param profile_id The name of the profile. - # \param source_version The profile version of 'serialized'. - # \return List of serialized profile strings and matching profile names. def _upgradeProfileVersion(self, serialized: str, profile_id: str, main_version: int, setting_version: int) -> List[Tuple[str, str]]: + """Upgrade a serialized profile to the current profile format. + + :param serialized: The profile data to convert. + :param profile_id: The name of the profile. + :param source_version: The profile version of 'serialized'. + :return: List of serialized profile strings and matching profile names. + """ + source_version = main_version * 1000000 + setting_version from UM.VersionUpgradeManager import VersionUpgradeManager diff --git a/plugins/CuraProfileWriter/CuraProfileWriter.py b/plugins/CuraProfileWriter/CuraProfileWriter.py index 78f0b078d9..6643bd8d1f 100644 --- a/plugins/CuraProfileWriter/CuraProfileWriter.py +++ b/plugins/CuraProfileWriter/CuraProfileWriter.py @@ -6,15 +6,18 @@ from UM.Logger import Logger from cura.ReaderWriters.ProfileWriter import ProfileWriter import zipfile -## Writes profiles to Cura's own profile format with config files. class CuraProfileWriter(ProfileWriter): - ## Writes a profile to the specified file path. - # - # \param path \type{string} The file to output to. - # \param profiles \type{Profile} \type{List} The profile(s) to write to that file. - # \return \code True \endcode if the writing was successful, or \code - # False \endcode if it wasn't. + """Writes profiles to Cura's own profile format with config files.""" + def write(self, path, profiles): + """Writes a profile to the specified file path. + + :param path: :type{string} The file to output to. + :param profiles: :type{Profile} :type{List} The profile(s) to write to that file. + :return: True if the writing was successful, or + False if it wasn't. + """ + if type(profiles) != list: profiles = [profiles] diff --git a/plugins/FirmwareUpdateChecker/FirmwareUpdateChecker.py b/plugins/FirmwareUpdateChecker/FirmwareUpdateChecker.py index 9c4d498d7e..92678cb546 100644 --- a/plugins/FirmwareUpdateChecker/FirmwareUpdateChecker.py +++ b/plugins/FirmwareUpdateChecker/FirmwareUpdateChecker.py @@ -18,10 +18,12 @@ from .FirmwareUpdateCheckerMessage import FirmwareUpdateCheckerMessage i18n_catalog = i18nCatalog("cura") -## This Extension checks for new versions of the firmware based on the latest checked version number. -# The plugin is currently only usable for applications maintained by Ultimaker. But it should be relatively easy -# to change it to work for other applications. class FirmwareUpdateChecker(Extension): + """This Extension checks for new versions of the firmware based on the latest checked version number. + + The plugin is currently only usable for applications maintained by Ultimaker. But it should be relatively easy + to change it to work for other applications. + """ def __init__(self) -> None: super().__init__() @@ -35,8 +37,9 @@ class FirmwareUpdateChecker(Extension): self._check_job = None self._checked_printer_names = set() # type: Set[str] - ## Callback for the message that is spawned when there is a new version. def _onActionTriggered(self, message, action): + """Callback for the message that is spawned when there is a new version.""" + if action == FirmwareUpdateCheckerMessage.STR_ACTION_DOWNLOAD: machine_id = message.getMachineId() download_url = message.getDownloadUrl() @@ -57,13 +60,15 @@ class FirmwareUpdateChecker(Extension): def _onJobFinished(self, *args, **kwargs): self._check_job = None - ## Connect with software.ultimaker.com, load latest.version and check version info. - # If the version info is different from the current version, spawn a message to - # allow the user to download it. - # - # \param silent type(boolean) Suppresses messages other than "new version found" messages. - # This is used when checking for a new firmware version at startup. def checkFirmwareVersion(self, container = None, silent = False): + """Connect with software.ultimaker.com, load latest.version and check version info. + + If the version info is different from the current version, spawn a message to + allow the user to download it. + + :param silent: type(boolean) Suppresses messages other than "new version found" messages. + This is used when checking for a new firmware version at startup. + """ container_name = container.definition.getName() if container_name in self._checked_printer_names: return diff --git a/plugins/FirmwareUpdateChecker/FirmwareUpdateCheckerJob.py b/plugins/FirmwareUpdateChecker/FirmwareUpdateCheckerJob.py index 279b397777..f049542db1 100644 --- a/plugins/FirmwareUpdateChecker/FirmwareUpdateCheckerJob.py +++ b/plugins/FirmwareUpdateChecker/FirmwareUpdateCheckerJob.py @@ -21,8 +21,9 @@ from UM.i18n import i18nCatalog i18n_catalog = i18nCatalog("cura") -## This job checks if there is an update available on the provided URL. class FirmwareUpdateCheckerJob(Job): + """This job checks if there is an update available on the provided URL.""" + STRING_ZERO_VERSION = "0.0.0" STRING_EPSILON_VERSION = "0.0.1" ZERO_VERSION = Version(STRING_ZERO_VERSION) diff --git a/plugins/FirmwareUpdater/FirmwareUpdaterMachineAction.py b/plugins/FirmwareUpdater/FirmwareUpdaterMachineAction.py index e2b0041674..35f338fb04 100644 --- a/plugins/FirmwareUpdater/FirmwareUpdaterMachineAction.py +++ b/plugins/FirmwareUpdater/FirmwareUpdaterMachineAction.py @@ -19,8 +19,10 @@ if MYPY: catalog = i18nCatalog("cura") -## Upgrade the firmware of a machine by USB with this action. + class FirmwareUpdaterMachineAction(MachineAction): + """Upgrade the firmware of a machine by USB with this action.""" + def __init__(self) -> None: super().__init__("UpgradeFirmware", catalog.i18nc("@action", "Update Firmware")) self._qml_url = "FirmwareUpdaterMachineAction.qml" diff --git a/plugins/GCodeGzReader/GCodeGzReader.py b/plugins/GCodeGzReader/GCodeGzReader.py index a528b494e9..b4d9e85b3e 100644 --- a/plugins/GCodeGzReader/GCodeGzReader.py +++ b/plugins/GCodeGzReader/GCodeGzReader.py @@ -7,10 +7,13 @@ from UM.Mesh.MeshReader import MeshReader #The class we're extending/implementin from UM.MimeTypeDatabase import MimeTypeDatabase, MimeType #To add the .gcode.gz files to the MIME type database. from UM.PluginRegistry import PluginRegistry -## A file reader that reads gzipped g-code. -# -# If you're zipping g-code, you might as well use gzip! + class GCodeGzReader(MeshReader): + """A file reader that reads gzipped g-code. + + If you're zipping g-code, you might as well use gzip! + """ + def __init__(self) -> None: super().__init__() MimeTypeDatabase.addMimeType( diff --git a/plugins/GCodeGzWriter/GCodeGzWriter.py b/plugins/GCodeGzWriter/GCodeGzWriter.py index cbbfb8f986..e9dcbad76b 100644 --- a/plugins/GCodeGzWriter/GCodeGzWriter.py +++ b/plugins/GCodeGzWriter/GCodeGzWriter.py @@ -13,26 +13,31 @@ from UM.Scene.SceneNode import SceneNode #For typing. from UM.i18n import i18nCatalog catalog = i18nCatalog("cura") -## A file writer that writes gzipped g-code. -# -# If you're zipping g-code, you might as well use gzip! + class GCodeGzWriter(MeshWriter): + """A file writer that writes gzipped g-code. + + If you're zipping g-code, you might as well use gzip! + """ + def __init__(self) -> None: super().__init__(add_to_recent_files = False) - ## Writes the gzipped g-code to a stream. - # - # Note that even though the function accepts a collection of nodes, the - # entire scene is always written to the file since it is not possible to - # separate the g-code for just specific nodes. - # - # \param stream The stream to write the gzipped g-code to. - # \param nodes This is ignored. - # \param mode Additional information on what type of stream to use. This - # must always be binary mode. - # \return Whether the write was successful. def write(self, stream: BufferedIOBase, nodes: List[SceneNode], mode = MeshWriter.OutputMode.BinaryMode) -> bool: + """Writes the gzipped g-code to a stream. + + Note that even though the function accepts a collection of nodes, the + entire scene is always written to the file since it is not possible to + separate the g-code for just specific nodes. + + :param stream: The stream to write the gzipped g-code to. + :param nodes: This is ignored. + :param mode: Additional information on what type of stream to use. This + must always be binary mode. + :return: Whether the write was successful. + """ + if mode != MeshWriter.OutputMode.BinaryMode: Logger.log("e", "GCodeGzWriter does not support text mode.") self.setInformation(catalog.i18nc("@error:not supported", "GCodeGzWriter does not support text mode.")) diff --git a/plugins/GCodeProfileReader/GCodeProfileReader.py b/plugins/GCodeProfileReader/GCodeProfileReader.py index 9fbae7b473..2b80e80085 100644 --- a/plugins/GCodeProfileReader/GCodeProfileReader.py +++ b/plugins/GCodeProfileReader/GCodeProfileReader.py @@ -12,40 +12,48 @@ catalog = i18nCatalog("cura") from cura.ReaderWriters.ProfileReader import ProfileReader, NoProfileException -## A class that reads profile data from g-code files. -# -# It reads the profile data from g-code files and stores it in a new profile. -# This class currently does not process the rest of the g-code in any way. class GCodeProfileReader(ProfileReader): - ## The file format version of the serialized g-code. - # - # It can only read settings with the same version as the version it was - # written with. If the file format is changed in a way that breaks reverse - # compatibility, increment this version number! - version = 3 + """A class that reads profile data from g-code files. + + It reads the profile data from g-code files and stores it in a new profile. + This class currently does not process the rest of the g-code in any way. + """ + + version = 3 + """The file format version of the serialized g-code. + + It can only read settings with the same version as the version it was + written with. If the file format is changed in a way that breaks reverse + compatibility, increment this version number! + """ - ## Dictionary that defines how characters are escaped when embedded in - # g-code. - # - # Note that the keys of this dictionary are regex strings. The values are - # not. escape_characters = { re.escape("\\\\"): "\\", #The escape character. re.escape("\\n"): "\n", #Newlines. They break off the comment. re.escape("\\r"): "\r" #Carriage return. Windows users may need this for visualisation in their editors. } + """Dictionary that defines how characters are escaped when embedded in + + g-code. + + Note that the keys of this dictionary are regex strings. The values are + not. + """ - ## Initialises the g-code reader as a profile reader. def __init__(self): + """Initialises the g-code reader as a profile reader.""" + super().__init__() - ## Reads a g-code file, loading the profile from it. - # - # \param file_name The name of the file to read the profile from. - # \return The profile that was in the specified file, if any. If the - # specified file was no g-code or contained no parsable profile, \code - # None \endcode is returned. def read(self, file_name): + """Reads a g-code file, loading the profile from it. + + :param file_name: The name of the file to read the profile from. + :return: The profile that was in the specified file, if any. If the + specified file was no g-code or contained no parsable profile, + None is returned. + """ + if file_name.split(".")[-1] != "gcode": return None @@ -94,22 +102,28 @@ class GCodeProfileReader(ProfileReader): profiles.append(readQualityProfileFromString(profile_string)) return profiles -## Unescape a string which has been escaped for use in a gcode comment. -# -# \param string The string to unescape. -# \return \type{str} The unscaped string. -def unescapeGcodeComment(string): + +def unescapeGcodeComment(string: str) -> str: + """Unescape a string which has been escaped for use in a gcode comment. + + :param string: The string to unescape. + :return: The unescaped string. + """ + # Un-escape the serialized profile. pattern = re.compile("|".join(GCodeProfileReader.escape_characters.keys())) # Perform the replacement with a regular expression. return pattern.sub(lambda m: GCodeProfileReader.escape_characters[re.escape(m.group(0))], string) -## Read in a profile from a serialized string. -# -# \param profile_string The profile data in serialized form. -# \return \type{Profile} the resulting Profile object or None if it could not be read. -def readQualityProfileFromString(profile_string): + +def readQualityProfileFromString(profile_string) -> InstanceContainer: + """Read in a profile from a serialized string. + + :param profile_string: The profile data in serialized form. + :return: The resulting Profile object or None if it could not be read. + """ + # Create an empty profile - the id and name will be changed by the ContainerRegistry profile = InstanceContainer("") try: diff --git a/plugins/GCodeReader/FlavorParser.py b/plugins/GCodeReader/FlavorParser.py index 7b19fdb160..bcb7efc816 100644 --- a/plugins/GCodeReader/FlavorParser.py +++ b/plugins/GCodeReader/FlavorParser.py @@ -28,9 +28,8 @@ PositionOptional = NamedTuple("Position", [("x", Optional[float]), ("y", Optiona Position = NamedTuple("Position", [("x", float), ("y", float), ("z", float), ("f", float), ("e", List[float])]) -## This parser is intended to interpret the common firmware codes among all the -# different flavors class FlavorParser: + """This parser is intended to interpret the common firmware codes among all the different flavors""" def __init__(self) -> None: CuraApplication.getInstance().hideMessageSignal.connect(self._onHideMessage) @@ -212,8 +211,9 @@ class FlavorParser: # G0 and G1 should be handled exactly the same. _gCode1 = _gCode0 - ## Home the head. def _gCode28(self, position: Position, params: PositionOptional, path: List[List[Union[float, int]]]) -> Position: + """Home the head.""" + return self._position( params.x if params.x is not None else position.x, params.y if params.y is not None else position.y, @@ -221,21 +221,26 @@ class FlavorParser: position.f, position.e) - ## Set the absolute positioning def _gCode90(self, position: Position, params: PositionOptional, path: List[List[Union[float, int]]]) -> Position: + """Set the absolute positioning""" + self._is_absolute_positioning = True self._is_absolute_extrusion = True return position - ## Set the relative positioning def _gCode91(self, position: Position, params: PositionOptional, path: List[List[Union[float, int]]]) -> Position: + """Set the relative positioning""" + self._is_absolute_positioning = False self._is_absolute_extrusion = False return position - ## Reset the current position to the values specified. - # For example: G92 X10 will set the X to 10 without any physical motion. def _gCode92(self, position: Position, params: PositionOptional, path: List[List[Union[float, int]]]) -> Position: + """Reset the current position to the values specified. + + For example: G92 X10 will set the X to 10 without any physical motion. + """ + if params.e is not None: # Sometimes a G92 E0 is introduced in the middle of the GCode so we need to keep those offsets for calculate the line_width self._extrusion_length_offset[self._extruder_number] = position.e[self._extruder_number] - params.e @@ -291,8 +296,9 @@ class FlavorParser: _type_keyword = ";TYPE:" _layer_keyword = ";LAYER:" - ## For showing correct x, y offsets for each extruder def _extruderOffsets(self) -> Dict[int, List[float]]: + """For showing correct x, y offsets for each extruder""" + result = {} for extruder in ExtruderManager.getInstance().getActiveExtruderStacks(): result[int(extruder.getMetaData().get("position", "0"))] = [ diff --git a/plugins/GCodeReader/RepRapFlavorParser.py b/plugins/GCodeReader/RepRapFlavorParser.py index 2a24d16add..05f86beab0 100644 --- a/plugins/GCodeReader/RepRapFlavorParser.py +++ b/plugins/GCodeReader/RepRapFlavorParser.py @@ -3,8 +3,10 @@ from . import FlavorParser -## This parser is intended to interpret the RepRap Firmware g-code flavor. + class RepRapFlavorParser(FlavorParser.FlavorParser): + """This parser is intended to interpret the RepRap Firmware g-code flavor.""" + def __init__(self): super().__init__() @@ -17,16 +19,20 @@ class RepRapFlavorParser(FlavorParser.FlavorParser): # Set relative extrusion mode self._is_absolute_extrusion = False - ## Set the absolute positioning - # RepRapFlavor code G90 sets position of X, Y, Z to absolute - # For absolute E, M82 is used def _gCode90(self, position, params, path): + """Set the absolute positioning + + RepRapFlavor code G90 sets position of X, Y, Z to absolute + For absolute E, M82 is used + """ self._is_absolute_positioning = True return position - ## Set the relative positioning - # RepRapFlavor code G91 sets position of X, Y, Z to relative - # For relative E, M83 is used def _gCode91(self, position, params, path): + """Set the relative positioning + + RepRapFlavor code G91 sets position of X, Y, Z to relative + For relative E, M83 is used + """ self._is_absolute_positioning = False return position \ No newline at end of file diff --git a/plugins/GCodeWriter/GCodeWriter.py b/plugins/GCodeWriter/GCodeWriter.py index 792b2aff10..93ff0f51ff 100644 --- a/plugins/GCodeWriter/GCodeWriter.py +++ b/plugins/GCodeWriter/GCodeWriter.py @@ -14,34 +14,40 @@ from cura.Machines.ContainerTree import ContainerTree from UM.i18n import i18nCatalog catalog = i18nCatalog("cura") -## Writes g-code to a file. -# -# While this poses as a mesh writer, what this really does is take the g-code -# in the entire scene and write it to an output device. Since the g-code of a -# single mesh isn't separable from the rest what with rafts and travel moves -# and all, it doesn't make sense to write just a single mesh. -# -# So this plug-in takes the g-code that is stored in the root of the scene -# node tree, adds a bit of extra information about the profiles and writes -# that to the output device. -class GCodeWriter(MeshWriter): - ## The file format version of the serialised g-code. - # - # It can only read settings with the same version as the version it was - # written with. If the file format is changed in a way that breaks reverse - # compatibility, increment this version number! - version = 3 - ## Dictionary that defines how characters are escaped when embedded in - # g-code. - # - # Note that the keys of this dictionary are regex strings. The values are - # not. +class GCodeWriter(MeshWriter): + """Writes g-code to a file. + + While this poses as a mesh writer, what this really does is take the g-code + in the entire scene and write it to an output device. Since the g-code of a + single mesh isn't separable from the rest what with rafts and travel moves + and all, it doesn't make sense to write just a single mesh. + + So this plug-in takes the g-code that is stored in the root of the scene + node tree, adds a bit of extra information about the profiles and writes + that to the output device. + """ + + version = 3 + """The file format version of the serialised g-code. + + It can only read settings with the same version as the version it was + written with. If the file format is changed in a way that breaks reverse + compatibility, increment this version number! + """ + escape_characters = { re.escape("\\"): "\\\\", # The escape character. re.escape("\n"): "\\n", # Newlines. They break off the comment. re.escape("\r"): "\\r" # Carriage return. Windows users may need this for visualisation in their editors. } + """Dictionary that defines how characters are escaped when embedded in + + g-code. + + Note that the keys of this dictionary are regex strings. The values are + not. + """ _setting_keyword = ";SETTING_" @@ -50,17 +56,19 @@ class GCodeWriter(MeshWriter): self._application = Application.getInstance() - ## Writes the g-code for the entire scene to a stream. - # - # Note that even though the function accepts a collection of nodes, the - # entire scene is always written to the file since it is not possible to - # separate the g-code for just specific nodes. - # - # \param stream The stream to write the g-code to. - # \param nodes This is ignored. - # \param mode Additional information on how to format the g-code in the - # file. This must always be text mode. def write(self, stream, nodes, mode = MeshWriter.OutputMode.TextMode): + """Writes the g-code for the entire scene to a stream. + + Note that even though the function accepts a collection of nodes, the + entire scene is always written to the file since it is not possible to + separate the g-code for just specific nodes. + + :param stream: The stream to write the g-code to. + :param nodes: This is ignored. + :param mode: Additional information on how to format the g-code in the + file. This must always be text mode. + """ + if mode != MeshWriter.OutputMode.TextMode: Logger.log("e", "GCodeWriter does not support non-text mode.") self.setInformation(catalog.i18nc("@error:not supported", "GCodeWriter does not support non-text mode.")) @@ -88,8 +96,9 @@ class GCodeWriter(MeshWriter): self.setInformation(catalog.i18nc("@warning:status", "Please prepare G-code before exporting.")) return False - ## Create a new container with container 2 as base and container 1 written over it. def _createFlattenedContainerInstance(self, instance_container1, instance_container2): + """Create a new container with container 2 as base and container 1 written over it.""" + flat_container = InstanceContainer(instance_container2.getName()) # The metadata includes id, name and definition @@ -106,15 +115,15 @@ class GCodeWriter(MeshWriter): return flat_container - ## Serialises a container stack to prepare it for writing at the end of the - # g-code. - # - # The settings are serialised, and special characters (including newline) - # are escaped. - # - # \param settings A container stack to serialise. - # \return A serialised string of the settings. def _serialiseSettings(self, stack): + """Serialises a container stack to prepare it for writing at the end of the g-code. + + The settings are serialised, and special characters (including newline) + are escaped. + + :param stack: A container stack to serialise. + :return: A serialised string of the settings. + """ container_registry = self._application.getContainerRegistry() prefix = self._setting_keyword + str(GCodeWriter.version) + " " # The prefix to put before each line. diff --git a/plugins/LegacyProfileReader/LegacyProfileReader.py b/plugins/LegacyProfileReader/LegacyProfileReader.py index 87b26eb4ec..c67136ce9b 100644 --- a/plugins/LegacyProfileReader/LegacyProfileReader.py +++ b/plugins/LegacyProfileReader/LegacyProfileReader.py @@ -16,58 +16,67 @@ from UM.Settings.InstanceContainer import InstanceContainer # The new profile t from cura.ReaderWriters.ProfileReader import ProfileReader # The plug-in type to implement. -## A plugin that reads profile data from legacy Cura versions. -# -# It reads a profile from an .ini file, and performs some translations on it. -# Not all translations are correct, mind you, but it is a best effort. class LegacyProfileReader(ProfileReader): - ## Initialises the legacy profile reader. - # - # This does nothing since the only other function is basically stateless. + """A plugin that reads profile data from legacy Cura versions. + + It reads a profile from an .ini file, and performs some translations on it. + Not all translations are correct, mind you, but it is a best effort. + """ + def __init__(self): + """Initialises the legacy profile reader. + + This does nothing since the only other function is basically stateless. + """ + super().__init__() - ## Prepares the default values of all legacy settings. - # - # These are loaded from the Dictionary of Doom. - # - # \param json The JSON file to load the default setting values from. This - # should not be a URL but a pre-loaded JSON handle. - # \return A dictionary of the default values of the legacy Cura version. def prepareDefaults(self, json: Dict[str, Dict[str, str]]) -> Dict[str, str]: + """Prepares the default values of all legacy settings. + + These are loaded from the Dictionary of Doom. + + :param json: The JSON file to load the default setting values from. This + should not be a URL but a pre-loaded JSON handle. + :return: A dictionary of the default values of the legacy Cura version. + """ + defaults = {} if "defaults" in json: for key in json["defaults"]: # We have to copy over all defaults from the JSON handle to a normal dict. defaults[key] = json["defaults"][key] return defaults - ## Prepares the local variables that can be used in evaluation of computing - # new setting values from the old ones. - # - # This fills a dictionary with all settings from the legacy Cura version - # and their values, so that they can be used in evaluating the new setting - # values as Python code. - # - # \param config_parser The ConfigParser that finds the settings in the - # legacy profile. - # \param config_section The section in the profile where the settings - # should be found. - # \param defaults The default values for all settings in the legacy Cura. - # \return A set of local variables, one for each setting in the legacy - # profile. def prepareLocals(self, config_parser, config_section, defaults): + """Prepares the local variables that can be used in evaluation of computing + + new setting values from the old ones. + + This fills a dictionary with all settings from the legacy Cura version + and their values, so that they can be used in evaluating the new setting + values as Python code. + + :param config_parser: The ConfigParser that finds the settings in the + legacy profile. + :param config_section: The section in the profile where the settings + should be found. + :param defaults: The default values for all settings in the legacy Cura. + :return: A set of local variables, one for each setting in the legacy + profile. + """ copied_locals = defaults.copy() # Don't edit the original! for option in config_parser.options(config_section): copied_locals[option] = config_parser.get(config_section, option) return copied_locals - ## Reads a legacy Cura profile from a file and returns it. - # - # \param file_name The file to read the legacy Cura profile from. - # \return The legacy Cura profile that was in the file, if any. If the - # file could not be read or didn't contain a valid profile, \code None - # \endcode is returned. def read(self, file_name): + """Reads a legacy Cura profile from a file and returns it. + + :param file_name: The file to read the legacy Cura profile from. + :return: The legacy Cura profile that was in the file, if any. If the + file could not be read or didn't contain a valid profile, None is returned. + """ + if file_name.split(".")[-1] != "ini": return None global_container_stack = Application.getInstance().getGlobalContainerStack() diff --git a/plugins/LegacyProfileReader/tests/TestLegacyProfileReader.py b/plugins/LegacyProfileReader/tests/TestLegacyProfileReader.py index cd0f681828..64f3e1d404 100644 --- a/plugins/LegacyProfileReader/tests/TestLegacyProfileReader.py +++ b/plugins/LegacyProfileReader/tests/TestLegacyProfileReader.py @@ -13,7 +13,7 @@ import UM.PluginRegistry # To mock the plug-in registry out. import UM.Settings.ContainerRegistry # To mock the container registry out. import UM.Settings.InstanceContainer # To intercept the serialised data from the read() function. -import LegacyProfileReader as LegacyProfileReaderModule # To get the directory of the module. +import LegacyProfileReader as LegacyProfileReaderModule # To get the directory of the module. @pytest.fixture @@ -126,9 +126,11 @@ test_prepareLocalsNoSectionErrorData = [ ) ] -## Test cases where a key error is expected. + @pytest.mark.parametrize("parser_data, defaults", test_prepareLocalsNoSectionErrorData) def test_prepareLocalsNoSectionError(legacy_profile_reader, parser_data, defaults): + """Test cases where a key error is expected.""" + parser = configparser.ConfigParser() parser.read_dict(parser_data) diff --git a/plugins/MachineSettingsAction/MachineSettingsAction.py b/plugins/MachineSettingsAction/MachineSettingsAction.py index 28535024a7..c82ef8f351 100755 --- a/plugins/MachineSettingsAction/MachineSettingsAction.py +++ b/plugins/MachineSettingsAction/MachineSettingsAction.py @@ -23,9 +23,11 @@ if TYPE_CHECKING: catalog = UM.i18n.i18nCatalog("cura") -## This action allows for certain settings that are "machine only") to be modified. -# It automatically detects machine definitions that it knows how to change and attaches itself to those. class MachineSettingsAction(MachineAction): + """This action allows for certain settings that are "machine only") to be modified. + + It automatically detects machine definitions that it knows how to change and attaches itself to those. + """ def __init__(self, parent: Optional["QObject"] = None) -> None: super().__init__("MachineSettingsAction", catalog.i18nc("@action", "Machine Settings")) self._qml_url = "MachineSettingsAction.qml" @@ -56,9 +58,11 @@ class MachineSettingsAction(MachineAction): if isinstance(container, DefinitionContainer) and container.getMetaDataEntry("type") == "machine": self._application.getMachineActionManager().addSupportedAction(container.getId(), self.getKey()) - ## Triggered when the global container stack changes or when the g-code - # flavour setting is changed. def _updateHasMaterialsInContainerTree(self) -> None: + """Triggered when the global container stack changes or when the g-code + + flavour setting is changed. + """ global_stack = cura.CuraApplication.CuraApplication.getInstance().getGlobalContainerStack() if global_stack is None: return diff --git a/plugins/ModelChecker/ModelChecker.py b/plugins/ModelChecker/ModelChecker.py index 057ee14945..138d84cc86 100644 --- a/plugins/ModelChecker/ModelChecker.py +++ b/plugins/ModelChecker/ModelChecker.py @@ -18,8 +18,8 @@ catalog = i18nCatalog("cura") class ModelChecker(QObject, Extension): - ## Signal that gets emitted when anything changed that we need to check. onChanged = pyqtSignal() + """Signal that gets emitted when anything changed that we need to check.""" def __init__(self): super().__init__() @@ -47,11 +47,13 @@ class ModelChecker(QObject, Extension): if not isinstance(args[0], Camera): self._change_timer.start() - ## Called when plug-ins are initialized. - # - # This makes sure that we listen to changes of the material and that the - # button is created that indicates warnings with the current set-up. def _pluginsInitialized(self): + """Called when plug-ins are initialized. + + This makes sure that we listen to changes of the material and that the + button is created that indicates warnings with the current set-up. + """ + Application.getInstance().getMachineManager().rootMaterialChanged.connect(self.onChanged) self._createView() @@ -106,8 +108,12 @@ class ModelChecker(QObject, Extension): if node.callDecoration("isSliceable"): yield node - ## Creates the view used by show popup. The view is saved because of the fairly aggressive garbage collection. def _createView(self): + """Creates the view used by show popup. + + The view is saved because of the fairly aggressive garbage collection. + """ + Logger.log("d", "Creating model checker view.") # Create the plugin dialog component diff --git a/plugins/MonitorStage/MonitorStage.py b/plugins/MonitorStage/MonitorStage.py index 3d2a1c3f37..4e4c442b4c 100644 --- a/plugins/MonitorStage/MonitorStage.py +++ b/plugins/MonitorStage/MonitorStage.py @@ -1,72 +1,72 @@ -# Copyright (c) 2017 Ultimaker B.V. -# Cura is released under the terms of the LGPLv3 or higher. -import os.path -from UM.Application import Application -from cura.Stages.CuraStage import CuraStage - - -## Stage for monitoring a 3D printing while it's printing. -class MonitorStage(CuraStage): - - def __init__(self, parent = None): - super().__init__(parent) - - # Wait until QML engine is created, otherwise creating the new QML components will fail - Application.getInstance().engineCreatedSignal.connect(self._onEngineCreated) - self._printer_output_device = None - - self._active_print_job = None - self._active_printer = None - - def _setActivePrintJob(self, print_job): - if self._active_print_job != print_job: - self._active_print_job = print_job - - def _setActivePrinter(self, printer): - if self._active_printer != printer: - if self._active_printer: - self._active_printer.activePrintJobChanged.disconnect(self._onActivePrintJobChanged) - self._active_printer = printer - if self._active_printer: - self._setActivePrintJob(self._active_printer.activePrintJob) - # Jobs might change, so we need to listen to it's changes. - self._active_printer.activePrintJobChanged.connect(self._onActivePrintJobChanged) - else: - self._setActivePrintJob(None) - - def _onActivePrintJobChanged(self): - self._setActivePrintJob(self._active_printer.activePrintJob) - - def _onActivePrinterChanged(self): - self._setActivePrinter(self._printer_output_device.activePrinter) - - def _onOutputDevicesChanged(self): - try: - # We assume that you are monitoring the device with the highest priority. - new_output_device = Application.getInstance().getMachineManager().printerOutputDevices[0] - if new_output_device != self._printer_output_device: - if self._printer_output_device: - try: - self._printer_output_device.printersChanged.disconnect(self._onActivePrinterChanged) - except TypeError: - # Ignore stupid "Not connected" errors. - pass - - self._printer_output_device = new_output_device - - self._printer_output_device.printersChanged.connect(self._onActivePrinterChanged) - self._setActivePrinter(self._printer_output_device.activePrinter) - except IndexError: - pass - - def _onEngineCreated(self): - # We can only connect now, as we need to be sure that everything is loaded (plugins get created quite early) - Application.getInstance().getMachineManager().outputDevicesChanged.connect(self._onOutputDevicesChanged) - self._onOutputDevicesChanged() - - plugin_path = Application.getInstance().getPluginRegistry().getPluginPath(self.getPluginId()) - if plugin_path is not None: - menu_component_path = os.path.join(plugin_path, "MonitorMenu.qml") - main_component_path = os.path.join(plugin_path, "MonitorMain.qml") - self.addDisplayComponent("menu", menu_component_path) - self.addDisplayComponent("main", main_component_path) +# Copyright (c) 2017 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. +import os.path +from UM.Application import Application +from cura.Stages.CuraStage import CuraStage + + +class MonitorStage(CuraStage): + """Stage for monitoring a 3D printing while it's printing.""" + + def __init__(self, parent = None): + super().__init__(parent) + + # Wait until QML engine is created, otherwise creating the new QML components will fail + Application.getInstance().engineCreatedSignal.connect(self._onEngineCreated) + self._printer_output_device = None + + self._active_print_job = None + self._active_printer = None + + def _setActivePrintJob(self, print_job): + if self._active_print_job != print_job: + self._active_print_job = print_job + + def _setActivePrinter(self, printer): + if self._active_printer != printer: + if self._active_printer: + self._active_printer.activePrintJobChanged.disconnect(self._onActivePrintJobChanged) + self._active_printer = printer + if self._active_printer: + self._setActivePrintJob(self._active_printer.activePrintJob) + # Jobs might change, so we need to listen to it's changes. + self._active_printer.activePrintJobChanged.connect(self._onActivePrintJobChanged) + else: + self._setActivePrintJob(None) + + def _onActivePrintJobChanged(self): + self._setActivePrintJob(self._active_printer.activePrintJob) + + def _onActivePrinterChanged(self): + self._setActivePrinter(self._printer_output_device.activePrinter) + + def _onOutputDevicesChanged(self): + try: + # We assume that you are monitoring the device with the highest priority. + new_output_device = Application.getInstance().getMachineManager().printerOutputDevices[0] + if new_output_device != self._printer_output_device: + if self._printer_output_device: + try: + self._printer_output_device.printersChanged.disconnect(self._onActivePrinterChanged) + except TypeError: + # Ignore stupid "Not connected" errors. + pass + + self._printer_output_device = new_output_device + + self._printer_output_device.printersChanged.connect(self._onActivePrinterChanged) + self._setActivePrinter(self._printer_output_device.activePrinter) + except IndexError: + pass + + def _onEngineCreated(self): + # We can only connect now, as we need to be sure that everything is loaded (plugins get created quite early) + Application.getInstance().getMachineManager().outputDevicesChanged.connect(self._onOutputDevicesChanged) + self._onOutputDevicesChanged() + + plugin_path = Application.getInstance().getPluginRegistry().getPluginPath(self.getPluginId()) + if plugin_path is not None: + menu_component_path = os.path.join(plugin_path, "MonitorMenu.qml") + main_component_path = os.path.join(plugin_path, "MonitorMain.qml") + self.addDisplayComponent("menu", menu_component_path) + self.addDisplayComponent("main", main_component_path) diff --git a/plugins/PerObjectSettingsTool/PerObjectSettingVisibilityHandler.py b/plugins/PerObjectSettingsTool/PerObjectSettingVisibilityHandler.py index 78da0512f7..c44e4a4df3 100644 --- a/plugins/PerObjectSettingsTool/PerObjectSettingVisibilityHandler.py +++ b/plugins/PerObjectSettingsTool/PerObjectSettingVisibilityHandler.py @@ -15,9 +15,11 @@ from cura.Settings.ExtruderManager import ExtruderManager #To get global-inherit 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): + """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): super().__init__(parent = parent, *args, **kwargs) diff --git a/plugins/PerObjectSettingsTool/PerObjectSettingsTool.py b/plugins/PerObjectSettingsTool/PerObjectSettingsTool.py index 4f0d90a8e3..d23096c0a3 100644 --- a/plugins/PerObjectSettingsTool/PerObjectSettingsTool.py +++ b/plugins/PerObjectSettingsTool/PerObjectSettingsTool.py @@ -12,9 +12,11 @@ from UM.Settings.SettingInstance import SettingInstance 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): + """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): super().__init__() self._model = None @@ -48,26 +50,31 @@ class PerObjectSettingsTool(Tool): except AttributeError: return "" - ## Gets the active extruder of the currently selected object. - # - # \return The active extruder of the currently selected object. 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) 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): + """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) stack = selected_object.callDecoration("getStack") #Don't try to get the active extruder since it may be None anyway. if not stack: selected_object.addDecorator(SettingOverrideDecorator()) 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: + """Returns True when the mesh_type was changed, False when current mesh_type == mesh_type""" + old_mesh_type = self.getMeshType() if old_mesh_type == mesh_type: return False diff --git a/plugins/PostProcessingPlugin/PostProcessingPlugin.py b/plugins/PostProcessingPlugin/PostProcessingPlugin.py index 9bf8062ffd..c1135640dd 100644 --- a/plugins/PostProcessingPlugin/PostProcessingPlugin.py +++ b/plugins/PostProcessingPlugin/PostProcessingPlugin.py @@ -27,9 +27,8 @@ if TYPE_CHECKING: 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): + """Extension type plugin that enables pre-written scripts to post process g-code files.""" def __init__(self, parent = None) -> None: QObject.__init__(self, parent) Extension.__init__(self) @@ -69,8 +68,9 @@ class PostProcessingPlugin(QObject, Extension): except IndexError: return "" - ## Execute all post-processing scripts on the gcode. def execute(self, output_device) -> None: + """Execute all post-processing scripts on the gcode.""" + scene = Application.getInstance().getController().getScene() # If the scene does not have a gcode, do nothing if not hasattr(scene, "gcode_dict"): @@ -119,9 +119,10 @@ class PostProcessingPlugin(QObject, Extension): self.selectedIndexChanged.emit() #Ensure that settings are updated self._propertyChanged() - ## Remove a script from the active script list by index. @pyqtSlot(int) def removeScriptByIndex(self, index: int) -> None: + """Remove a script from the active script list by index.""" + self._script_list.pop(index) if len(self._script_list) - 1 < self._selected_script_index: 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._propertyChanged() - ## Load all scripts from all paths where scripts can be found. - # - # This should probably only be done on init. 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. return @@ -152,10 +155,12 @@ class PostProcessingPlugin(QObject, Extension): 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: + """Load all scripts from provided path. + + This should probably only be done on init. + :param path: Path to check for scripts. + """ if ApplicationMetadata.IsEnterpriseVersion: # 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: TrustBasics.removeCached(path) - ## Load all scripts in the scripts folders scripts = pkgutil.iter_modules(path = [path]) + """Load all scripts in the scripts folders""" for loader, script_name, ispkg in scripts: # Iterate over all scripts. if script_name not in sys.modules: @@ -278,9 +283,8 @@ class PostProcessingPlugin(QObject, Extension): self.scriptListChanged.emit() self._propertyChanged() - ## When the global container stack is changed, swap out the list of active - # scripts. def _onGlobalContainerStackChanged(self) -> None: + """When the global container stack is changed, swap out the list of active scripts.""" if self._global_container_stack: self._global_container_stack.metaDataChanged.disconnect(self._restoreScriptInforFromMetadata) @@ -323,8 +327,12 @@ class PostProcessingPlugin(QObject, Extension): # We do want to listen to other events. 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: + """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.") self.loadAllScripts() @@ -340,8 +348,9 @@ class PostProcessingPlugin(QObject, Extension): # Create the save button component CuraApplication.getInstance().addAdditionalComponent("saveButton", self._view.findChild(QObject, "postProcessingSaveAreaButton")) - ## Show the (GUI) popup of the post processing plugin. def showPopup(self) -> None: + """Show the (GUI) popup of the post processing plugin.""" + if self._view is None: self._createView() if self._view is None: @@ -349,11 +358,13 @@ class PostProcessingPlugin(QObject, Extension): return 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: + """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() if global_container_stack is not None: global_container_stack.propertyChanged.emit("post_processing_plugin", "value") diff --git a/plugins/PostProcessingPlugin/Script.py b/plugins/PostProcessingPlugin/Script.py index e502f107f9..d15b209478 100644 --- a/plugins/PostProcessingPlugin/Script.py +++ b/plugins/PostProcessingPlugin/Script.py @@ -23,9 +23,10 @@ if TYPE_CHECKING: from UM.Settings.Interfaces import DefinitionContainerInterface -## Base class for scripts. All scripts should inherit the script class. @signalemitter class Script: + """Base class for scripts. All scripts should inherit the script class.""" + def __init__(self) -> None: super().__init__() self._stack = None # type: Optional[ContainerStack] @@ -78,13 +79,15 @@ class Script: if global_container_stack is not None: 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]: + """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 = json.loads(setting_data_as_string, object_pairs_hook = collections.OrderedDict) return setting_data @@ -104,15 +107,18 @@ class Script: return self._stack.getId() return None - ## Convenience function that retrieves value of a setting from the stack. def getSettingValueByKey(self, key: str) -> Any: + """Convenience function that retrieves value of a setting from the stack.""" + if self._stack is not None: return self._stack.getProperty(key, "value") 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: + """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(';')): return default sub_part = line[line.find(key) + 1:] @@ -127,20 +133,23 @@ class Script: except ValueError: #Not a number at all. 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: + """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. comment = "" if ";" in line: @@ -179,7 +188,9 @@ class Script: 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]: + """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() diff --git a/plugins/PostProcessingPlugin/scripts/FilamentChange.py b/plugins/PostProcessingPlugin/scripts/FilamentChange.py index 943ca30f2e..351b985d49 100644 --- a/plugins/PostProcessingPlugin/scripts/FilamentChange.py +++ b/plugins/PostProcessingPlugin/scripts/FilamentChange.py @@ -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]): + """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") initial_retract = self.getSettingValueByKey("initial_retract") later_retract = self.getSettingValueByKey("later_retract") diff --git a/plugins/PostProcessingPlugin/scripts/PauseAtHeight.py b/plugins/PostProcessingPlugin/scripts/PauseAtHeight.py index 1ba8b8213b..766677af2a 100644 --- a/plugins/PostProcessingPlugin/scripts/PauseAtHeight.py +++ b/plugins/PostProcessingPlugin/scripts/PauseAtHeight.py @@ -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]: + """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") for line in lines: 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 0, 0 - ## Inserts the pause commands. - # \param data: List of layers. - # \return New list of layers. 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_height = self.getSettingValueByKey("pause_height") pause_layer = self.getSettingValueByKey("pause_layer") diff --git a/plugins/PostProcessingPlugin/scripts/RetractContinue.py b/plugins/PostProcessingPlugin/scripts/RetractContinue.py index e437439287..3e095bd395 100644 --- a/plugins/PostProcessingPlugin/scripts/RetractContinue.py +++ b/plugins/PostProcessingPlugin/scripts/RetractContinue.py @@ -5,8 +5,10 @@ import math from ..Script import Script -## Continues retracting during all travel moves. + class RetractContinue(Script): + """Continues retracting during all travel moves.""" + def getSettingDataString(self): return """{ "name": "Retract Continue", diff --git a/plugins/PostProcessingPlugin/scripts/SearchAndReplace.py b/plugins/PostProcessingPlugin/scripts/SearchAndReplace.py index 68d697e470..b601a037da 100644 --- a/plugins/PostProcessingPlugin/scripts/SearchAndReplace.py +++ b/plugins/PostProcessingPlugin/scripts/SearchAndReplace.py @@ -5,11 +5,14 @@ import re #To perform the search and replace. 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): + """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): return """{ "name": "Search and Replace", diff --git a/plugins/PrepareStage/PrepareStage.py b/plugins/PrepareStage/PrepareStage.py index c2dee9693b..2d7ee9ee4f 100644 --- a/plugins/PrepareStage/PrepareStage.py +++ b/plugins/PrepareStage/PrepareStage.py @@ -1,19 +1,21 @@ -# Copyright (c) 2019 Ultimaker B.V. -# Cura is released under the terms of the LGPLv3 or higher. - -import os.path -from UM.Application import Application -from UM.PluginRegistry import PluginRegistry -from cura.Stages.CuraStage import CuraStage - -## Stage for preparing model (slicing). -class PrepareStage(CuraStage): - def __init__(self, parent = None): - super().__init__(parent) - Application.getInstance().engineCreatedSignal.connect(self._engineCreated) - - def _engineCreated(self): - menu_component_path = os.path.join(PluginRegistry.getInstance().getPluginPath("PrepareStage"), "PrepareMenu.qml") - main_component_path = os.path.join(PluginRegistry.getInstance().getPluginPath("PrepareStage"), "PrepareMain.qml") - self.addDisplayComponent("menu", menu_component_path) +# Copyright (c) 2019 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. + +import os.path +from UM.Application import Application +from UM.PluginRegistry import PluginRegistry +from cura.Stages.CuraStage import CuraStage + + +class PrepareStage(CuraStage): + """Stage for preparing model (slicing).""" + + def __init__(self, parent = None): + super().__init__(parent) + Application.getInstance().engineCreatedSignal.connect(self._engineCreated) + + def _engineCreated(self): + menu_component_path = os.path.join(PluginRegistry.getInstance().getPluginPath("PrepareStage"), "PrepareMenu.qml") + main_component_path = os.path.join(PluginRegistry.getInstance().getPluginPath("PrepareStage"), "PrepareMain.qml") + self.addDisplayComponent("menu", menu_component_path) self.addDisplayComponent("main", main_component_path) \ No newline at end of file diff --git a/plugins/PreviewStage/PreviewStage.py b/plugins/PreviewStage/PreviewStage.py index 1c487c8340..deec8b4197 100644 --- a/plugins/PreviewStage/PreviewStage.py +++ b/plugins/PreviewStage/PreviewStage.py @@ -12,37 +12,45 @@ if TYPE_CHECKING: from UM.View.View import View -## Displays a preview of what you're about to print. -# -# The Python component of this stage just loads PreviewMain.qml for display -# when the stage is selected, and makes sure that it reverts to the previous -# view when the previous stage is activated. class PreviewStage(CuraStage): + """Displays a preview of what you're about to print. + + The Python component of this stage just loads PreviewMain.qml for display + when the stage is selected, and makes sure that it reverts to the previous + view when the previous stage is activated. + """ + def __init__(self, application: QtApplication, parent = None) -> None: super().__init__(parent) self._application = application self._application.engineCreatedSignal.connect(self._engineCreated) self._previously_active_view = None # type: Optional[View] - ## When selecting the stage, remember which was the previous view so that - # we can revert to that view when we go out of the stage later. def onStageSelected(self) -> None: + """When selecting the stage, remember which was the previous view so that + + we can revert to that view when we go out of the stage later. + """ self._previously_active_view = self._application.getController().getActiveView() - ## Called when going to a different stage (away from the Preview Stage). - # - # When going to a different stage, the view should be reverted to what it - # was before. Normally, that just reverts it to solid view. def onStageDeselected(self) -> None: + """Called when going to a different stage (away from the Preview Stage). + + When going to a different stage, the view should be reverted to what it + was before. Normally, that just reverts it to solid view. + """ + if self._previously_active_view is not None: self._application.getController().setActiveView(self._previously_active_view.getPluginId()) self._previously_active_view = None - ## Delayed load of the QML files. - # - # We need to make sure that the QML engine is running before we can load - # these. def _engineCreated(self) -> None: + """Delayed load of the QML files. + + We need to make sure that the QML engine is running before we can load + these. + """ + plugin_path = self._application.getPluginRegistry().getPluginPath(self.getPluginId()) if plugin_path is not None: menu_component_path = os.path.join(plugin_path, "PreviewMenu.qml") diff --git a/plugins/RemovableDriveOutputDevice/LinuxRemovableDrivePlugin.py b/plugins/RemovableDriveOutputDevice/LinuxRemovableDrivePlugin.py index cf889ebb12..3661bfa63c 100644 --- a/plugins/RemovableDriveOutputDevice/LinuxRemovableDrivePlugin.py +++ b/plugins/RemovableDriveOutputDevice/LinuxRemovableDrivePlugin.py @@ -10,12 +10,14 @@ import glob import os import subprocess -## Support for removable devices on Linux. -# -# TODO: This code uses the most basic interfaces for handling this. -# We should instead use UDisks2 to handle mount/unmount and hotplugging events. -# + class LinuxRemovableDrivePlugin(RemovableDrivePlugin.RemovableDrivePlugin): + """Support for removable devices on Linux. + + TODO: This code uses the most basic interfaces for handling this. + We should instead use UDisks2 to handle mount/unmount and hotplugging events. + """ + def checkRemovableDrives(self): drives = {} for volume in glob.glob("/media/*"): diff --git a/plugins/RemovableDriveOutputDevice/OSXRemovableDrivePlugin.py b/plugins/RemovableDriveOutputDevice/OSXRemovableDrivePlugin.py index 0d2c474e42..70e2107898 100644 --- a/plugins/RemovableDriveOutputDevice/OSXRemovableDrivePlugin.py +++ b/plugins/RemovableDriveOutputDevice/OSXRemovableDrivePlugin.py @@ -9,8 +9,10 @@ import os import plistlib -## Support for removable devices on Mac OSX + class OSXRemovableDrivePlugin(RemovableDrivePlugin.RemovableDrivePlugin): + """Support for removable devices on Mac OSX""" + def checkRemovableDrives(self): drives = {} p = subprocess.Popen(["system_profiler", "SPUSBDataType", "-xml"], stdout = subprocess.PIPE, stderr = subprocess.PIPE) diff --git a/plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py b/plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py index c81e4a76bc..a4fe7309f7 100644 --- a/plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py +++ b/plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py @@ -28,17 +28,19 @@ class RemovableDriveOutputDevice(OutputDevice): self._writing = False self._stream = None - ## Request the specified nodes to be written to the removable drive. - # - # \param nodes A collection of scene nodes that should be written to the - # removable drive. - # \param file_name \type{string} A suggestion for the file name to write - # to. If none is provided, a file name will be made from the names of the - # meshes. - # \param limit_mimetypes Should we limit the available MIME types to the - # MIME types available to the currently active machine? - # def requestWrite(self, nodes, file_name = None, filter_by_machine = False, file_handler = None, **kwargs): + """Request the specified nodes to be written to the removable drive. + + :param nodes: A collection of scene nodes that should be written to the + removable drive. + :param file_name: :type{string} A suggestion for the file name to write to. + If none is provided, a file name will be made from the names of the + meshes. + :param limit_mimetypes: Should we limit the available MIME types to the + MIME types available to the currently active machine? + + """ + filter_by_machine = True # This plugin is intended to be used by machine (regardless of what it was told to do) if self._writing: raise OutputDeviceError.DeviceBusyError() @@ -106,14 +108,14 @@ class RemovableDriveOutputDevice(OutputDevice): Logger.log("e", "Operating system would not let us write to %s: %s", file_name, str(e)) raise OutputDeviceError.WriteRequestFailedError(catalog.i18nc("@info:status Don't translate the XML tags or !", "Could not save to {0}: {1}").format(file_name, str(e))) from e - ## Generate a file name automatically for the specified nodes to be saved - # in. - # - # The name generated will be the name of one of the nodes. Which node that - # is can not be guaranteed. - # - # \param nodes A collection of nodes for which to generate a file name. def _automaticFileName(self, nodes): + """Generate a file name automatically for the specified nodes to be saved in. + + The name generated will be the name of one of the nodes. Which node that + is can not be guaranteed. + + :param nodes: A collection of nodes for which to generate a file name. + """ for root in nodes: for child in BreadthFirstIterator(root): if child.getMeshData(): diff --git a/plugins/RemovableDriveOutputDevice/WindowsRemovableDrivePlugin.py b/plugins/RemovableDriveOutputDevice/WindowsRemovableDrivePlugin.py index 8a183c25f4..ddcabd7311 100644 --- a/plugins/RemovableDriveOutputDevice/WindowsRemovableDrivePlugin.py +++ b/plugins/RemovableDriveOutputDevice/WindowsRemovableDrivePlugin.py @@ -42,8 +42,9 @@ ctypes.windll.kernel32.DeviceIoControl.argtypes = [ #type: ignore ctypes.windll.kernel32.DeviceIoControl.restype = wintypes.BOOL #type: ignore -## Removable drive support for windows class WindowsRemovableDrivePlugin(RemovableDrivePlugin.RemovableDrivePlugin): + """Removable drive support for windows""" + def checkRemovableDrives(self): drives = {}