initial commit
This commit is contained in:
commit
9f669bbbbb
BIN
._README.md
Normal file
BIN
._README.md
Normal file
Binary file not shown.
3
.idea/.gitignore
vendored
Normal file
3
.idea/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
8
.idea/cura_temp_plugin.iml
Normal file
8
.idea/cura_temp_plugin.iml
Normal 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>
|
6
.idea/inspectionProfiles/profiles_settings.xml
Normal file
6
.idea/inspectionProfiles/profiles_settings.xml
Normal 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
Normal file
4
.idea/misc.xml
Normal 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
Normal file
8
.idea/modules.xml
Normal 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
17
README.md
Normal 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.
|
136
TemperatureCalculatorPlugin.py
Normal file
136
TemperatureCalculatorPlugin.py
Normal 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
23
__init__.py
Normal 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
9
plugin.json
Normal 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"
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user