initial commit

This commit is contained in:
Manuel Weiser 2022-02-11 20:34:13 +01:00
commit 9f669bbbbb
10 changed files with 214 additions and 0 deletions

BIN
._README.md Normal file

Binary file not shown.

3
.idea/.gitignore generated vendored Normal file
View File

@ -0,0 +1,3 @@
# Default ignored files
/shelf/
/workspace.xml

8
.idea/cura_temp_plugin.iml generated Normal file
View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="PYTHON_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

View File

@ -0,0 +1,6 @@
<component name="InspectionProjectProfileManager">
<settings>
<option name="USE_PROJECT_PROFILE" value="false" />
<version value="1.0" />
</settings>
</component>

4
.idea/misc.xml generated Normal file
View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.8" project-jdk-type="Python SDK" />
</project>

8
.idea/modules.xml generated Normal file
View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/cura_temp_plugin.iml" filepath="$PROJECT_DIR$/.idea/cura_temp_plugin.iml" />
</modules>
</component>
</project>

17
README.md Normal file
View File

@ -0,0 +1,17 @@
Example Extension
=================
This is an example extension plug-in for Uranium. Uranium is the underlying framework used in Ultimaker Cura and NinjaKittens.
The extension type plug-in is a "generic" type of plug-in that just gets some object constructed upon loading the plug-in for the first time. Using the initialisation of that class as starting point for your code, you can access all of the application.
There are two typical use cases for extensions:
1. Modifying some behaviour in the application or modifying current functionality. This is done by listening to the desired event, such as the changing of the current machine or on start-up. When that event happens, some code can be executed that adds on the behaviour.
2. Adding a dialogue that provides additional functionality. There is a handy built-in method that allows you to add a menu item easily, and what should happen when the user clicks on it.
This plug-in shows an example of both use cases.
Packaging
---------
To package your plug-in, compress your plug-in folder in a .zip archive and rename that archive to get the `.plugin` extension. These .plugin files can be dropped into any Uranium application to be installed.

View File

