Skip to content

Commit

Permalink
embed QOI thumbnail images when using "Save to Disk" option
Browse files Browse the repository at this point in the history
similar the PostProcessing plugin by hooking into writeStarted event
  • Loading branch information
Kriechi committed Aug 5, 2022
1 parent 04d8d25 commit c103f19
Show file tree
Hide file tree
Showing 5 changed files with 57 additions and 41 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# Changelog of Cura-DuetRRFPlugin

## v1.2.8: 2022-XX-XX
* also embed QOI thumbnail images when using "Save to Disk" option

## v1.2.7: 2022-04-26
* bump compatibility for Cura 5.0 / API 8.0, while retaining compatibility with Cura 4.11 / 7.7 and up

Expand Down
41 changes: 2 additions & 39 deletions DuetRRFOutputDevice.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,13 @@
from UM.Application import Application
from UM.Logger import Logger
from UM.Message import Message
from UM.Mesh.MeshWriter import MeshWriter
from UM.PluginRegistry import PluginRegistry
from UM.OutputDevice.OutputDevice import OutputDevice
from UM.OutputDevice import OutputDeviceError
from UM.i18n import i18nCatalog
catalog = i18nCatalog("cura")

from . import DuetRRFSettings
from .thumbnails import generate_thumbnail

from .helpers import serializing_scene_to_gcode

class OutputStage(Enum):
ready = 0
Expand Down Expand Up @@ -229,17 +226,7 @@ def _onFilenameAccepted(self):
)
self._message.show()

# get the gcode through the GCodeWrite plugin
# this serializes the actual scene and should produce the same output as "Save to File"
gcode_stream = self._serializing_scene_to_gcode()

# generate model thumbnail and embedd in gcode file
self._message.setText("Rendering thumbnail image...")
thumbnail_stream = generate_thumbnail()

# assemble everything and inject custom data
self._message.setText("Assembling final gcode file...")
self._stream = self._assemble_final_gcode(gcode_stream, thumbnail_stream)
self._stream = serializing_scene_to_gcode()

# start upload workflow
self._message.setText("Uploading {} ...".format(self._fileName))
Expand All @@ -250,30 +237,6 @@ def _onFilenameAccepted(self):
on_error=self._check_duet3_sbc,
)

def _serializing_scene_to_gcode(self):
Logger.log("d", "Serializing gcode...")
gcode_writer = cast(MeshWriter, PluginRegistry.getInstance().getPluginObject("GCodeWriter"))
gcode_stream = StringIO()
success = gcode_writer.write(gcode_stream, None)
if not success:
Logger.log("e", "GCodeWriter failed.")
return None
return gcode_stream

def _assemble_final_gcode(self, gcode_stream, thumbnail_stream):
Logger.log("d", "Assembling final gcode file...")

final_stream = StringIO()
gcode_stream.seek(0)
for l in gcode_stream.readlines():
final_stream.write(l)
if l.startswith(";Generated with"):
version = DuetRRFSettings.get_plugin_version()
final_stream.write(f";Exported with Cura-DuetRRF v{version} plugin by Thomas Kriechbaumer\n")
final_stream.write(thumbnail_stream.getvalue())

return final_stream

def _check_duet3_sbc(self, reply, error):
Logger.log("d", "rr_connect failed with error " + str(error))
if error == QNetworkReply.NetworkError.ContentNotFoundError:
Expand Down
33 changes: 31 additions & 2 deletions DuetRRFPlugin.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from io import StringIO
import json

try: # Cura 5
Expand All @@ -8,6 +9,7 @@
from cura.CuraApplication import CuraApplication
from cura.Settings.CuraContainerRegistry import CuraContainerRegistry

from UM.Application import Application
from UM.Message import Message
from UM.Logger import Logger
from UM.Extension import Extension
Expand All @@ -17,15 +19,16 @@
catalog = i18nCatalog("cura")

from .DuetRRFOutputDevice import DuetRRFConfigureOutputDevice, DuetRRFOutputDevice, DuetRRFDeviceType
from .DuetRRFSettings import delete_config, get_config, init_settings, DUETRRF_SETTINGS

from .DuetRRFSettings import get_plugin_version, delete_config, get_config, init_settings, DUETRRF_SETTINGS
from .thumbnails import generate_thumbnail

class DuetRRFPlugin(Extension, OutputDevicePlugin):
def __init__(self):
super().__init__()
self._application = CuraApplication.getInstance()
self._application.globalContainerStackChanged.connect(self._checkDuetRRFOutputDevices)
self._application.initializationFinished.connect(self._delay_check_unmapped_settings)
self._application.getOutputDeviceManager().writeStarted.connect(self._embed_thumbnails)

init_settings()

Expand All @@ -39,6 +42,32 @@ def start(self):
def stop(self, store_data: bool = True):
pass

def _embed_thumbnails(self, output_device) -> None:
# fetch sliced gcode from scene and active build plate
active_build_plate_id = self._application.getMultiBuildPlateModel().activeBuildPlate
scene = Application.getInstance().getController().getScene()
gcode_dict = getattr(scene, "gcode_dict", None)
if not gcode_dict:
return
gcode_list = gcode_dict[active_build_plate_id]
if not gcode_list:
return

if ";Exported with Cura-DuetRRF" not in gcode_list[0]:
# assemble everything and inject custom data
Logger.log("i", "Assembling final gcode file...")

version = get_plugin_version()
thumbnail_stream = generate_thumbnail()
gcode_list[0] += f";Exported with Cura-DuetRRF v{version} plugin by Thomas Kriechbaumer\n"
gcode_list[0] += thumbnail_stream.getvalue()

# store new gcode back into scene and active build plate
gcode_dict[active_build_plate_id] = gcode_list
setattr(scene, "gcode_dict", gcode_dict)
else:
Logger.log("e", "Already embedded thumbnails")

def _delay_check_unmapped_settings(self):
self._change_timer = QTimer()
self._change_timer.setInterval(10000)
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ This button is also a dropdown to choose between **Print**, **Simulate**, or
* Works with HTTP Basic Auth (optional)
* Works with RRF passwords (if you used `M551`, default is `reprap`)
* No support for UNC paths, only IP addresses or resolvable domain names (DNS)
* Embeds thumbnails in QOI format for PanelDue and DWC

## Use

Expand Down
20 changes: 20 additions & 0 deletions helpers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
from io import StringIO
from typing import cast

from UM.Logger import Logger
from UM.Mesh.MeshWriter import MeshWriter
from UM.PluginRegistry import PluginRegistry


def serializing_scene_to_gcode():
# get the gcode through the GCodeWrite plugin
# this serializes the actual scene and should produce the same output as "Save to File"

Logger.log("d", "Serializing gcode...")
gcode_writer = cast(MeshWriter, PluginRegistry.getInstance().getPluginObject("GCodeWriter"))
gcode_stream = StringIO()
success = gcode_writer.write(gcode_stream, None)
if not success:
Logger.log("e", "GCodeWriter failed.")
return None
return gcode_stream

0 comments on commit c103f19

Please sign in to comment.