diff --git a/plugins/PCBWriter/PCBDialog.py b/plugins/PCBWriter/PCBDialog.py new file mode 100644 index 0000000000..f31c87a61b --- /dev/null +++ b/plugins/PCBWriter/PCBDialog.py @@ -0,0 +1,37 @@ +# Copyright (c) 2024 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. + +from PyQt6.QtCore import pyqtSignal, QObject, pyqtProperty, QCoreApplication, QUrl +from PyQt6.QtGui import QDesktopServices +from typing import List, Optional, Dict, cast + +from cura.Machines.Models.MachineListModel import MachineListModel +from cura.Machines.Models.IntentTranslations import intent_translations +from cura.Settings.GlobalStack import GlobalStack +from UM.Application import Application +from UM.FlameProfiler import pyqtSlot +from UM.i18n import i18nCatalog +from UM.Logger import Logger +from UM.Message import Message +from UM.PluginRegistry import PluginRegistry +from UM.Settings.ContainerRegistry import ContainerRegistry + +import os +import threading +import time + +from cura.CuraApplication import CuraApplication + +i18n_catalog = i18nCatalog("cura") + + +class PCBDialog(QObject): + def __init__(self, parent = None) -> None: + super().__init__(parent) + + plugin_path = os.path.dirname(__file__) + dialog_path = os.path.join(plugin_path, 'PCBDialog.qml') + self._view = CuraApplication.getInstance().createQmlComponent(dialog_path, {"manager": self}) + + def show(self) -> None: + self._view.show() diff --git a/plugins/PCBWriter/PCBDialog.qml b/plugins/PCBWriter/PCBDialog.qml new file mode 100644 index 0000000000..1937c00828 --- /dev/null +++ b/plugins/PCBWriter/PCBDialog.qml @@ -0,0 +1,148 @@ +// Copyright (c) 2024 Ultimaker B.V. +// Cura is released under the terms of the LGPLv3 or higher. + +import QtQuick 2.10 +import QtQuick.Controls 2.3 +import QtQuick.Layouts 1.3 +import QtQuick.Window 2.2 + +import UM 1.5 as UM +import Cura 1.1 as Cura +import PCBWriter 1.0 as PCBWriter + +UM.Dialog +{ + id: workspaceDialog + title: catalog.i18nc("@title:window", "Export pre-configured build batch") + + margin: UM.Theme.getSize("default_margin").width + minimumWidth: UM.Theme.getSize("modal_window_minimum").width + minimumHeight: UM.Theme.getSize("modal_window_minimum").height + + backgroundColor: UM.Theme.getColor("detail_background") + + headerComponent: Rectangle + { + UM.I18nCatalog { id: catalog; name: "cura" } + + height: childrenRect.height + 2 * UM.Theme.getSize("default_margin").height + color: UM.Theme.getColor("main_background") + + ColumnLayout + { + id: headerColumn + + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + anchors.topMargin: UM.Theme.getSize("default_margin").height + anchors.leftMargin: UM.Theme.getSize("default_margin").width + anchors.rightMargin: anchors.leftMargin + + UM.Label + { + id: titleLabel + text: catalog.i18nc("@action:title", "Summary - Pre-configured build batch") + font: UM.Theme.getFont("large") + } + + UM.Label + { + id: descriptionLabel + text: catalog.i18nc("@action:description", "When exporting a build batch, all the models present on the build plate will be included with their current position, orientation and scale. You can also select which per-extruder or per-model settings should be included to ensure a proper printing of the batch, even on different printers.") + font: UM.Theme.getFont("default") + wrapMode: Text.Wrap + Layout.maximumWidth: headerColumn.width + } + } + } + + Rectangle + { + anchors.fill: parent + color: UM.Theme.getColor("main_background") + + PCBWriter.SettingsExportModel{ id: settingsExportModel } + + ListView + { + id: settingsExportList + anchors.fill: parent + anchors.margins: UM.Theme.getSize("default_margin").width + spacing: UM.Theme.getSize("default_margin").height + model: settingsExportModel.settingsGroups + clip: true + + ScrollBar.vertical: UM.ScrollBar { id: verticalScrollBar } + + delegate: SettingsSelectionGroup { Layout.margins: 0 } + } + + // Flickable + // { + // Column + // { + // width: parent.width - scrollbar.width - UM.Theme.getSize("default_margin").width + // height: childrenRect.height + // + // spacing: UM.Theme.getSize("default_margin").height + // leftPadding: UM.Theme.getSize("default_margin").width + // rightPadding: leftPadding + // topPadding: UM.Theme.getSize("default_margin").height + // bottomPadding: topPadding + // } + // } + } + + footerComponent: Rectangle + { + color: warning ? UM.Theme.getColor("warning") : "transparent" + anchors.bottom: parent.bottom + width: parent.width + height: childrenRect.height + (warning ? 2 * workspaceDialog.margin : workspaceDialog.margin) + + Column + { + height: childrenRect.height + spacing: workspaceDialog.margin + + anchors.leftMargin: workspaceDialog.margin + anchors.rightMargin: workspaceDialog.margin + anchors.bottomMargin: workspaceDialog.margin + anchors.topMargin: warning ? workspaceDialog.margin : 0 + + anchors.left: parent.left + anchors.right: parent.right + anchors.top: parent.top + + RowLayout + { + id: warningRow + height: childrenRect.height + visible: warning + spacing: workspaceDialog.margin + UM.ColorImage + { + width: UM.Theme.getSize("extruder_icon").width + height: UM.Theme.getSize("extruder_icon").height + source: UM.Theme.getIcon("Warning") + } + + UM.Label + { + id: warningText + text: catalog.i18nc("@label", "This project contains materials or plugins that are currently not installed in Cura.
Install the missing packages and reopen the project.") + } + } + + Loader + { + width: parent.width + height: childrenRect.height + sourceComponent: buttonRow + } + } + } + + buttonSpacing: UM.Theme.getSize("wide_margin").width +} diff --git a/plugins/PCBWriter/PCBWriter.py b/plugins/PCBWriter/PCBWriter.py new file mode 100644 index 0000000000..6391493ae3 --- /dev/null +++ b/plugins/PCBWriter/PCBWriter.py @@ -0,0 +1,67 @@ +# Copyright (c) 2024 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. +import json +import re + +from threading import Lock + +from typing import Optional, cast, List, Dict, Pattern, Set + +from UM.Mesh.MeshWriter import MeshWriter +from UM.Math.Vector import Vector +from UM.Logger import Logger +from UM.Math.Matrix import Matrix +from UM.Application import Application +from UM.Message import Message +from UM.Resources import Resources +from UM.Scene.SceneNode import SceneNode +from UM.Settings.ContainerRegistry import ContainerRegistry +from UM.Settings.EmptyInstanceContainer import EmptyInstanceContainer +from PyQt6.QtQml import qmlRegisterType + +from cura.CuraApplication import CuraApplication +from cura.CuraPackageManager import CuraPackageManager +from cura.Settings import CuraContainerStack +from cura.Utils.Threading import call_on_qt_thread +from cura.Snapshot import Snapshot + +from PyQt6.QtCore import QBuffer + +import pySavitar as Savitar + +import numpy +import datetime + +import zipfile +import UM.Application + +from .PCBDialog import PCBDialog +from .SettingsExportModel import SettingsExportModel +from .SettingsExportGroup import SettingsExportGroup + +from UM.i18n import i18nCatalog +catalog = i18nCatalog("cura") + +class PCBWriter(MeshWriter): + def __init__(self): + super().__init__() + + qmlRegisterType(SettingsExportModel, "PCBWriter", 1, 0, "SettingsExportModel") + qmlRegisterType(SettingsExportGroup, "PCBWriter", 1, 0, "SettingsExportGroup") + #qmlRegisterUncreatableType(SettingsExportGroup.Category, "PCBWriter", 1, 0, "SettingsExportGroup.Category") + + self._config_dialog = None + self._main_thread_lock = Lock() + + def write(self, stream, nodes, mode = MeshWriter.OutputMode.BinaryMode) -> bool: + self._main_thread_lock.acquire() + # Start configuration window in main application thread + CuraApplication.getInstance().callLater(self._write, stream, nodes, mode) + self._main_thread_lock.acquire() # Block until lock has been released, meaning the config is over + + self._main_thread_lock.release() + return True + + def _write(self, stream, nodes, mode): + self._config_dialog = PCBDialog() + self._config_dialog.show() diff --git a/plugins/PCBWriter/SettingExport.py b/plugins/PCBWriter/SettingExport.py new file mode 100644 index 0000000000..901dcdc804 --- /dev/null +++ b/plugins/PCBWriter/SettingExport.py @@ -0,0 +1,24 @@ +# Copyright (c) 2024 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. + +from PyQt6.QtCore import Qt, QObject, pyqtProperty +from UM.FlameProfiler import pyqtSlot +from UM.Application import Application +from UM.Qt.ListModel import ListModel +from UM.Logger import Logger + + +class SettingsExport(): + + def __init__(self): + super().__init__() + self._name = "Generate Support" + self._value = "Enabled" + + @pyqtProperty(str, constant=True) + def name(self): + return self._name + + @pyqtProperty(str, constant=True) + def value(self): + return self._value diff --git a/plugins/PCBWriter/SettingsExportGroup.py b/plugins/PCBWriter/SettingsExportGroup.py new file mode 100644 index 0000000000..355b2347f6 --- /dev/null +++ b/plugins/PCBWriter/SettingsExportGroup.py @@ -0,0 +1,59 @@ +# Copyright (c) 2024 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. + +from enum import IntEnum + +from PyQt6.QtCore import Qt, QObject, pyqtProperty, pyqtEnum +from UM.FlameProfiler import pyqtSlot +from UM.Application import Application +from UM.Qt.ListModel import ListModel +from UM.Logger import Logger + +from .SettingExport import SettingsExport + + +class SettingsExportGroup(QObject): + + @pyqtEnum + class Category(IntEnum): + Global = 0 + Extruder = 1 + Model = 2 + + def __init__(self, name, category, category_details = '', extruder_index = 0, extruder_color = ''): + super().__init__() + self._name = name + self._settings = [] + self._category = category + self._category_details = category_details + self._extruder_index = extruder_index + self._extruder_color = extruder_color + self._updateSettings() + + @pyqtProperty(str, constant=True) + def name(self): + return self._name + + @pyqtProperty(list, constant=True) + def settings(self): + return self._settings + + @pyqtProperty(int, constant=True) + def category(self): + return self._category + + @pyqtProperty(str, constant=True) + def category_details(self): + return self._category_details + + @pyqtProperty(int, constant=True) + def extruder_index(self): + return self._extruder_index + + @pyqtProperty(str, constant=True) + def extruder_color(self): + return self._extruder_color + + def _updateSettings(self): + self._settings.append(SettingsExport()) + self._settings.append(SettingsExport()) \ No newline at end of file diff --git a/plugins/PCBWriter/SettingsExportModel.py b/plugins/PCBWriter/SettingsExportModel.py new file mode 100644 index 0000000000..3351e22b59 --- /dev/null +++ b/plugins/PCBWriter/SettingsExportModel.py @@ -0,0 +1,31 @@ +# Copyright (c) 2024 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. + +from PyQt6.QtCore import QObject, Qt, pyqtProperty +from UM.FlameProfiler import pyqtSlot +from UM.Application import Application +from UM.Qt.ListModel import ListModel +from UM.Logger import Logger + +from .SettingsExportGroup import SettingsExportGroup + + +class SettingsExportModel(QObject): + + def __init__(self, parent = None): + super().__init__(parent) + self._settingsGroups = [] + self._updateSettingsExportGroups() + + @pyqtProperty(list, constant=True) + def settingsGroups(self): + return self._settingsGroups + + def _updateSettingsExportGroups(self): + self._settingsGroups.append(SettingsExportGroup("Global settings", SettingsExportGroup.Category.Global)) + self._settingsGroups.append(SettingsExportGroup("Extruder settings", SettingsExportGroup.Category.Extruder, extruder_index=1, extruder_color='#ff0000')) + self._settingsGroups.append(SettingsExportGroup("Extruder settings", SettingsExportGroup.Category.Extruder, extruder_index=8, extruder_color='#008fff')) + self._settingsGroups.append(SettingsExportGroup("Model settings", + SettingsExportGroup.Category.Model, 'hypercube.stl')) + self._settingsGroups.append(SettingsExportGroup("Model settings", + SettingsExportGroup.Category.Model, 'homer-simpson.stl')) diff --git a/plugins/PCBWriter/SettingsSelectionGroup.qml b/plugins/PCBWriter/SettingsSelectionGroup.qml new file mode 100644 index 0000000000..12829c96d4 --- /dev/null +++ b/plugins/PCBWriter/SettingsSelectionGroup.qml @@ -0,0 +1,70 @@ +// Copyright (c) 2024 Ultimaker B.V. +// Cura is released under the terms of the LGPLv3 or higher. + +import QtQuick 2.10 +import QtQuick.Controls 2.3 +import QtQuick.Layouts 1.3 +import QtQuick.Window 2.2 + +import UM 1.5 as UM +import Cura 1.1 as Cura +import PCBWriter 1.0 as PCBWriter + +Column +{ + id: settingsGroup + + UM.I18nCatalog { id: catalog; name: "cura" } + + Row + { + id: settingsGroupTitleRow + spacing: UM.Theme.getSize("default_margin").width + + Item + { + id: icon + anchors.verticalCenter: parent.verticalCenter + height: UM.Theme.getSize("medium_button_icon").height + width: height + + UM.ColorImage + { + id: settingsMainImage + anchors.fill: parent + source: + { + switch(modelData.category) + { + case PCBWriter.SettingsExportGroup.Global: + return UM.Theme.getIcon("Sliders") + case PCBWriter.SettingsExportGroup.Model: + return UM.Theme.getIcon("View3D") + default: + return "" + } + } + + color: UM.Theme.getColor("text") + } + + Cura.ExtruderIcon + { + id: settingsExtruderIcon + anchors.fill: parent + visible: modelData.category === PCBWriter.SettingsExportGroup.Extruder + text: (modelData.extruder_index + 1).toString() + font: UM.Theme.getFont("tiny_emphasis") + materialColor: modelData.extruder_color + } + } + + UM.Label + { + id: settingsTitle + text: modelData.name + (modelData.category_details ? ' (%1)'.arg(modelData.category_details) : '') + anchors.verticalCenter: parent.verticalCenter + font: UM.Theme.getFont("default_bold") + } + } +} diff --git a/plugins/PCBWriter/__init__.py b/plugins/PCBWriter/__init__.py new file mode 100644 index 0000000000..3ec2eba95f --- /dev/null +++ b/plugins/PCBWriter/__init__.py @@ -0,0 +1,21 @@ +# Copyright (c) 2024 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. +import sys + +from . import PCBWriter +from UM.i18n import i18nCatalog + +i18n_catalog = i18nCatalog("cura") + +def getMetaData(): + return {"mesh_writer": { + "output": [{ + "extension": "pcb", + "description": i18n_catalog.i18nc("@item:inlistbox", "Pre-Configured Batch file"), + "mime_type": "application/vnd.um.preconfigured-batch+3mf", + "mode": PCBWriter.PCBWriter.OutputMode.BinaryMode + }] + }} + +def register(app): + return {"mesh_writer": PCBWriter.PCBWriter() } diff --git a/plugins/PCBWriter/plugin.json b/plugins/PCBWriter/plugin.json new file mode 100644 index 0000000000..6571185779 --- /dev/null +++ b/plugins/PCBWriter/plugin.json @@ -0,0 +1,8 @@ +{ + "name": "Pre-Configured Batch Writer", + "author": "Ultimaker B.V.", + "version": "1.0.0", + "description": "Provides support for writing Pre-Configured Batch files.", + "api": 8, + "i18n-catalog": "cura" +} diff --git a/resources/qml/ExtruderIcon.qml b/resources/qml/ExtruderIcon.qml index 3231d924ee..bd15df7848 100644 --- a/resources/qml/ExtruderIcon.qml +++ b/resources/qml/ExtruderIcon.qml @@ -15,6 +15,7 @@ Item property int iconSize: UM.Theme.getSize("extruder_icon").width property string iconVariant: "medium" property alias font: extruderNumberText.font + property alias text: extruderNumberText.text implicitWidth: iconSize implicitHeight: iconSize