@ -0,0 +1,136 @@
# Copyright (c) 2022 ManuelW
# The Temperature Profile Correction Plugin is released under the terms of the AGPLv3 or higher.
import re
from collections import OrderedDict
from UM.Extension import Extension
from UM.Application import Application
from UM.Settings.SettingDefinition import SettingDefinition
from UM.Settings.DefinitionContainer import DefinitionContainer
from UM.Settings.ContainerRegistry import ContainerRegistry
from UM.Logger import Logger
class TemperatureCalculatorPlugin(Extension):
def __init__(self):
super().__init__()
self._application = Application.getInstance()
self._i18n_catalog = None
self._settings_dict = OrderedDict()
self._settings_dict["temp_calc_number"] = {
"label": "Material Temperature Correction",
"description": "A correction of the print temperature from Material settings. A negative value decreases the print temperature, a positive value will increase the print temperature.",
"type": "float",
"unit": "°C",
"default_value": 0,
"maximum_value_warning": 30,
"settable_per_mesh": False,
"settable_per_extruder": False,
"settable_per_meshgroup": False
}
ContainerRegistry.getInstance().containerLoadComplete.connect(self._onContainerLoadComplete)
self._application.getOutputDeviceManager().writeStarted.connect(self._filterGcode)
def _onContainerLoadComplete(self, container_id):
if not ContainerRegistry.getInstance().isLoaded(container_id):
# skip containers that could not be loaded, or subsequent findContainers() will cause an infinite loop
return
try:
container = ContainerRegistry.getInstance().findContainers(id = container_id)[0]
except IndexError:
# the container no longer exists
return
if not isinstance(container, DefinitionContainer):
# skip containers that are not definitions
return
if container.getMetaDataEntry("type") == "extruder":
# skip extruder definitions
return
material = container.findDefinitions(key="material")
temp_calc = container.findDefinitions(key=list(self._settings_dict.keys())[0])
if material and not temp_calc:
material = material[0]
for setting_key, setting_dict in self._settings_dict.items():
definition = SettingDefinition(setting_key, container, material, self._i18n_catalog)
definition.deserialize(setting_dict)
# add the setting to the already existing platform adhesion settingdefinition
# private member access is naughty, but the alternative is to serialise, nix and deserialise the whole thing,
# which breaks stuff
material._children.append(definition)
container._definition_cache[setting_key] = definition
container._updateRelations(definition)
def _filterGcode(self, output_device):
scene = self._application.getController().getScene()
global_container_stack = self._application.getGlobalContainerStack()
if not global_container_stack:
return
# get setting from Cura
temp_calc_value = global_container_stack.getProperty("temp_calc_number", "value")
if temp_calc_value == 0:
return
use_temp_calc = global_container_stack.getProperty("temp_calc_number", "value")
gcode_dict = getattr(scene, "gcode_dict", {})
if not gcode_dict: # this also checks for an empty dict
Logger.log("w", "Scene has no gcode to process")
return
dict_changed = False
hotend_temp_regex = re.compile(r"^(M104\sS)(\d{2,3})\Z") #grab only "M104 Sxx" Strings
for plate_id in gcode_dict:
gcode_list = gcode_dict[plate_id]
if len(gcode_list) < 2:
Logger.log("w", "Plate %s does not contain any layers", plate_id)
continue
if ";TEMPCALCPROCESSED\n" not in gcode_list[0]:
# look for the first line that contains a G0 or G1 move on the Z axis
# gcode_list[2] is the first layer, after the preamble and the start gcode
if ";LAYER:0\n" in gcode_list[1]:
# layer 0 somehow got appended to the start gcode chunk
chunks = gcode_list[1].split(";LAYER:0\n")
gcode_list[1] = chunks[0]
gcode_list.insert(2, ";LAYER:0\n" + chunks[1])
# process all G0/G1 lines and adjust the Z value
for n in range(2, len(gcode_list)): # all gcode lists / layers, start at layer 1 = gcode list 2
lines = gcode_list[n].split("\n")
for (line_nr, line) in enumerate(lines):
result = hotend_temp_regex.fullmatch(line)
if result:
try:
adjusted_temp = round(float(result.group(2)) + temp_calc_value, 5)
except ValueError:
Logger.log("e", "Unable to process temp in line %s", line)
continue
lines[line_nr] = result.group(1) + str(adjusted_temp) + " ;adjusted by temp calc"
gcode_list[n] = "\n".join(lines)
gcode_list[0] += ";TEMPCALCPROCESSED\n"
gcode_dict[plate_id] = gcode_list
dict_changed = True
else:
Logger.log("d", "Plate %s has already been processed", plate_id)
continue
if dict_changed:
setattr(scene, "gcode_dict", gcode_dict)

23
__init__.py Normal file
View File

@ -0,0 +1,23 @@
# Copyright (c) 2022 ManuelW
# The Temperature Profile Correction Plugin is released under the terms of the AGPLv3 or higher.
from . import TemperatureCalculatorPlugin
## Defines additional metadata for the plug-in.
#
# Some types of plug-ins require additional metadata, such as which file types
# they are able to read or the name of the tool they define. In the case of
# the "Extension" type plug-in, there is no additional metadata though.
def getMetaData():
return {}
## Lets Uranium know that this plug-in exists.
#
# This is called when starting the application to find out which plug-ins
# exist and what their types are. We need to return a dictionary mapping from
# strings representing plug-in types (in this case "extension") to objects
# that inherit from PluginObject.
#
# \param app The application that the plug-in needs to register with.
def register(app):
return {"extension": TemperatureCalculatorPlugin.TemperatureCalculatorPlugin()}

9
plugin.json Normal file
View File

@ -0,0 +1,9 @@
{
"name": "Profile Temperature Correction",
"author": "ManuelW",
"version": "1.0",
"api": 5,
"supported_sdk_versions": ["5.0.0", "6.0.0", "7.0.0"],
"description": "Correct the Material Temperature in Profiles by setting positive or negative Values.",
"catalog": "uranium"
}