Skip to content

Commit

Permalink
refactor thumbnail generation and embedding
Browse files Browse the repository at this point in the history
closes #52, closes #53
  • Loading branch information
Kriechi committed May 26, 2024
1 parent bc796a2 commit fafa99f
Show file tree
Hide file tree
Showing 7 changed files with 147 additions and 119 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Changelog of Cura-DuetRRFPlugin

## v1.2.11: 2024-XX-XX
* fix thumbnail generation on Cura 5.7+
* allow disabling of thumbnail embedding (disabled by default for non-Duet-RRF printers)
* allow custom thumbnail sizes, defaulting to 48x48, 240x240, 320x320

## v1.2.10: 2024-01-20
* fix HTTP Basic Auth
* bump compatibility for Cura 5.6 / API 8.6
Expand Down
30 changes: 26 additions & 4 deletions DuetRRFAction.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
from UM.i18n import i18nCatalog
catalog = i18nCatalog("cura")

from .DuetRRFSettings import delete_config, get_config, save_config
from .DuetRRFSettings import DEFAULT_THUMBNAIL_SIZES_STR, delete_config, get_config, save_config


class DuetRRFAction(MachineAction):
Expand All @@ -38,6 +38,8 @@ def _onGlobalContainerStackChanged(self) -> None:
self.printerSettingsDuetPasswordChanged.emit()
self.printerSettingsHTTPUserChanged.emit()
self.printerSettingsHTTPPasswordChanged.emit()
self.printerSettingsEmbedThumbnailsChanged.emit()
self.printerSettingsThumbnailSizesChanged.emit()

def _onContainerAdded(self, container: "ContainerInterface") -> None:
# Add this action as a supported action to all machine definitions
Expand All @@ -53,11 +55,16 @@ def _reset(self) -> None:
self.printerSettingsDuetPasswordChanged.emit()
self.printerSettingsHTTPUserChanged.emit()
self.printerSettingsHTTPPasswordChanged.emit()
self.printerSettingsEmbedThumbnailsChanged.emit()
self.printerSettingsThumbnailSizesChanged.emit()


printerSettingsUrlChanged = pyqtSignal()
printerSettingsDuetPasswordChanged = pyqtSignal()
printerSettingsHTTPUserChanged = pyqtSignal()
printerSettingsHTTPPasswordChanged = pyqtSignal()
printerSettingsEmbedThumbnailsChanged = pyqtSignal()
printerSettingsThumbnailSizesChanged = pyqtSignal()

@pyqtProperty(str, notify=printerSettingsUrlChanged)
def printerSettingUrl(self) -> Optional[str]:
Expand Down Expand Up @@ -87,12 +94,27 @@ def printerSettingHTTPPassword(self) -> Optional[str]:
return s["http_password"]
return ""

@pyqtSlot(str, str, str, str)
def saveConfig(self, url, duet_password, http_user, http_password):
@pyqtProperty(bool, notify=printerSettingsEmbedThumbnailsChanged)
def printerSettingEmbedThumbnails(self) -> Optional[bool]:
s = get_config()
if s:
return s["embed_thumbnails"]
return True

@pyqtProperty(str, notify=printerSettingsThumbnailSizesChanged)
def printerSettingThumbnailSizes(self) -> Optional[str]:
s = get_config()
if s:
return s["thumbnail_sizes"]
return DEFAULT_THUMBNAIL_SIZES_STR

@pyqtSlot(str, str, str, str, bool, str)
def saveConfig(self, url, duet_password, http_user, http_password, embed_thumbnails, thumbnail_sizes):
if not url.endswith('/'):
url += '/'

save_config(url, duet_password, http_user, http_password)
Logger.log("d", f"saving config: {url=}, {duet_password=}, {http_user=}, {http_password=}, {embed_thumbnails=}, {thumbnail_sizes=}")
save_config(url, duet_password, http_user, http_password, embed_thumbnails, thumbnail_sizes)
Logger.log("d", "config saved")

# trigger a stack change to reload the output devices
Expand Down
17 changes: 7 additions & 10 deletions DuetRRFOutputDevice.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@
import urllib
import json
import base64
from io import StringIO
from typing import cast
from enum import Enum

try: # Cura 5
Expand All @@ -27,7 +25,6 @@
from UM.i18n import i18nCatalog
catalog = i18nCatalog("cura")

