From 823e3180d2f3897c500d3f2b1e90d30f8062a4ee Mon Sep 17 00:00:00 2001 From: Iskander Sultanov Date: Tue, 5 Mar 2024 18:34:44 +0300 Subject: [PATCH] feat: add webcam function Signed-off-by: Iskander Sultanov --- Constants.py | 5 +++ MKSOutputDevice.py | 29 +++++++++++++ MachineConfig.py | 59 +++++++++++++++++++++++++++ i18n/mksplugin.pot | 17 ++++++++ qml/MachineConfig.qml | 95 +++++++++++++++++++++++++++++++++++++++++++ qml/MonitorItem.qml | 70 +++++++++++++++++++++++++++++++ 6 files changed, 275 insertions(+) diff --git a/Constants.py b/Constants.py index 0e15e9f..2cf5937 100644 --- a/Constants.py +++ b/Constants.py @@ -14,5 +14,10 @@ GIMAGE = "mks_gimage" IS_PREVIEW_ENCODED = "mks_is_preview_encoded" +#camera view parameters (experimental) +IS_CAMERA_VIEW = "mks_is_camera_view" +CAMERA_URL = "mks_camera_url" +CAMERA_VIDEO_ZOOM = "mks_camera_video_zoom" + # Errors EXCEPTION_MESSAGE = "An exception occurred in network connection: %s" diff --git a/MKSOutputDevice.py b/MKSOutputDevice.py index be12775..345a2f9 100644 --- a/MKSOutputDevice.py +++ b/MKSOutputDevice.py @@ -245,6 +245,35 @@ def disconnect(self): def getProperties(self): return self._properties + + #camera view (experimental), begining# + @pyqtProperty(bool) + def isCameraView(self): + global_container_stack = Application.getInstance().getGlobalContainerStack() + if global_container_stack: + meta_data = global_container_stack.getMetaData() + if Constants.IS_CAMERA_VIEW in meta_data: + return True + return False + + @pyqtProperty(str) + def cameraUrl(self): + global_container_stack = Application.getInstance().getGlobalContainerStack() + if global_container_stack: + meta_data = global_container_stack.getMetaData() + if Constants.CAMERA_URL in meta_data: + return global_container_stack.getMetaDataEntry(Constants.CAMERA_URL) + return "" + + @pyqtProperty(float) + def cameraVideoZoom(self): + global_container_stack = Application.getInstance().getGlobalContainerStack() + if global_container_stack: + meta_data = global_container_stack.getMetaData() + if Constants.CAMERA_VIDEO_ZOOM in meta_data: + return float(global_container_stack.getMetaDataEntry(Constants.CAMERA_VIDEO_ZOOM)) + return 1.0 + #camera view (experimental), end# @pyqtSlot(str, result=str) def getProperty(self, key): diff --git a/MachineConfig.py b/MachineConfig.py index dedf557..fe17148 100644 --- a/MachineConfig.py +++ b/MachineConfig.py @@ -66,6 +66,65 @@ def __init__(self, parent=None): printersChanged = pyqtSignal() printersTryToConnect = pyqtSignal() + #camera view (experimental), begining# + @pyqtSlot(result=bool) + def isCameraViewEnabled(self): + global_container_stack = Application.getInstance().getGlobalContainerStack() + if global_container_stack: + meta_data = global_container_stack.getMetaData() + if Constants.IS_CAMERA_VIEW in meta_data: + return True + return False + + @pyqtSlot(str) + def setCameraViewEnable(self, is_camera_view_enabled): + global_container_stack = Application.getInstance().getGlobalContainerStack() + if global_container_stack: + if is_camera_view_enabled == "true": + global_container_stack.setMetaDataEntry(Constants.IS_CAMERA_VIEW, is_camera_view_enabled) + else: + global_container_stack.setMetaDataEntry(Constants.IS_CAMERA_VIEW, None) + global_container_stack.removeMetaDataEntry(Constants.IS_CAMERA_VIEW) + + @pyqtSlot(result=str) + def getCameraUrl(self): + global_container_stack = Application.getInstance().getGlobalContainerStack() + if global_container_stack: + meta_data = global_container_stack.getMetaData() + if Constants.CAMERA_URL in meta_data: + return global_container_stack.getMetaDataEntry(Constants.CAMERA_URL) + return "http://" + + @pyqtSlot(str) + def setCameraUrl(self, camera_url): + global_container_stack = Application.getInstance().getGlobalContainerStack() + if global_container_stack: + if camera_url != "http://": + global_container_stack.setMetaDataEntry(Constants.CAMERA_URL, camera_url) + else: + global_container_stack.setMetaDataEntry(Constants.CAMERA_URL, None) + global_container_stack.removeMetaDataEntry(Constants.CAMERA_URL) + + @pyqtSlot(result=str) + def getCameraVideoZoom(self): + global_container_stack = Application.getInstance().getGlobalContainerStack() + if global_container_stack: + meta_data = global_container_stack.getMetaData() + if Constants.CAMERA_VIDEO_ZOOM in meta_data: + return global_container_stack.getMetaDataEntry(Constants.CAMERA_VIDEO_ZOOM) + return "1.0" + + @pyqtSlot(str) + def setCameraVideoZoom(self, camera_video_zoom): + global_container_stack = Application.getInstance().getGlobalContainerStack() + if global_container_stack: + if camera_video_zoom != "1.0": + global_container_stack.setMetaDataEntry(Constants.CAMERA_VIDEO_ZOOM, camera_video_zoom) + else: + global_container_stack.setMetaDataEntry(Constants.CAMERA_VIDEO_ZOOM, None) + global_container_stack.removeMetaDataEntry(Constants.CAMERA_VIDEO_ZOOM) + #camera view (experimental), end# + @pyqtProperty(str, constant=True) def pluginVersion(self) -> str: return self._plugin_version diff --git a/i18n/mksplugin.pot b/i18n/mksplugin.pot index c850861..1992d20 100644 --- a/i18n/mksplugin.pot +++ b/i18n/mksplugin.pot @@ -275,6 +275,23 @@ msgctxt "@label" msgid "Switch to Monitor Tab after file upload" msgstr "" +#camera view (experimental), begining# +#: qml/MachineConfig.qml:620 +msgctxt "@label" +msgid "Camera view (experimental feature)" +msgstr "" + +#: qml/MachineConfig.qml:646 +msgctxt "@label" +msgid "Camera URL (MJPG-streamer stream URL)" +msgstr "" + +#: qml/MachineConfig.qml:676 +msgctxt "@label" +msgid "Camera video zoom (1.0 by default)" +msgstr "" +#camera view (experimental), end# + #: qml/MachineConfig.qml:631 msgctxt "@label" msgid "MKS WiFi Plugin is active for this printer" diff --git a/qml/MachineConfig.qml b/qml/MachineConfig.qml index df9f97d..c3dd6ad 100644 --- a/qml/MachineConfig.qml +++ b/qml/MachineConfig.qml @@ -604,6 +604,101 @@ Cura.MachineAction { enabled: mksSupport.checked } } + + //camera view (experimental), begining + Row { + anchors + { + left: parent.left + right: parent.right + } + UM.Label { + width: Math.round(parent.width * 0.5) + height: monitorTabAutoOpen.height + verticalAlignment: Text.AlignVCenter + wrapMode: Text.WordWrap + text: catalog.i18nc("@label", "Camera view (experimental feature)") + + enabled: mksSupport.checked + } + UM.CheckBox { + id: isCameraView + checked: manager.isCameraViewEnabled() + + onCheckedChanged: { + manager.setCameraViewEnable(isCameraView.checked) + } + + enabled: mksSupport.checked + } + } + Row { + anchors + { + left: parent.left + right: parent.right + } + UM.Label { + width: Math.round(parent.width * 0.5) + height: monitorTabAutoOpen.height + verticalAlignment: Text.AlignVCenter + wrapMode: Text.WordWrap + text: catalog.i18nc("@label", "Camera URL (MJPG-streamer stream URL)") + + enabled: isCameraView.checked + } + Cura.TextField { + id: cameraUrl + width: Math.round(parent.width * 0.5) - UM.Theme.getSize("default_margin").width + maximumLength: 1024 + validator: RegularExpressionValidator + { + regularExpression: /^((?:http:\/\/)|(?:https:\/\/))\S{0,}$/ + } + + text: manager.getCameraUrl() + + onEditingFinished: { + manager.setCameraUrl(cameraUrl.text) + } + + enabled: isCameraView.checked + } + } + Row { + anchors + { + left: parent.left + right: parent.right + } + UM.Label { + width: Math.round(parent.width * 0.5) + height: monitorTabAutoOpen.height + verticalAlignment: Text.AlignVCenter + wrapMode: Text.WordWrap + text: catalog.i18nc("@label", "Camera video zoom (1.0 by default)") + + enabled: isCameraView.checked + } + Cura.TextField { + id: cameraVideoZoom + width: Math.round(parent.width * 0.5) - UM.Theme.getSize("default_margin").width + maximumLength: 3 + validator: RegularExpressionValidator + { + regularExpression: /^(?:[0-9]).(?:[0-9])$/ + } + + text: manager.getCameraVideoZoom() + + onEditingFinished: { + manager.setCameraVideoZoom(cameraVideoZoom.text) + } + + enabled: isCameraView.checked + } + } + //camera view (experimental), end } } } diff --git a/qml/MonitorItem.qml b/qml/MonitorItem.qml index c0109a3..19f4314 100644 --- a/qml/MonitorItem.qml +++ b/qml/MonitorItem.qml @@ -25,6 +25,76 @@ Component UM.I18nCatalog { id: catalog; name:"mksplugin" } UM.I18nCatalog { id: cura_catalog; name: "cura"} + //camera view (experimental), begining + Rectangle { + Cura.NetworkMJPGImage { + id: cameraImage + + visible: OutputDevice.isCameraView + source: OutputDevice.cameraUrl //MJPG-streamer video stream URL + property real videoZoom: OutputDevice.cameraVideoZoom + + property real videoWidth: Math.floor(imageWidth * videoZoom) + property real videoHeight: Math.floor(imageHeight * videoZoom) + property real modVideoWidth: Math.floor(videoWidth * (parent.height / videoHeight)) + property real modVideoHeight: Math.floor(videoHeight * (parent.width / videoWidth)) + + width: { + if (videoWidth < parent.width) { + if (videoHeight < parent.height) { + return videoWidth + } else { + return modVideoWidth + } + } else { + if (modVideoWidth < parent.width) { + return modVideoWidth + } else { + return parent.width + } + } + } + height: { + if (videoHeight < parent.height) { + if (videoWidth < parent.width) { + return videoHeight + } else { + return modVideoHeight + } + } else { + if (modVideoHeight < parent.height) { + return modVideoHeight + } else { + return parent.height + } + } + } + + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.verticalCenter + + Component.onCompleted: { + if (visible) { + start(); + } + } + onVisibleChanged: { + if (visible) { + start(); + } else { + stop(); + } + } + } + + color: UM.Theme.getColor("detail_background") + anchors.left: parent.left + width: parent.width - (Math.floor(parent.width * 0.35) - ((Math.floor(parent.width * 0.35) % 2) ? 0 : 1)) + anchors.top: parent.top + anchors.bottom: parent.bottom + } + //camera view (experimental), end + Rectangle { color: UM.Theme.getColor("main_background")