from . import DuetRRFSettings
from .helpers import serializing_scene_to_gcode

class OutputStage(Enum):
Expand Down Expand Up @@ -66,14 +63,14 @@ def requestWrite(self, node, fileName=None, *args, **kwargs):


class DuetRRFOutputDevice(OutputDevice):
def __init__(self, settings, device_type):
def __init__(self, config, device_type):
self._name_id = "duetrrf-{}".format(device_type.name)
super().__init__(self._name_id)

self._url = settings["url"]
self._duet_password = settings["duet_password"]
self._http_user = settings["http_user"]
self._http_password = settings["http_password"]
self._url = config["url"]
self._duet_password = config["duet_password"]
self._http_user = config["http_user"]
self._http_password = config["http_password"]

self.application = CuraApplication.getInstance()
global_container_stack = self.application.getGlobalContainerStack()
Expand Down Expand Up @@ -301,7 +298,7 @@ def _onUploadDone(self, reply):
self._message.show()

gcode='M37 P"0:/gcodes/' + self._fileName + '"'
Logger.log("d", "Sending gcode:" + gcode)
Logger.log("d", "Sending gcode: " + gcode)
if self._use_rrf_http_api:
self._send('rr_gcode',
query=[("gcode", gcode)],
Expand Down Expand Up @@ -340,7 +337,7 @@ def _onReadyToPrint(self):
Logger.log("d", "Ready to print")

gcode = 'M32 "0:/gcodes/' + self._fileName + '"'
Logger.log("d", "Sending gcode:" + gcode)
Logger.log("d", "Sending gcode: " + gcode)
if self._use_rrf_http_api:
self._send('rr_gcode',
query=[("gcode", gcode)],
Expand Down
19 changes: 10 additions & 9 deletions DuetRRFPlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ def stop(self, store_data: bool = True):
pass

def _embed_thumbnails(self, output_device) -> None:
if not get_config().get("embed_thumbnails", False):
Logger.log("d", f"Skipping disabled thumbnail embedding or not a Duet-RRF printer.")
return

# fetch sliced gcode from scene and active build plate
active_build_plate_id = self._application.getMultiBuildPlateModel().activeBuildPlate
scene = Application.getInstance().getController().getScene()
Expand Down Expand Up @@ -181,15 +185,12 @@ def _checkDuetRRFOutputDevices(self):
manager.removeOutputDevice("duetrrf-upload")

# check and load new output devices
s = get_config()
if s:
Logger.log("d", "DuetRRF is active for printer: id:{}, name:{}".format(
global_container_stack.getId(),
global_container_stack.getName(),
))
manager.addOutputDevice(DuetRRFOutputDevice(s, DuetRRFDeviceType.print))
manager.addOutputDevice(DuetRRFOutputDevice(s, DuetRRFDeviceType.simulate))
manager.addOutputDevice(DuetRRFOutputDevice(s, DuetRRFDeviceType.upload))
config = get_config()
if config:
Logger.log("d", f"DuetRRF is active for printer: id:{global_container_stack.getId()}, name:{global_container_stack.getName(),}, config:{config}")
manager.addOutputDevice(DuetRRFOutputDevice(config, DuetRRFDeviceType.print))
manager.addOutputDevice(DuetRRFOutputDevice(config, DuetRRFDeviceType.simulate))
manager.addOutputDevice(DuetRRFOutputDevice(config, DuetRRFDeviceType.upload))
else:
manager.addOutputDevice(DuetRRFConfigureOutputDevice())
Logger.log("d", "DuetRRF is not available for printer: id:{}, name:{}".format(
Expand Down
46 changes: 42 additions & 4 deletions DuetRRFSettings.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,35 @@
import json
import os

from UM.Logger import Logger

from cura.CuraApplication import CuraApplication

DUETRRF_SETTINGS = "duetrrf/instances"

# PanelDue firmware v3.5.0:
# ref https://forum.duet3d.com/post/270550 and https://forum.duet3d.com/post/270553

# Display X = 480
# >>> rowTextHeight = 21 ; 7 * rowTextHeight + (2 * rowTextHeight) / 3
# 161.0
# >>> margin = 2 ; fullPopupWidth = 480 - (2 * margin) ; fileInfoPopupWidth = fullPopupWidth - (4 * margin) ; fileInfoPopupWidth / 3 + 5;
# 161.0

# Display X = 800
# >>> rowTextHeight = 32 ; 7 * rowTextHeight + (2 * rowTextHeight) / 3
# 245.33333333333334
# >>> margin = 4 ; fullPopupWidth = 800 - (2 * margin) ; fileInfoPopupWidth = fullPopupWidth - (4 * margin) ; fileInfoPopupWidth / 3 + 5;
# 263.6666666666667

DEFAULT_THUMBNAIL_SIZES = [
(48, 48), # DuetWebControl Job File List table icon
# (160, 160), # PanelDue 4.3" (not supporting thumbnails currently, DISPLAY_X=480)
(240, 240), # PanelDue 5" and 7" (both have DISPLAY_X=800)
(320, 320), # DuetWebContol Job File List expanded hover image (largest available thumbnail gets chosen)
]
DEFAULT_THUMBNAIL_SIZES_STR = ",".join([f"{w}x{h}" for w, h in DEFAULT_THUMBNAIL_SIZES])

def _load_prefs():
application = CuraApplication.getInstance()
global_container_stack = application.getGlobalContainerStack()
Expand All @@ -20,24 +45,37 @@ def init_settings():
p = application.getPreferences()
p.addPreference(DUETRRF_SETTINGS, json.dumps({}))

def get_config():
def get_config() -> dict:
s, printer_id = _load_prefs()
if printer_id in s:
return s[printer_id]
config = s[printer_id]

# migrate to the latest default values
return save_config(
url=config.get("url", ""),
duet_password=config.get("duet_password", ""),
http_user=config.get("http_user", ""),
http_password=config.get("http_password", ""),
embed_thumbnails=config.get("embed_thumbnails", True),
thumbnail_sizes=config.get("thumbnail_sizes", DEFAULT_THUMBNAIL_SIZES_STR),
)

return {}

def save_config(url, duet_password, http_user, http_password):
def save_config(url: str, duet_password: str, http_user: str, http_password: str, embed_thumbnails: bool, thumbnail_sizes: str):
s, printer_id = _load_prefs()
s[printer_id] = {
"url": url,
"duet_password": duet_password,
"http_user": http_user,
"http_password": http_password,
"embed_thumbnails": embed_thumbnails,
"thumbnail_sizes": thumbnail_sizes,
}
application = CuraApplication.getInstance()
p = application.getPreferences()
p.setValue(DUETRRF_SETTINGS, json.dumps(s))
return s
return s[printer_id]

def delete_config(printer_id=None):
s, active_printer_id = _load_prefs()
Expand Down
23 changes: 21 additions & 2 deletions resources/qml/DuetRRFAction.qml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ Cura.MachineAction

Component.onCompleted: {
actionDialog.minimumWidth = screenScaleFactor * 500;
actionDialog.minimumHeight = screenScaleFactor * 325;
actionDialog.minimumHeight = screenScaleFactor * 425;
}

Column {
Expand Down Expand Up @@ -88,6 +88,25 @@ Cura.MachineAction
anchors.right: parent.right
}

CheckBox {
id: embed_thumbnailsField
text: catalog.i18nc("@label", "Embed thumbnails into generated gcode file")
checked: manager.printerSettingEmbedThumbnails
anchors.left: parent.left
}

UM.Label {
text: catalog.i18nc("@label", "Thumbnail sizes (comma-separated WxH, e.g., 48x48,240x240,320x320)")
}
TextField {
id: thumbnail_sizesField
text: manager.printerSettingThumbnailSizes
selectByMouse: true
maximumLength: 1024
anchors.left: parent.left
anchors.right: parent.right
}

Item {
width: errorMsgLabel.implicitWidth
height: errorMsgLabel.implicitHeight
Expand All @@ -109,7 +128,7 @@ Cura.MachineAction
id: saveButton
text: catalog.i18nc("@action:button", "Save Config")
onClicked: {
manager.saveConfig(urlField.text, duet_passwordField.text, http_userField.text, http_passwordField.text)
manager.saveConfig(urlField.text, duet_passwordField.text, http_userField.text, http_passwordField.text, embed_thumbnailsField.checked, thumbnail_sizesField.text)
actionDialog.reject()
}
enabled: base.validUrl
Expand Down
Loading

0 comments on commit fafa99f

Please sign in to comment.