diff --git a/plugins/MonitorStage/GrblController.py b/plugins/MonitorStage/GrblController.py new file mode 100644 index 0000000000..816ce039e8 --- /dev/null +++ b/plugins/MonitorStage/GrblController.py @@ -0,0 +1,241 @@ +import time +import serial +import serial.tools.list_ports +from UM.Logger import Logger +from PyQt6.QtCore import QObject, pyqtSignal + +class GrblController(QObject): + connection_status_changed = pyqtSignal(bool) + data_received = pyqtSignal(str) + + MAX_BUFFER_SIZE = 20 + SERIAL_TIMEOUT = 5 # seconds + MAX_TEMP_OFFSET = 5 # degrees Celsius + JOG_DISTANCE = 40 # mm + RECONNECT_INTERVAL = 5 # seconds + + def __init__(self): # No printer_output_device + super().__init__() + self._serial_port = None # New: Serial port object + self.is_connected = False + self.last_reconnect_attempt = 0 + self._received_data_buffer = [] + self.target_temperatures = {"T0": 20, "T1": 20} + self.current_temperatures = {"T0": 0.0, "T1": 0.0} + Logger.log("d", "GrblController initialized.") + + def connect(self): + Logger.log("d", "Attempting to connect to GRBL device via serial port.") + if self._serial_port and self._serial_port.is_open: + Logger.log("d", "Already connected to a serial port.") + self.is_connected = True + self.connection_status_changed.emit(self.is_connected) + return + + ports = serial.tools.list_ports.comports() + if not ports: + Logger.log("w", "No serial ports found.") + self.is_connected = False + self.connection_status_changed.emit(self.is_connected) + return + + for p in ports: + Logger.log("d", f"Found port: {p.device}") + try: + self._serial_port = serial.Serial(p.device, 115200, timeout=1) # 115200 baud rate, 1 second timeout + time.sleep(2) # Wait for GRBL to initialize + self._serial_port.flushInput() # Clear input buffer + Logger.log("d", f"Successfully connected to {p.device}") + self.is_connected = True + break + except serial.SerialException as e: + Logger.log("e", f"Failed to connect to {p.device}: {e}") + self.is_connected = False + + if self.is_connected: + Logger.log("d", "GrblController connected.") + # Send GRBL initialization commands and wait for 'ok' + self.send_gcode("$22=0") # disable homing + self.wait_for_ok() + self.send_gcode("$X") + self.wait_for_ok() + self.send_gcode("$21=0") + self.wait_for_ok() + self.send_gcode("$3=4") # invert Z axis + self.wait_for_ok() + self.send_gcode("G21") # mm + self.wait_for_ok() + self.send_gcode("G90") # absolute coords + self.wait_for_ok() + else: + Logger.log("w", "Could not establish a serial connection to any GRBL device.") + + self.connection_status_changed.emit(self.is_connected) + + def send_gcode(self, command): + if self.is_connected and self._serial_port and self._serial_port.is_open: + Logger.log("d", f"Sending G-code: {command.strip()}") + try: + self._serial_port.write(f"{command.strip()}\r\n".encode('utf-8')) + except serial.SerialException as e: + Logger.log("e", f"Failed to send G-code: {e}") + self.is_connected = False + self.connection_status_changed.emit(self.is_connected) + else: + Logger.log("w", f"Cannot send G-code, GrblController not connected: {command.strip()}") + + def get_connection_status(self): + return self.is_connected + + def wait_for_ok(self): + Logger.log("d", "Waiting for 'ok' from printer...") + t_0 = time.time() + while time.time() - t_0 < self.SERIAL_TIMEOUT: + # Read new data from serial port + try: + while self._serial_port and self._serial_port.in_waiting: + line = self._serial_port.readline().decode('utf-8').strip() + if line: + Logger.log("d", f"Data received from serial: {line}") + self._received_data_buffer.append(line) + self.data_received.emit(line) + except serial.SerialException as e: + Logger.log("e", f"Error reading from serial port: {e}") + self.is_connected = False + self.connection_status_changed.emit(self.is_connected) + return False + + # Check buffer for 'ok' + for i, line in enumerate(self._received_data_buffer): + if "ok" in line: + del self._received_data_buffer[:i+1] # Clear processed data + Logger.log("d", "Received 'ok'.") + return True + time.sleep(0.01) # Small delay to prevent busy-waiting + Logger.log("w", "Timeout waiting for 'ok'.") + return False + + def update(self): + if not self.is_connected: + if time.time() - self.last_reconnect_attempt > self.RECONNECT_INTERVAL: + self.last_reconnect_attempt = time.time() + self.connect() + return + + # Read data from serial port + try: + while self._serial_port and self._serial_port.in_waiting: + line = self._serial_port.readline().decode('utf-8').strip() + if line: + Logger.log("d", f"Data received from serial: {line}") + self._received_data_buffer.append(line) + self.data_received.emit(line) + except serial.SerialException as e: + Logger.log("e", f"Error reading from serial port: {e}") + self.is_connected = False + self.connection_status_changed.emit(self.is_connected) + return + + # Process received data for 'ok' and temperature + # This part remains largely the same, but now it's processing data from the serial port directly. + # Request temperatures + self.send_gcode("M105") + + t_0 = time.time() + while time.time() - t_0 < self.SERIAL_TIMEOUT: + for i, line in enumerate(self._received_data_buffer): + if "ok" in line: # Check for 'ok' in the buffer + del self._received_data_buffer[:i+1] + Logger.log("d", "Received 'ok'.") + # If 'ok' is received, we can proceed with other commands or just return + # For now, we'll continue to check for M105 response in the same loop + pass + if "$M105=" in line: + # Parse tuple response: $M105=T0:200.000,T1:180.000 + payload = line.split("=")[-1].strip() + pairs = payload.split(",") + for pair in pairs: + tool, val = pair.split(":") + self.current_temperatures[tool] = float(val) + del self._received_data_buffer[:i+1] + Logger.log("d", f"Received temperature data: {payload}") + return + time.sleep(0.01) + Logger.log("w", "Timeout waiting for M105 response.") + + def set_heating(self, tool: str, temperature: int): + """Set target temperature for a specific tool (e.g., T0, T1).""" + self.target_temperatures[tool] = temperature + # Send full tuple (all tools), so firmware always has consistent state + cmd_parts = [f"{t}:{temp}" for t, temp in self.target_temperatures.items()] + cmd = ",".join(cmd_parts) + self.send_gcode(f"M104 {cmd}") + self.wait_for_ok() + + def handle(self, event): + if not self.is_connected: + return + try: + # Placeholder for event classes + class UpdateTargetTemperature: + def __init__(self, tool, temperature): + self.tool = tool + self.temperature = temperature + class PlayGcode: pass + class PauseGcode: pass + class Home: pass + class NewGcodeFile: + def __init__(self, filename): + self.filename = filename + class Jog: + def __init__(self, movement): + self.movement = movement + + if isinstance(event, UpdateTargetTemperature): + self.set_heating(event.tool, event.temperature) + elif isinstance(event, PlayGcode): + # self.gcode_handler.play() # GcodeHandler not yet implemented + Logger.log("w", "PlayGcode event received, but GcodeHandler is not implemented.") + elif isinstance(event, PauseGcode): + # self.gcode_handler.pause() # GcodeHandler not yet implemented + Logger.log("w", "PauseGcode event received, but GcodeHandler is not implemented.") + elif isinstance(event, Home): + self.send_gcode("$H") + self.wait_for_ok() + elif isinstance(event, NewGcodeFile): + # self.set_gcode_file(event.filename) # set_gcode_file not yet fully implemented + Logger.log("w", f"NewGcodeFile event received for {event.filename}, but set_gcode_file is not fully implemented.") + elif isinstance(event, Jog): + self.send_gcode("G91") # relative coords + self.wait_for_ok() + movement = event.movement + command = ( + f"G1 X{movement[0]*self.JOG_DISTANCE} " + f"Y{movement[1]*self.JOG_DISTANCE} " + f"Z{movement[2]*self.JOG_DISTANCE} " + f"E{movement[3]*self.JOG_DISTANCE + 7} " + f"B{movement[3]*self.JOG_DISTANCE + 7} " + "F200" + ) + self.send_gcode(command) + self.wait_for_ok() + self.send_gcode("G90") # absolute coords + self.wait_for_ok() + else: + Logger.log("w", "Event not caught: " + str(event)) + except Exception as e: # Catch all exceptions for now + Logger.log("e", f"Error handling event: {e}") + self.is_connected = False + # self.register_event(events.ArduinoDisconnected()) # events not implemented + self.connection_status_changed.emit(self.is_connected) + + def get_aprox_buffer(self): + Logger.log("w", "get_aprox_buffer method is a placeholder and not fully implemented for Cura's active printer.") + # In a real implementation, this would send a command and parse the response + # For now, return a default value + return 10 # A reasonable default buffer size + + def set_gcode_file(self, filename: str): + Logger.log("w", f"set_gcode_file method is a placeholder and not fully implemented for Cura's active printer. Filename: {filename}") + # This would typically involve loading a G-code file and preparing it for printing + pass \ No newline at end of file diff --git a/plugins/MonitorStage/MonitorMain.qml b/plugins/MonitorStage/MonitorMain.qml index a89938530c..8257a1c9bf 100644 --- a/plugins/MonitorStage/MonitorMain.qml +++ b/plugins/MonitorStage/MonitorMain.qml @@ -1,44 +1,17 @@ -// Copyright (c) 2022 Ultimaker B.V. -// Cura is released under the terms of the LGPLv3 or higher. - import QtQuick 2.10 import QtQuick.Controls 2.0 import UM 1.5 as UM -import Cura 1.0 as Cura +import Cura 1.1 as Cura -// We show a nice overlay on the 3D viewer when the current output device has no monitor view Rectangle { id: viewportOverlay - property bool isConnected: Cura.MachineManager.activeMachineHasNetworkConnection || Cura.MachineManager.activeMachineHasCloudConnection - property bool isNetworkConfigurable: - { - if(Cura.MachineManager.activeMachine === null) - { - return false - } - return Cura.MachineManager.activeMachine.supportsNetworkConnection - } + property var machineManager: Cura.MachineManager + property var activeMachine: machineManager.activeMachine + property bool isMachineConnected: activeMachine ? activeMachine.is_connected : false - property bool isNetworkConfigured: - { - // Readability: - var connectedTypes = [2, 3]; - var types = Cura.MachineManager.activeMachine.configuredConnectionTypes - - // Check if configured connection types includes either 2 or 3 (LAN or cloud) - for (var i = 0; i < types.length; i++) - { - if (connectedTypes.indexOf(types[i]) >= 0) - { - return true - } - } - return false - } - - color: UM.Theme.getColor("viewport_overlay") + color: "#FAFAFA" anchors.fill: parent UM.I18nCatalog @@ -47,124 +20,55 @@ Rectangle name: "cura" } - // This mouse area is to prevent mouse clicks to be passed onto the scene. - MouseArea - { - anchors.fill: parent - acceptedButtons: Qt.AllButtons - onWheel: wheel.accepted = true - } - - // Disable dropping files into Cura when the monitor page is active - DropArea - { - anchors.fill: parent - } - - // CASE 1: CAN MONITOR & CONNECTED - Loader - { - id: monitorViewComponent - - anchors.fill: parent - - height: parent.height - - property real maximumWidth: parent.width - property real maximumHeight: parent.height - - sourceComponent: Cura.MachineManager.printerOutputDevices.length > 0 ? Cura.MachineManager.printerOutputDevices[0].monitorItem : null - } - - // CASE 2 & 3: Empty states Column { - anchors - { - top: parent.top - topMargin: UM.Theme.getSize("monitor_empty_state_offset").height - horizontalCenter: parent.horizontalCenter - } - width: UM.Theme.getSize("monitor_empty_state_size").width - spacing: UM.Theme.getSize("default_margin").height - visible: monitorViewComponent.sourceComponent == null + anchors.centerIn: parent + spacing: 10 - // CASE 2: CAN MONITOR & NOT CONNECTED - UM.Label + // Graph Placeholder + Rectangle { - anchors + width: 400 + height: 200 + color: "gray" + Text { - horizontalCenter: parent.horizontalCenter + anchors.centerIn: parent + text: "Graph Placeholder" + color: "white" + font.pointSize: 18 } - visible: isNetworkConfigured && !isConnected - text: catalog.i18nc("@info", "Please make sure your printer has a connection:\n- Check if the printer is turned on.\n- Check if the printer is connected to the network.\n- Check if you are signed in to discover cloud-connected printers.") - font: UM.Theme.getFont("medium") - width: contentWidth } - UM.Label + // Command Buttons + Column { - id: noNetworkLabel - anchors - { - horizontalCenter: parent.horizontalCenter + spacing: 5 + Text { text: "Command Buttons"; color: "white"; font.pointSize: 16 } + Row { + spacing: 5 + Button { text: "Z+" } + Button { text: "Z-" } + } + Row { + spacing: 5 + Button { text: "X+" } + Button { text: "X-" } + } + Row { + spacing: 5 + Button { text: "Y+" } + Button { text: "Y-" } } - visible: !isNetworkConfigured && isNetworkConfigurable - text: catalog.i18nc("@info", "Please connect your printer to the network.") - font: UM.Theme.getFont("medium") - width: contentWidth } - Item + // Run Buttons + Row { - anchors - { - left: noNetworkLabel.left - } - visible: !isNetworkConfigured && isNetworkConfigurable - width: childrenRect.width - height: childrenRect.height - - UM.ColorImage - { - id: externalLinkIcon - anchors.verticalCenter: parent.verticalCenter - color: UM.Theme.getColor("text_link") - source: UM.Theme.getIcon("LinkExternal") - width: UM.Theme.getSize("icon_indicator").width - height: UM.Theme.getSize("icon_indicator").height - } - UM.Label - { - id: manageQueueText - anchors - { - left: externalLinkIcon.right - leftMargin: UM.Theme.getSize("narrow_margin").width - verticalCenter: externalLinkIcon.verticalCenter - } - color: UM.Theme.getColor("text_link") - font: UM.Theme.getFont("medium") - text: catalog.i18nc("@label link to technical assistance", "View user manuals online") - } - MouseArea - { - anchors.fill: parent - hoverEnabled: true - onClicked: Qt.openUrlExternally("https://ultimaker.com/in/cura/troubleshooting/network?utm_source=cura&utm_medium=software&utm_campaign=monitor-not-connected") - onEntered: manageQueueText.font.underline = true - onExited: manageQueueText.font.underline = false - } - } - UM.Label - { - id: noConnectionLabel - anchors.horizontalCenter: parent.horizontalCenter - visible: !isNetworkConfigurable - text: catalog.i18nc("@info", "In order to monitor your print from Cura, please connect the printer.") - font: UM.Theme.getFont("medium") - wrapMode: Text.WordWrap - width: contentWidth + spacing: 10 + Text { text: "Printer Control"; color: "white"; font.pointSize: 16 } + Button { text: "Start Printer" } + Button { text: "Stop Printer" } } } -} +} \ No newline at end of file diff --git a/plugins/MonitorStage/MonitorMenu.qml b/plugins/MonitorStage/MonitorMenu.qml index 5715d9a27c..919b5ee997 100644 --- a/plugins/MonitorStage/MonitorMenu.qml +++ b/plugins/MonitorStage/MonitorMenu.qml @@ -7,27 +7,20 @@ import QtQuick.Controls 2.3 import UM 1.3 as UM import Cura 1.1 as Cura -Item +Rectangle { - signal showTooltip(Item item, point location, string text) - signal hideTooltip() + id: root - Cura.MachineSelector - { - id: machineSelection - headerCornerSide: Cura.RoundedRectangle.Direction.All - width: UM.Theme.getSize("machine_selector_widget").width - height: parent.height + property var machineManager: Cura.MachineManager + property var activeMachine: machineManager.activeMachine + property bool isMachineConnected: activeMachine ? activeMachine.is_connected : false + + color: isMachineConnected ? "green" : "red" + + Label { + id: machineStatusLabel + text: isMachineConnected ? qsTr("Connected") : qsTr("Disconnected") anchors.centerIn: parent - - machineListModel: Cura.MachineListModel {} - - machineManager: Cura.MachineManager - - onSelectPrinter: function(machine) - { - toggleContent(); - Cura.MachineManager.setActiveMachine(machine.id); - } + color: "white" } } \ No newline at end of file diff --git a/plugins/MonitorStage/MonitorStage.py b/plugins/MonitorStage/MonitorStage.py index 4e4c442b4c..076755fd67 100644 --- a/plugins/MonitorStage/MonitorStage.py +++ b/plugins/MonitorStage/MonitorStage.py @@ -3,6 +3,7 @@ import os.path from UM.Application import Application from cura.Stages.CuraStage import CuraStage +from .GrblController import GrblController # New import class MonitorStage(CuraStage): @@ -13,51 +14,13 @@ class MonitorStage(CuraStage): # Wait until QML engine is created, otherwise creating the new QML components will fail Application.getInstance().engineCreatedSignal.connect(self._onEngineCreated) - self._printer_output_device = None - - self._active_print_job = None - self._active_printer = None - - def _setActivePrintJob(self, print_job): - if self._active_print_job != print_job: - self._active_print_job = print_job - - def _setActivePrinter(self, printer): - if self._active_printer != printer: - if self._active_printer: - self._active_printer.activePrintJobChanged.disconnect(self._onActivePrintJobChanged) - self._active_printer = printer - if self._active_printer: - self._setActivePrintJob(self._active_printer.activePrintJob) - # Jobs might change, so we need to listen to it's changes. - self._active_printer.activePrintJobChanged.connect(self._onActivePrintJobChanged) - else: - self._setActivePrintJob(None) - - def _onActivePrintJobChanged(self): - self._setActivePrintJob(self._active_printer.activePrintJob) - - def _onActivePrinterChanged(self): - self._setActivePrinter(self._printer_output_device.activePrinter) + self._grbl_controller = None # New member variable def _onOutputDevicesChanged(self): - try: - # We assume that you are monitoring the device with the highest priority. - new_output_device = Application.getInstance().getMachineManager().printerOutputDevices[0] - if new_output_device != self._printer_output_device: - if self._printer_output_device: - try: - self._printer_output_device.printersChanged.disconnect(self._onActivePrinterChanged) - except TypeError: - # Ignore stupid "Not connected" errors. - pass - - self._printer_output_device = new_output_device - - self._printer_output_device.printersChanged.connect(self._onActivePrinterChanged) - self._setActivePrinter(self._printer_output_device.activePrinter) - except IndexError: - pass + # Instantiate GrblController and connect + if self._grbl_controller is None: # Only instantiate once + self._grbl_controller = GrblController() + self._grbl_controller.connect() def _onEngineCreated(self): # We can only connect now, as we need to be sure that everything is loaded (plugins get created quite early) diff --git a/plugins/MonitorStage/old-src/application.py b/plugins/MonitorStage/old-src/application.py new file mode 100644 index 0000000000..2ed94af32b --- /dev/null +++ b/plugins/MonitorStage/old-src/application.py @@ -0,0 +1,32 @@ +from core import Controller +from ui import Ui # Renamed Ui to PrinterGUI as per previous refactoring +import tkinter as tk # Tkinter root will be managed here +from logger import Logger + +class PrinterApplication: + """ + Orchestrates the 3D Printer Controller and its Graphical User Interface. + Manages the setup, communication, and lifecycle of the application. + """ + def __init__(self): + self.controller = Controller(Logger("Controller")) + self.ui = Ui(Logger("Ui"), update=self.update) # Pass the root to the UI + + self._update_job_id = None + self.gcode_pointer = 0 # Manage gcode pointer state within the application + + def update(self): + """TODO""" + for event in self.controller.update(): + self.ui.handle(event) + + for event in self.ui.update(): + self.controller.handle(event) + + def run(self): + """Initializes and runs the application.""" + print("Initializing application...") + self.controller.connect() # Establish connection to hardware/simulator + self.ui.initialize() # Build the UI widgets + + self.ui.run() \ No newline at end of file diff --git a/plugins/MonitorStage/old-src/core/__init__.py b/plugins/MonitorStage/old-src/core/__init__.py new file mode 100644 index 0000000000..d1ee4f4215 --- /dev/null +++ b/plugins/MonitorStage/old-src/core/__init__.py @@ -0,0 +1 @@ +from .controller import Controller \ No newline at end of file diff --git a/plugins/MonitorStage/old-src/core/commandIterator.py b/plugins/MonitorStage/old-src/core/commandIterator.py new file mode 100644 index 0000000000..83d150a46e --- /dev/null +++ b/plugins/MonitorStage/old-src/core/commandIterator.py @@ -0,0 +1,29 @@ +from abc import ABC + +""" +TODO +""" +class CommandIterator(ABC): + def __init__(self): + self.pointer = 0 + + def get_pointer(self) -> int: + return self.pointer + + def get_text(self, pointer: int) -> str: + return ... + + +""" +TODO +""" +class SweepCommandIterator(ABC): + def __init__(self): + self.pointer = 0 + + def get_pointer(self) -> int: + return self.pointer + + def get_text(self, pointer: int) -> str: + self.pointer += 1 + return "G0 X10" if self.get_pointer() % 2 == 0 else "G0 X-10" \ No newline at end of file diff --git a/plugins/MonitorStage/old-src/core/controller.py b/plugins/MonitorStage/old-src/core/controller.py new file mode 100644 index 0000000000..184cf78b8f --- /dev/null +++ b/plugins/MonitorStage/old-src/core/controller.py @@ -0,0 +1,213 @@ +import time +from .serialBridge import SerialBridge +from .filehandler import GcodeHandler +import events +from logger import NozzleTemperatureWarning +from serial.serialutil import SerialException + +class Controller: + MAX_BUFFER_SIZE = 20 + SERIAL_TIMEOUT = 5 # seconds + MAX_TEMP_OFFSET = 5 # degrees Celsius + JOG_DISTANCE = 40 # mm + RECONNECT_INTERVAL = 5 # seconds + + def __init__(self, logger): + self.logger = logger + self.registered_events = [] + + self.set_gcode_file("3d files/hart/hart.gcode") + self.serialBridge = SerialBridge() + self.is_connected = False + self.last_reconnect_attempt = 0 + + # Track per-tool target & current temperatures + self.target_temperatures = {"T0": 20, "T1": 20} + self.current_temperatures = {"T0": 0.0, "T1": 0.0} + + def connect(self): + try: + self.serialBridge.connect() + t_0 = time.time() + while "Grbl" not in self.serialBridge.readline(): + if time.time() - t_0 > Controller.SERIAL_TIMEOUT: + raise RuntimeError("Timeout") + + self.serialBridge.write("$22=0\r\n") # disable homing + + self.wait_for_ok() + + self.serialBridge.write("$X\r\n") + self.wait_for_ok() + + self.serialBridge.write("$21=0\r\n") + self.wait_for_ok() + + self.serialBridge.write("$3=4\r\n") # invert Z axis + self.wait_for_ok() + + self.serialBridge.write("G21\r\n") # mm + self.wait_for_ok() + self.serialBridge.write("G90\r\n") # absolute coords + self.wait_for_ok() + + + self.wait_for_ok() + self.is_connected = True + self.serialBridge.is_connected = True + self.register_event(events.ArduinoConnected()) + except (RuntimeError, SerialException): + self.is_connected = False + self.serialBridge.is_connected = False + self.register_event(events.ArduinoDisconnected()) + + def set_heating(self, tool: str, temperature: int): + """Set target temperature for a specific tool (e.g., T0, T1).""" + self.target_temperatures[tool] = temperature + # Send full tuple (all tools), so firmware always has consistent state + cmd_parts = [f"{t}:{temp}" for t, temp in self.target_temperatures.items()] + cmd = ",".join(cmd_parts) + self.serialBridge.write(f"M104 {cmd}\r\n") + self.wait_for_ok() + + def update(self): + if not self.is_connected: + if time.time() - self.last_reconnect_attempt > self.RECONNECT_INTERVAL: + self.last_reconnect_attempt = time.time() + self.connect() + return [] # No updates if not connected + + try: + self.serialBridge.flush() + + # Update buffer size bookkeeping + if self.gcode_handler.aprox_buffer >= Controller.MAX_BUFFER_SIZE: + cpy = self.gcode_handler.aprox_buffer + self.gcode_handler.aprox_buffer = self.get_aprox_buffer() + self.gcode_handler.execution_line += max(0, cpy - self.gcode_handler.aprox_buffer) + self.register_event(events.SetGcodeLine(self.gcode_handler.execution_line)) + + # Feed commands + if self.gcode_handler and self.gcode_handler.playing: + if self.gcode_handler.com_line < self.gcode_handler.get_size(): + t_0 = time.time() + while self.gcode_handler.aprox_buffer < Controller.MAX_BUFFER_SIZE: + if time.time() - t_0 > Controller.SERIAL_TIMEOUT: + raise RuntimeError("Timeout") + if self.gcode_handler.com_line >= self.gcode_handler.get_size(): + break + else: + command = self.gcode_handler.get_line(self.gcode_handler.com_line) + if ( + len(command) == 0 + or command[0] == ";" + or command[0] == "M" + or command[0:3] == "G92" + ): + self.gcode_handler.execution_line += 1 + else: + self.serialBridge.write(command + "\r\n") + self.wait_for_ok() + self.gcode_handler.aprox_buffer += 1 + self.gcode_handler.com_line += 1 + + # Request temperatures + self.serialBridge.write("M105\r\n") + t_0 = time.time() + while True: + if time.time() - t_0 > Controller.SERIAL_TIMEOUT: + raise RuntimeError("Timeout") + line = self.serialBridge.readline() + if "ok\r\n" == line: + continue + elif "$M105=" in line: + # Parse tuple response: $M105=T0:200.000,T1:180.000 + payload = line.split("=")[-1].strip() + pairs = payload.split(",") + for pair in pairs: + tool, val = pair.split(":") + self.current_temperatures[tool] = float(val) + break + + # Safety check for each tool + for tool, target in self.target_temperatures.items(): + current = self.current_temperatures.get(tool, 0.0) + if abs(target - current) > Controller.MAX_TEMP_OFFSET: + self.logger.show_message(NozzleTemperatureWarning(tool)) + self.register_event(events.UpdateNozzleTemperature(tool, current)) + + self.serialBridge.flush() + + except (SerialException, RuntimeError) as e: + print(f"Error: {e}") + self.is_connected = False + self.serialBridge.is_connected = False + self.register_event(events.ArduinoDisconnected()) + + cpy = self.registered_events + self.registered_events = [] + return cpy + + def register_event(self, event: events.Event): + self.registered_events.append(event) + + def handle(self, event: events.Event): + if not self.is_connected: + return + try: + match event: + case events.UpdateTargetTemperature(tool=tool, temperature=temp): + self.set_heating(tool, temp) + case events.PlayGcode: + self.gcode_handler.play() + case events.PauseGcode: + self.gcode_handler.pause() + case events.Home: + self.serialBridge.write("$H\r\n") + self.wait_for_ok() + case events.NewGcodeFile(filename): + self.set_gcode_file(filename) + case events.Jog(movement): + self.serialBridge.write("G91\r\n") # relative coords + self.wait_for_ok() + command = ( + f"G1 X{movement[0]*Controller.JOG_DISTANCE} " + f"Y{movement[1]*Controller.JOG_DISTANCE} " + f"Z{movement[2]*Controller.JOG_DISTANCE} " + f"E{movement[3]*Controller.JOG_DISTANCE + 7} " + f"B{movement[3]*Controller.JOG_DISTANCE + 7} " + "F200\r\n" + ) + self.serialBridge.write(command) + self.wait_for_ok() + self.serialBridge.write("G90\r\n") # absolute coords + self.wait_for_ok() + case _: + raise NotImplementedError("Event not caught: " + str(event)) + except (SerialException, RuntimeError): + self.is_connected = False + self.serialBridge.is_connected = False + self.register_event(events.ArduinoDisconnected()) + + def wait_for_ok(self): + t_0 = time.time() + while "ok\r\n" not in self.serialBridge.readline(): + if time.time() - t_0 > Controller.SERIAL_TIMEOUT: + raise RuntimeError("Timeout") + + def get_aprox_buffer(self): + self.serialBridge.flush() + self.serialBridge.write("G200\r\n") # custom command + t_0 = time.time() + while True: + if time.time() - t_0 > Controller.SERIAL_TIMEOUT: + raise RuntimeError("Timeout") + line = self.serialBridge.readline() + if "ok\r\n" == line: + continue + elif "$G200=" in line: + return int(line.split("=")[-1][:-2]) + + def set_gcode_file(self, filename: str): + self.gcode_handler = GcodeHandler(filename) + self.register_event(events.NewGcodeFileHandler(self.gcode_handler)) diff --git a/plugins/MonitorStage/old-src/core/filehandler.py b/plugins/MonitorStage/old-src/core/filehandler.py new file mode 100644 index 0000000000..dcc4da7381 --- /dev/null +++ b/plugins/MonitorStage/old-src/core/filehandler.py @@ -0,0 +1,47 @@ +from abc import ABC, abstractmethod + + +class GcodeHandler: + def __init__(self, filename=None): + self.filename = filename + + self.playing = False + self.execution_line = -1 # the line that is estimated to be executing + self.com_line = 0 # the last line that was sent to the COM + self.aprox_buffer = 0 # an estimate of the current planner buffer size in the arduino + # this is a theoretical maximum + + with open(filename, 'r') as f: + self.gcode_lines = f.read().split('\n') + + def get_line(self, line: int): + return self.gcode_lines[line] + + def get_size(self): + return len(self.gcode_lines) + + def play(self): + if not self.playing: + self.playing = True + + def pause(self): + if self.playing: + self.playing = False + + +class EmptyGcodeHandler: + def __init__(self, filename=None): + self.playing = False + self.aprox_buffer = 0 + + def get_line(self, line: int): + return "" + + def get_size(self): + return 1 + + def play(self): + ... + + def pause(self): + ... \ No newline at end of file diff --git a/plugins/MonitorStage/old-src/core/serialBridge.py b/plugins/MonitorStage/old-src/core/serialBridge.py new file mode 100644 index 0000000000..11d16f6399 --- /dev/null +++ b/plugins/MonitorStage/old-src/core/serialBridge.py @@ -0,0 +1,55 @@ +import serial +from serial.serialutil import SerialException + +SERIAL_PORT = "COM5" + +class SerialBridge: + def __init__(self): + self.ser = None + + def connect(self): + self.ser = serial.Serial( + SERIAL_PORT, + baudrate=115200, + timeout=1 + ) + self._buffer = "" + + def disconnect(self): + if self.ser and self.ser.is_open: + self.ser.close() + + def read(self): + if not self.ser: + raise SerialException("Not connected") + return self.ser.read() + + def write(self, data): + if not self.ser: + raise SerialException("Not connected") + print(f"> {data.strip()}") + return self.ser.write(data.encode("utf-8")) + + def readline(self) -> str: + char = True + while char: + char = self.read() + if char: + # print(char) + try: + char = char.decode("utf-8") + except UnicodeDecodeError: + continue # Ignore non-utf8 characters + self._buffer += char + + if char == '\n': + cpy = self._buffer + print(f"< {cpy.strip()}") + self._buffer = "" # clear buffer + return cpy + return "" + + def flush(self): + if self.ser: + self.ser.flush() + diff --git a/plugins/MonitorStage/old-src/events.py b/plugins/MonitorStage/old-src/events.py new file mode 100644 index 0000000000..727c29ef77 --- /dev/null +++ b/plugins/MonitorStage/old-src/events.py @@ -0,0 +1,57 @@ +from dataclasses import dataclass + +# Base Event class (optional, but good for type hinting) +@dataclass(frozen=True) # frozen=True makes them immutable and hashable +class Event: + pass + +@dataclass(frozen=True) +class UpdateNozzleTemperature(Event): + tool: str # e.g. "T0", "T1" + temperature: float # current temperature of that tool + +@dataclass(frozen=True) +class UpdateTargetTemperature(Event): + tool: str # e.g. "T0", "T1" + temperature: float # target temperature for that tool + +@dataclass(frozen=True) +class NewGcodeFileHandler(Event): + handler: object + +@dataclass(frozen=True) +class SetGcodeLine(Event): + line: int + +@dataclass(frozen=True) +class NewGcodeFile(Event): + filename: str + +# +@dataclass(frozen=True) +class Jog(Event): + movement: list # [delta_x, delta_y, delta_z, delta_z] + + +@dataclass(frozen=True) +class Home(Event): + ... + + +# GCODE ACTIONS + +@dataclass(frozen=True) +class PlayGcode(Event): + ... + +@dataclass(frozen=True) +class PauseGcode(Event): + ... + +@dataclass(frozen=True) +class ArduinoConnected(Event): + ... + +@dataclass(frozen=True) +class ArduinoDisconnected(Event): + ... diff --git a/plugins/MonitorStage/old-src/images/logo.ico b/plugins/MonitorStage/old-src/images/logo.ico new file mode 100644 index 0000000000..6460c8d3bf Binary files /dev/null and b/plugins/MonitorStage/old-src/images/logo.ico differ diff --git a/plugins/MonitorStage/old-src/images/logo.png b/plugins/MonitorStage/old-src/images/logo.png new file mode 100644 index 0000000000..49b3878db7 Binary files /dev/null and b/plugins/MonitorStage/old-src/images/logo.png differ diff --git a/plugins/MonitorStage/old-src/logger.py b/plugins/MonitorStage/old-src/logger.py new file mode 100644 index 0000000000..04bbbdec9b --- /dev/null +++ b/plugins/MonitorStage/old-src/logger.py @@ -0,0 +1,48 @@ +from dataclasses import dataclass + +class Logger: + def __init__(self, name: str): + self.name = name + self.shown_messages = set() + + def show_message(self, message: object): + if issubclass(type(message), LogMessage): + print("ℹ️ " + message.content) + elif issubclass(type(message), WarningMessage): + for msg in self.shown_messages: + if type(message) == type(msg): + # already shown + return + # show `message` + print("⚠️ "+message.content) + self.shown_messages.add(message) + else: + raise NotImplementedError("Event not catched: ") + + +## MESSAGES ## + +@dataclass(frozen=True) +class Message: + content: str + +## LOG MESSAGES ## + +@dataclass(frozen=True) +class LogMessage(Message): + ... + +## WARNING MESSAGES ## + +@dataclass(frozen=True) +class WarningMessage(Message): + pass + +@dataclass(frozen=True) +class NozzleTemperatureWarning(WarningMessage): + def __init__(self, tool): + super().__init__( + f"The temperature for {tool} is too far from the target temperature." + ) + +## ERROR MESSAGES ## \ No newline at end of file diff --git a/plugins/MonitorStage/old-src/main.py b/plugins/MonitorStage/old-src/main.py new file mode 100644 index 0000000000..db7cc13310 --- /dev/null +++ b/plugins/MonitorStage/old-src/main.py @@ -0,0 +1,8 @@ +from application import PrinterApplication + +def main(): + app = PrinterApplication() + app.run() + +if __name__ == "__main__": + main() diff --git a/plugins/MonitorStage/old-src/ui/__init__.py b/plugins/MonitorStage/old-src/ui/__init__.py new file mode 100644 index 0000000000..b7ed16c6bd --- /dev/null +++ b/plugins/MonitorStage/old-src/ui/__init__.py @@ -0,0 +1 @@ +from .tkinter_ui import TkinterUi as Ui \ No newline at end of file diff --git a/plugins/MonitorStage/old-src/ui/abstract_ui.py b/plugins/MonitorStage/old-src/ui/abstract_ui.py new file mode 100644 index 0000000000..3408b91ede --- /dev/null +++ b/plugins/MonitorStage/old-src/ui/abstract_ui.py @@ -0,0 +1,10 @@ +from abc import ABC, abstractmethod + +# Define the abstract base class for the GUI interface +class AbstractUI(ABC): + @abstractmethod + def update(self): + """ + Abstract method: Updates the GUI elements. + """ + pass \ No newline at end of file diff --git a/plugins/MonitorStage/old-src/ui/actions_control.py b/plugins/MonitorStage/old-src/ui/actions_control.py new file mode 100644 index 0000000000..ef811cfd98 --- /dev/null +++ b/plugins/MonitorStage/old-src/ui/actions_control.py @@ -0,0 +1,123 @@ +import tkinter as tk +from tkinter import ttk + +class ActionsControl: + """Manages the Play, Pause, Stop, and Jog buttons.""" + def __init__( + self, + parent_frame: ttk.Frame, + play_callback: callable = None, + home_callback: callable = None, + pause_callback: callable = None, + stop_callback: callable = None, + jog_callback: callable = None, + open_file_callback: callable = None, + ): + self.parent_frame = parent_frame + + self.play_callback = play_callback + self.home_callback = home_callback + self.pause_callback = pause_callback + self.stop_callback = stop_callback + self.jog_callback = jog_callback # Accept a general jog callback + self.open_file_callback = open_file_callback + + self.playing = False + + ttk.Label(parent_frame, text="Actions", style='Heading.TLabel').pack(anchor=tk.NW, pady=(0, 10)) + + button_frame = ttk.Frame(parent_frame, style='DarkFrame.TFrame') + button_frame.pack(fill=tk.X, pady=(5, 0)) + + self.play_button = ttk.Button( + button_frame, + text="▶ Play", + command=self._on_play, + style='DarkButton.TButton' + ) + self.play_button.pack(side=tk.LEFT, expand=True, padx=5, pady=5) + + self.open_file_button = ttk.Button( + button_frame, + text="📄 Open", + command=self._on_open_file, + style='DarkButton.TButton' + ) + self.open_file_button.pack(side=tk.LEFT, expand=True, padx=5, pady=5) + + # Jog buttons section + jog_frame = ttk.Frame(parent_frame, style='DarkFrame.TFrame') + jog_frame.pack(pady=(10, 0)) + + directions = [ + ("X-", [-1, 0, 0, 0], 0, 0), ("X+", [1, 0, 0, 0], 0, 1), + ("Y-", [0, -1, 0, 0], 1, 0), ("Y+", [0, 1, 0, 0], 1, 1), + ("Z-", [0, 0, -1, 0], 2, 0), ("Z+", [0, 0, 1, 0], 2, 1), + ("E-", [0, 0, 0, -1], 3, 0), ("E+", [0, 0, 0, 1], 3, 1), + ] + + for label, movement, row, col in directions: + btn = ttk.Button( + jog_frame, + text=label, + command=lambda movement=movement: self._on_jog(movement), + style='DarkButton.TButton' + ) + btn.grid(row=row, column=col, padx=5, pady=5, sticky="nsew") + + # Optional: make buttons stretch to fill the frame + for i in range(3): + jog_frame.rowconfigure(i, weight=1) + for i in range(2): + jog_frame.columnconfigure(i, weight=1) + + + self.home_button = ttk.Button( + button_frame, + text="🏠 Home", + command=self._on_home, + style='DarkButton.TButton' + ) + self.home_button.pack(side=tk.LEFT, expand=True, padx=5, pady=5) + + def _on_play(self): + if self.playing: + # pause + if self.pause_callback: + self.pause_callback() + self.play_button.configure(text="▶ Play") + self.playing = False + else: + # play + if self.play_callback: + self.play_callback() + self.play_button.configure(text="⏸ Pause") + self.playing = True + + + def _on_home(self): + if self.home_callback: + self.home_callback() + + def _on_jog(self, axis): + if self.jog_callback: + self.jog_callback(axis) + + def _on_open_file(self): + file = tk.filedialog.askopenfilename( + parent=self.parent_frame, + # mode='r', + title="Select a file", + initialdir="C:\\Users\\Simon\\Documents\\projecten\\Chocolate-Printer\\3d files\\", + filetypes=(("Gcode Slice", "*.gcode"),), + defaultextension="gcode", + multiple=False + ) + if file is None: + # user canceled selection + return + + if self.open_file_callback is None: + raise ValueError() + + self.open_file_callback(file) \ No newline at end of file diff --git a/plugins/MonitorStage/old-src/ui/dark_theme.py b/plugins/MonitorStage/old-src/ui/dark_theme.py new file mode 100644 index 0000000000..72def1ccc4 --- /dev/null +++ b/plugins/MonitorStage/old-src/ui/dark_theme.py @@ -0,0 +1,56 @@ +from tkinter import ttk + + +class DarkTheme: + """Manages the application's dark theme styles for Tkinter widgets.""" + def __init__(self, root): + self.style = ttk.Style() + self._configure_styles() + root.tk_setPalette(background='#2c313a', foreground='#ffffff') + + def _configure_styles(self): + """Applies the dark theme configurations to ttk widgets.""" + FONT_SIZE = 16 + self.style.theme_use('clam') + self.style.configure('DarkFrame.TFrame', background='#2c313a', relief='flat') + self.style.configure('DarkLabel.TLabel', background='#2c313a', foreground='#ffffff', font=('Inter', 16)) + self.style.configure('Heading.TLabel', background='#2c313a', foreground='#ffffff', font=('Inter', FONT_SIZE, 'bold')) + self.style.configure('DarkText.TText', background='#1e2127', foreground='#ffffff', insertbackground='#ffffff', font=('monospace', 10)) + self.style.configure('DarkButton.TButton', background='#4a90e2', foreground='#ffffff', + font=('Inter', FONT_SIZE, 'bold'), borderwidth=0, focusthickness=3, focuscolor='none') + self.style.map('DarkButton.TButton', + background=[('active', '#357abd')], + foreground=[('active', '#ffffff')]) + + # ----- Scale ----- + self.style.layout('DarkScale.Horizontal.TScale', + [('Horizontal.Scale.trough', + {'children': [('Horizontal.Scale.slider', + {'side': 'left', 'sticky': 'ns'})], + 'sticky': 'nswe'})]) + + self.style.configure('DarkScale.Horizontal.TScale', + background='#2c313a', troughcolor='#1e2127', + slidercolor='#4a90e2', + foreground='#ffffff', highlightbackground='#2c313a', + borderwidth=0) + self.style.map('DarkScale.Horizontal.TScale', + slidercolor=[('active', '#357abd')]) + + # ----- Progressbar ----- + self.style.layout('Dark.Horizontal.TProgressbar', + [('Horizontal.Progressbar.trough', + {'children': [('Horizontal.Progressbar.pbar', + {'side': 'left', 'sticky': 'ns'})], + 'sticky': 'nswe'})]) + + self.style.configure('Dark.Horizontal.TProgressbar', + troughcolor='#1e2127', + background='#4a90e2', + bordercolor='#2c313a', + lightcolor='#4a90e2', + darkcolor='#4a90e2', + thickness=20) + + self.style.map('Dark.Horizontal.TProgressbar', + background=[('active', '#357abd')]) diff --git a/plugins/MonitorStage/old-src/ui/gcode/__init__.py b/plugins/MonitorStage/old-src/ui/gcode/__init__.py new file mode 100644 index 0000000000..da7e5b0a55 --- /dev/null +++ b/plugins/MonitorStage/old-src/ui/gcode/__init__.py @@ -0,0 +1,25 @@ +import tkinter as tk +from tkinter import ttk +from .gcode_viewer import GcodeViewer + + + +class GcodeFrame: + """Manages the G-code text area, highlighting, and scrolling.""" + def __init__(self, parent_frame: ttk.Frame): + + title = ttk.Label(parent_frame, text="G-code Execution", style='Heading.TLabel') + title.pack(anchor=tk.NW, pady=(0, 10)) + + self.filename_label = tk.Label(parent_frame, text="No file selected") + self.filename_label.pack(anchor=tk.NW, pady=(0, 10)) + + self.gcode_viewer = GcodeViewer(parent_frame) + + + def set_fileHandler(self, filehandler: object): + self.filename_label.configure(text=filehandler.filename) + self.gcode_viewer.set_fileHandler(filehandler) + + def set_gcode_pointer(self, pointer: int): + self.gcode_viewer.set_gcode_pointer(pointer) \ No newline at end of file diff --git a/plugins/MonitorStage/old-src/ui/gcode/gcode_viewer.py b/plugins/MonitorStage/old-src/ui/gcode/gcode_viewer.py new file mode 100644 index 0000000000..85f260b279 --- /dev/null +++ b/plugins/MonitorStage/old-src/ui/gcode/gcode_viewer.py @@ -0,0 +1,71 @@ +import tkinter as tk +from tkinter import ttk + +class GcodeViewer: + """Manages the G-code text area, highlighting, and scrolling.""" + def __init__(self, parent_frame: ttk.Frame): + self.filehandler = None + self.current_gcode_pointer = -1 + + self.gcode_text = tk.Text(parent_frame, wrap=tk.WORD, height=7, width=80, + background='#1e2127', foreground='#ffffff', + insertbackground='#ffffff', font=('monospace', 10), + borderwidth=0, highlightthickness=0, relief='flat') + self.gcode_text.pack(fill=tk.BOTH, expand=True, pady=(0, 10)) + + self.gcode_text.tag_configure("current_line", background="#3a4049", foreground="#2ecc71", font=('monospace', 10, 'bold')) + self.gcode_text.tag_configure("comment", foreground="#888888") + + self.progress_bar = ttk.Progressbar( + parent_frame, + orient="horizontal", + length=200, + mode="determinate", + style='Dark.Horizontal.TProgressbar', + takefocus=True, + maximum=100, + # height=10, + ) + self.progress_bar.pack(fill=tk.X, expand=True, pady=(0, 10)) + + def set_gcode_pointer(self, pointer: int): + """ + Sets the pointer in the gcode to a specific line position. + Auto-scrolls to the highlighted line. + """ + self.gcode_text.tag_remove("current_line", "1.0", tk.END) + + if 0 <= pointer < self.filehandler.get_size(): + self.current_gcode_pointer = pointer + start_index = f"{pointer + 1}.0" + end_index = f"{pointer + 1}.end" + self.gcode_text.tag_add("current_line", start_index, end_index) + self.gcode_text.see(start_index) + + # set progress bar + percentage = pointer/self.filehandler.get_size()*100 + self.progress_bar["value"] = percentage + else: + self.current_gcode_pointer = -1 + + def set_fileHandler(self, filehandler: object): + """TODO""" + self.filehandler = filehandler + + self.gcode_text.config(state=tk.NORMAL) # enable editing + + self.gcode_text.delete('1.0', tk.END) # clear all contents + + # Populates the G-code text area with lines from the G-code file. + for i in range(self.filehandler.get_size()): + self.gcode_text.insert(tk.END, self.filehandler.get_line(i) + "\n") + + # Applies comment highlighting to G-code lines. + for i in range(self.filehandler.get_size()): + line = self.filehandler.get_line(i) + if ';' in line: + comment_start = line.find(';') + self.gcode_text.tag_add("comment", f"{i+1}.{comment_start}", f"{i+1}.end") + + self.gcode_text.config(state=tk.DISABLED) + self.set_gcode_pointer(0) \ No newline at end of file diff --git a/plugins/MonitorStage/old-src/ui/heating_control.py b/plugins/MonitorStage/old-src/ui/heating_control.py new file mode 100644 index 0000000000..eef97f32d5 --- /dev/null +++ b/plugins/MonitorStage/old-src/ui/heating_control.py @@ -0,0 +1,63 @@ +import tkinter as tk +from tkinter import ttk + +class HeatingControl: + """Manages heating sliders for multiple tools independently.""" + def __init__(self, parent_frame: ttk.Frame, on_update=None): + self._on_update = on_update + + # Initial slider values + self.slider_values = {"T0": 20, "T1": 20} + + # --- T0 Slider --- + ttk.Label(parent_frame, text="T0 Temperature", style='DarkLabel.TLabel').pack(anchor=tk.NW, pady=(5, 0)) + self.slider_t0 = tk.Scale( + parent_frame, + from_=20, + to=50, + orient=tk.HORIZONTAL, + command=lambda v: self._on_slider_change("T0", v), + tickinterval=5, + showvalue=0, + relief=tk.RIDGE, + bd=2 + ) + self.slider_t0.set(self.slider_values["T0"]) + self.slider_t0.pack(fill=tk.X, pady=(0, 10)) + + self.label_t0 = ttk.Label(parent_frame, text=f"T0 Heating Level: {self.slider_values['T0']}°C", style='DarkLabel.TLabel') + self.label_t0.pack(anchor=tk.NW, pady=(0, 10)) + + # --- T1 Slider --- + ttk.Label(parent_frame, text="T1 Temperature", style='DarkLabel.TLabel').pack(anchor=tk.NW, pady=(5, 0)) + self.slider_t1 = tk.Scale( + parent_frame, + from_=20, + to=50, + orient=tk.HORIZONTAL, + command=lambda v: self._on_slider_change("T1", v), + tickinterval=5, + showvalue=0, + relief=tk.RIDGE, + bd=2 + ) + self.slider_t1.set(self.slider_values["T1"]) + self.slider_t1.pack(fill=tk.X, pady=(0, 10)) + + self.label_t1 = ttk.Label(parent_frame, text=f"T1 Heating Level: {self.slider_values['T1']}°C", style='DarkLabel.TLabel') + self.label_t1.pack(anchor=tk.NW, pady=(0, 10)) + + def _on_slider_change(self, tool, value): + """Callback when a slider value changes.""" + level = int(float(value)) + self.slider_values[tool] = level + + # Update label + if tool == "T0": + self.label_t0.config(text=f"T0 Heating Level: {level}°C") + elif tool == "T1": + self.label_t1.config(text=f"T1 Heating Level: {level}°C") + + # Call update callback if provided + if self._on_update: + self._on_update(tool=tool, level=level) diff --git a/plugins/MonitorStage/old-src/ui/temperature_chart.py b/plugins/MonitorStage/old-src/ui/temperature_chart.py new file mode 100644 index 0000000000..266863725d --- /dev/null +++ b/plugins/MonitorStage/old-src/ui/temperature_chart.py @@ -0,0 +1,112 @@ +import matplotlib.pyplot as plt +from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg +from datetime import datetime +from tkinter import ttk +from collections import deque + +class TemperatureChart: + """Manages the Matplotlib temperature chart for multiple tools.""" + COLORS = {"T0": "#e74c3c", "T1": "#3498db"} # T0=red, T1=blue + + def __init__(self, parent_frame: ttk.Frame, max_chart_points: int = 60): + self.max_chart_points = max_chart_points + self.temperature_data = {tool: deque(maxlen=max_chart_points) for tool in TemperatureChart.COLORS} + self.time_data = deque(maxlen=max_chart_points) + self.target_temperatures = {tool: None for tool in TemperatureChart.COLORS} + self.lines = {} + self.target_lines = {} + + # Figure setup + self.fig, self.ax = plt.subplots(figsize=(5, 3), dpi=100) + self._configure_chart_style() + + # Create one line per tool + for tool, color in TemperatureChart.COLORS.items(): + line, = self.ax.plot([], [], color=color, linewidth=2, label=f"{tool} Current") + self.lines[tool] = line + + self.canvas = FigureCanvasTkAgg(self.fig, master=parent_frame) + self.canvas_widget = self.canvas.get_tk_widget() + self.canvas_widget.pack(fill="both", expand=True) + + # Labels per tool + self.temp_labels = {} + for tool in TemperatureChart.COLORS: + label = ttk.Label(parent_frame, text=f"{tool}: --.--°C", style='DarkLabel.TLabel') + label.pack(anchor="s", pady=(5, 0)) + self.temp_labels[tool] = label + + self.ax.legend(loc='upper left', facecolor='#2c313a', edgecolor='#cccccc', labelcolor='white', framealpha=0.8) + + def _configure_chart_style(self): + self.ax.set_facecolor('#1e2127') + self.fig.patch.set_facecolor('#1e2127') + self.ax.tick_params(axis='x', colors='#cccccc') + self.ax.tick_params(axis='y', colors='#cccccc') + self.ax.spines['bottom'].set_color('#cccccc') + self.ax.spines['left'].set_color('#cccccc') + self.ax.spines['top'].set_visible(False) + self.ax.spines['right'].set_visible(False) + + self.ax.set_xlabel("Time", color='#cccccc') + self.ax.set_ylabel("Temperature (°C)", color='#cccccc') + self.ax.set_title("Temperature Readings", color='#ffffff') + self.ax.set_ylim(0, 50) + + def add_temperatures(self, temps: dict): + """ + Add temperatures for all tools at the current time step. + temps: dict mapping tool -> temperature + """ + now = datetime.now() + self.time_data.append(now) + + for tool in self.temperature_data: + if tool in temps: + self.temperature_data[tool].append(temps[tool]) + else: + # Repeat last value if no new reading + last = self.temperature_data[tool][-1] if self.temperature_data[tool] else 0.0 + self.temperature_data[tool].append(last) + + # Update label if tool was provided + if tool in temps: + self.temp_labels[tool].config(text=f"{tool}: {temps[tool]:.1f}°C") + + # Update all lines + for tool, line in self.lines.items(): + line.set_data(list(self.time_data), list(self.temperature_data[tool])) + + # Update x-axis + if self.time_data: + self.ax.set_xlim(self.time_data[0], self.time_data[-1]) + num_ticks = 5 + tick_indices = [int(i*(len(self.time_data)-1)/(num_ticks-1)) for i in range(num_ticks)] if len(self.time_data) > 1 else [0] + self.ax.set_xticks([self.time_data[i] for i in tick_indices]) + self.ax.set_xticklabels([self.time_data[i].strftime("%H:%M:%S") for i in tick_indices], rotation=45, ha='right') + self.ax.figure.autofmt_xdate() + + self.canvas.draw_idle() + + + + def set_target_temperature(self, tool: str, target_temp: float): + """Set target temperature for a specific tool.""" + self.target_temperatures[tool] = target_temp + + if tool in self.target_lines and self.target_lines[tool]: + self.target_lines[tool].remove() + + self.target_lines[tool] = self.ax.axhline( + y=target_temp, + color=TemperatureChart.COLORS[tool], + linestyle='--', + linewidth=1.5, + label=f"{tool} Target: {target_temp:.1f}°C" + ) + + self.ax.legend(loc='upper left', facecolor='#2c313a', edgecolor='#cccccc', labelcolor='white', framealpha=0.8) + self.canvas.draw_idle() + + def close(self): + plt.close(self.fig) diff --git a/plugins/MonitorStage/old-src/ui/tkinter_ui.py b/plugins/MonitorStage/old-src/ui/tkinter_ui.py new file mode 100644 index 0000000000..ab5dc1f75e --- /dev/null +++ b/plugins/MonitorStage/old-src/ui/tkinter_ui.py @@ -0,0 +1,139 @@ +from .abstract_ui import AbstractUI +from .dark_theme import DarkTheme +from .heating_control import HeatingControl +from .temperature_chart import TemperatureChart +from .gcode import GcodeFrame +import events +from .actions_control import ActionsControl + +import tkinter as tk +from tkinter import ttk + +class TkinterUi(AbstractUI): + MIN_WIDTH = 1000 + MIN_HEIGHT = 800 + + def __init__(self, logger, update:callable): + self.logger = logger + self.registered_events = [] + + self.root = tk.Tk() + self.root.title("3D Printer Control Panel") + self.root.geometry(f"{TkinterUi.MIN_WIDTH}x{TkinterUi.MIN_HEIGHT}") + self.root.minsize(TkinterUi.MIN_WIDTH, TkinterUi.MIN_HEIGHT) + self.root.configure(bg="#282c36") + + self.status_label = tk.Label(self.root, text="Disconnected", fg="red", bg="#282c36", font=("Arial", 10)) + + self._update_job_id = None + self.root.protocol("WM_DELETE_WINDOW", self._on_closing) + self._running = True + self._on_update = update + + def initialize(self): + """Creates and lays out all the GUI elements using specialized components.""" + + # Apply dark theme using the global ttk.Style instance + self.theme = DarkTheme(self.root) # Call without arguments now + + # Main Layout Frames + top_frame = ttk.Frame(self.root, padding="10 10 10 10", style='DarkFrame.TFrame') + top_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10) + + bottom_frame = ttk.Frame(self.root, padding="10 10 10 10", style='DarkFrame.TFrame') + bottom_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10) + + # Temperature Chart Section + temp_chart_frame = ttk.Frame(top_frame, padding="15", style='DarkFrame.TFrame') + temp_chart_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=5, pady=5) + ttk.Label(temp_chart_frame, text="🔴 Current Temperature (Live Chart)", style='Heading.TLabel', foreground='#e74c3c').pack(anchor=tk.NW, pady=(0, 10)) + self.temperature_chart = TemperatureChart(temp_chart_frame) + + actions_frame = ttk.Frame(top_frame, padding="0 15 0 0", style='DarkFrame.TFrame') # Add some top padding + actions_frame.pack(fill=tk.X, pady=(10,0)) # Fill horizontally within settings_frame + # Initialize ActionsControl, passing initial callbacks (which might be None at this point) + self.actions_control = ActionsControl( + actions_frame, + play_callback=lambda: self.register_event(events.PlayGcode), + home_callback=lambda: self.register_event(events.Home), + pause_callback=lambda: self.register_event(events.PauseGcode), + jog_callback=lambda movement: self.register_event(events.Jog(movement)), + open_file_callback=lambda filename: self.register_event(events.NewGcodeFile(filename)), + ) + + # Printer Settings Section + settings_frame = ttk.Frame(top_frame, padding="15", style='DarkFrame.TFrame') + settings_frame.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True, padx=5, pady=5) + # ttk.Label(settings_frame, text="Printer Settings", style='Heading.TLabel').pack(anchor=tk.NW, pady=(0, 10)) + + # Pass the slider_callback to HeatingControl + def update_heating_temp(tool, level): + # update horizontal line in graph + self.temperature_chart.set_target_temperature(tool, level) + # register event for controller + self.register_event(events.UpdateTargetTemperature(tool, level)) + self.heating_control = HeatingControl(settings_frame, on_update=update_heating_temp) + + # G-code Execution Section + self.gcode_frame = ttk.Frame(bottom_frame, padding="15", style='DarkFrame.TFrame') + self.gcode_viewer = GcodeFrame(self.gcode_frame) + self.gcode_frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5) + + self.status_label.pack(side=tk.BOTTOM, fill=tk.X) + + def run(self): + """TODO""" + self._periodic_update() + self.root.mainloop() + + def handle(self, event: events.Event): + match event: + case events.UpdateNozzleTemperature(tool=tool, temperature=temp): + self.temperature_chart.add_temperatures({tool: temp}) + case events.NewGcodeFileHandler(handler=handler): + self.gcode_viewer.set_fileHandler(handler) + case events.SetGcodeLine(line=line): + self.gcode_viewer.set_gcode_pointer(line) + case events.ArduinoConnected(): + self.status_label.config(text="Connected", fg="green") + case events.ArduinoDisconnected(): + self.status_label.config(text="Disconnected", fg="red") + case _: + raise NotImplementedError("Event not caught: " + str(event)) + + + def register_event(self, event: events.Event): + self.registered_events.append(event) + + def update(self): + """ + Updates the GUI. This is typically handled by the Tkinter event loop, + but can be called explicitly for immediate redraws if necessary. + """ + self.root.update_idletasks() + + # return all registered events and clear + cpy = self.registered_events + self.registered_events = [] + return cpy + + def _periodic_update(self): + """ + Performs periodic updates for the controller and UI. + This simulates continuous data flow and G-code progression. + """ + if self._running: + self._on_update() + + # Schedule the next update + if self._running: + self._update_job_id = self.root.after(500, self._periodic_update) + else: + print("UI is not running, stopping periodic updates.") + + def _on_closing(self): + self._running = False + self.temperature_chart.close() # Close Matplotlib figure + self.root.destroy() + if self._update_job_id: + self.root.after_cancel(self._update_job_id) \ No newline at end of file diff --git a/plugins/USBPrinting/__init__.py b/plugins/USBPrinting/__init__.py index 075ad2943b..4934897e17 100644 --- a/plugins/USBPrinting/__init__.py +++ b/plugins/USBPrinting/__init__.py @@ -9,6 +9,5 @@ def getMetaData(): def register(app): - # We are violating the QT API here (as we use a factory, which is technically not allowed). - # but we don't really have another means for doing this (and it seems to you know -work-) - return {"output_device": USBPrinterOutputDeviceManager.USBPrinterOutputDeviceManager(app)} + # USBPrinting plugin disabled: do not register any output devices + return {} diff --git a/resources/themes/cura-dark-colorblind/theme.json b/resources/themes/cura-dark-colorblind/theme.json index 4a006ee836..df9b65f03a 100644 --- a/resources/themes/cura-dark-colorblind/theme.json +++ b/resources/themes/cura-dark-colorblind/theme.json @@ -1,26 +1 @@ -{ - "metadata": { - "name": "Colorblind Assist Dark", - "inherits": "cura-dark" - }, - - "colors": { - "x_axis": [212, 0, 0, 255], - "y_axis": [64, 64, 255, 255], - - "model_overhang": [200, 0, 255, 255], - - "xray": [26, 26, 62, 255], - "xray_error": [255, 0, 0, 255], - - "layerview_inset_0": [255, 64, 0, 255], - "layerview_inset_x": [0, 156, 128, 255], - "layerview_skin": [255, 255, 86, 255], - "layerview_support": [255, 255, 0, 255], - - "layerview_infill": [0, 255, 255, 255], - "layerview_support_infill": [0, 200, 200, 255], - - "layerview_move_retraction": [0, 100, 255, 255] - } -} +{"metadata": {"name": "Colorblind Assist Dark", "inherits": "cura-dark"}, "colors": {"x_axis": [212, 0, 0, 255], "y_axis": [64, 64, 255, 255], "model_overhang": [200, 0, 255, 255], "xray": [26, 26, 62, 255], "xray_error": [255, 0, 0, 255], "layerview_inset_0": [255, 64, 0, 255], "layerview_inset_x": [0, 156, 128, 255], "layerview_skin": [255, 255, 86, 255], "layerview_support": [255, 255, 0, 255], "layerview_infill": [0, 255, 255, 255], "layerview_support_infill": [0, 200, 200, 255], "layerview_move_retraction": [0, 100, 255, 255], "main_window_header_background": [126, 196, 193, 255]}} \ No newline at end of file diff --git a/resources/themes/cura-dark/theme.json b/resources/themes/cura-dark/theme.json index 1517b22eb9..9c65fb9d7d 100644 --- a/resources/themes/cura-dark/theme.json +++ b/resources/themes/cura-dark/theme.json @@ -1,212 +1 @@ -{ - "metadata": { - "name": "UltiMaker Dark", - "inherits": "cura-light" - }, - - "base_colors": - { - "background_1": [31, 31, 32, 255], - "background_2": [57, 57, 58, 255], - "background_3": [85, 85, 87, 255], - "background_4": [23, 23, 23, 255], - - "accent_1": [25, 110, 240, 255], - "accent_2": [16, 70, 156, 255], - "border_main": [118, 118, 119, 255], - "border_accent_1": [255, 255, 255, 255], - "border_accent_2": [243, 243, 243, 255], - "border_field": [57, 57, 58, 255], - - "text_default": [255, 255, 255, 255], - "text_disabled": [118, 118, 118, 255], - "text_primary_button": [255, 255, 255, 255], - "text_secondary_button": [255, 255, 255, 255], - "text_link_hover": [156, 195, 255, 255], - "text_lighter": [243, 243, 243, 255], - - "um_green_1": [233, 245, 237, 255], - "um_green_5": [36, 162, 73, 255], - "um_green_9": [31, 44, 36, 255], - "um_red_1": [251, 232, 233, 255], - "um_red_5": [218, 30, 40, 255], - "um_red_9": [59, 31, 33, 255], - "um_orange_1": [255, 235, 221, 255], - "um_orange_5": [252, 123, 30, 255], - "um_orange_9": [64, 45, 32, 255], - "um_yellow_1": [255, 248, 225, 255], - "um_yellow_5": [253, 209, 58, 255], - "um_yellow_9": [64, 58, 36, 255] - }, - - "colors": { - "main_background": "background_1", - "detail_background": "background_2", - "message_background": "background_1", - "wide_lining": [31, 36, 39, 255], - "thick_lining": [255, 255, 255, 60], - "lining": "border_main", - "viewport_overlay": "background_1", - - "primary_text": "text_default", - "secondary": [95, 95, 95, 255], - - "expandable_active": "background_2", - "expandable_hover": "background_2", - - "secondary_button": "background_1", - "secondary_button_hover": "background_3", - "secondary_button_text": "text_secondary_button", - - "icon": "text_default", - "toolbar_background": "background_1", - "toolbar_button_active": "background_3", - "toolbar_button_hover": "background_3", - "toolbar_button_active_hover": "background_3", - - "main_window_header_button_background_inactive": "background_4", - "main_window_header_button_text_inactive": "text_primary_button", - "main_window_header_button_text_active": "background_4", - "main_window_header_background": "background_4", - "main_window_header_background_gradient": "background_4", - "main_window_header_button_background_hovered": [46, 46, 46, 255], - - "account_sync_state_icon": [255, 255, 255, 204], - - "machine_selector_printer_icon": [204, 204, 204, 255], - - "text": "text_default", - "text_detail": [255, 255, 255, 172], - "text_link": "accent_1", - "text_inactive": [118, 118, 118, 255], - "text_hover": [255, 255, 255, 255], - "text_scene": [250, 250, 250, 255], - "text_scene_hover": [255, 255, 255, 255], - - "printer_type_label_background": [95, 95, 95, 255], - - "error": [212, 31, 53, 255], - "disabled": [32, 32, 32, 255], - - "button": [39, 44, 48, 255], - "button_hover": [39, 44, 48, 255], - "button_text": "text_default", - "button_disabled": [39, 44, 48, 255], - "button_disabled_text": [255, 255, 255, 101], - - "small_button_text": [255, 255, 255, 197], - "small_button_text_hover": [255, 255, 255, 255], - - "button_tooltip": [39, 44, 48, 255], - - "tab_checked": [39, 44, 48, 255], - "tab_checked_border": [255, 255, 255, 30], - "tab_checked_text": [255, 255, 255, 255], - "tab_unchecked": [39, 44, 48, 255], - "tab_unchecked_border": [255, 255, 255, 30], - "tab_unchecked_text": [255, 255, 255, 101], - "tab_hovered": [39, 44, 48, 255], - "tab_hovered_border": [255, 255, 255, 30], - "tab_hovered_text": [255, 255, 255, 255], - "tab_active": [39, 44, 48, 255], - "tab_active_border": [255, 255, 255, 30], - "tab_active_text": [255, 255, 255, 255], - "tab_background": [39, 44, 48, 255], - - "action_button": "background_1", - "action_button_text": [255, 255, 255, 200], - "action_button_border": "border_main", - "action_button_hovered": [79, 85, 89, 255], - "action_button_hovered_text": "text_default", - "action_button_hovered_border": "border_main", - "action_button_active": [39, 44, 48, 30], - "action_button_active_text": "text_default", - "action_button_active_border": [255, 255, 255, 100], - "action_button_disabled": "background_3", - "action_button_disabled_text": "text_disabled", - "action_button_disabled_border": [255, 255, 255, 30], - - "scrollbar_background": [39, 44, 48, 0], - "scrollbar_handle": [255, 255, 255, 105], - "scrollbar_handle_hover": [255, 255, 255, 255], - "scrollbar_handle_down": [255, 255, 255, 255], - - "setting_category_disabled": [75, 80, 83, 255], - "setting_category_disabled_text": [255, 255, 255, 101], - - "setting_control": "background_2", - "setting_control_selected": [34, 39, 42, 38], - "setting_control_highlight": "background_3", - "setting_control_border": [255, 255, 255, 38], - "setting_control_border_highlight": [12, 169, 227, 255], - "setting_control_text": "text_default", - "setting_control_button": [255, 255, 255, 127], - "setting_control_button_hover": [255, 255, 255, 204], - "setting_control_disabled": [34, 39, 42, 255], - "setting_control_disabled_text": [255, 255, 255, 101], - "setting_control_disabled_border": [255, 255, 255, 101], - "setting_unit": [255, 255, 255, 127], - "setting_validation_error_background": "um_red_9", - "setting_validation_warning_background": "um_yellow_9", - "setting_validation_ok": "background_2", - - "progressbar_background": [255, 255, 255, 48], - "progressbar_control": [255, 255, 255, 197], - - "slider_groove": [127, 127, 127, 255], - "slider_groove_border": [127, 127, 127, 255], - "slider_groove_fill": [245, 245, 245, 255], - "slider_handle": [255, 255, 255, 255], - "slider_handle_active": [68, 192, 255, 255], - - "category_background": "background_3", - - "tooltip": "background_2", - "tooltip_text": "text_default", - - "tool_panel_background": "background_1", - - "viewport_background": "background_1", - "volume_outline": [12, 169, 227, 128], - "buildplate": [169, 169, 169, 255], - "buildplate_grid_minor": [154, 154, 155, 255], - - "disallowed_area": [0, 0, 0, 52], - - "model_selection_outline": [12, 169, 227, 255], - - "material_compatibility_warning": [255, 255, 255, 255], - - "core_compatibility_warning": [255, 255, 255, 255], - - "quality_slider_available": [255, 255, 255, 255], - - "monitor_printer_family_tag": [86, 86, 106, 255], - "monitor_text_disabled": [102, 102, 102, 255], - "monitor_icon_primary": [229, 229, 229, 255], - "monitor_icon_accent": [51, 53, 54, 255], - "monitor_icon_disabled": [102, 102, 102, 255], - - "monitor_secondary_button_hover": [80, 80, 80, 255], - "monitor_card_border": [102, 102, 102, 255], - "monitor_card_background": [51, 53, 54, 255], - "monitor_card_hover": [84, 89, 95, 255], - - "monitor_stage_background": "background_1", - "monitor_stage_background_fade": "background_1", - - "monitor_progress_bar_deactive": [102, 102, 102, 255], - "monitor_progress_bar_empty": [67, 67, 67, 255], - - "monitor_tooltip_text": [229, 229, 229, 255], - "monitor_context_menu": [67, 67, 67, 255], - "monitor_context_menu_hover": [30, 102, 215, 255], - - "monitor_skeleton_loading": [102, 102, 102, 255], - "monitor_placeholder_image": [102, 102, 102, 255], - "monitor_shadow": [4, 10, 13, 255], - - "monitor_carousel_dot": [119, 119, 119, 255], - "monitor_carousel_dot_current": [216, 216, 216, 255] - } -} +{"metadata": {"name": "UltiMaker Dark", "inherits": "cura-light"}, "base_colors": {"background_1": [31, 31, 32, 255], "background_2": [57, 57, 58, 255], "background_3": [85, 85, 87, 255], "background_4": [23, 23, 23, 255], "accent_1": [25, 110, 240, 255], "accent_2": [16, 70, 156, 255], "border_main": [118, 118, 119, 255], "border_accent_1": [255, 255, 255, 255], "border_accent_2": [243, 243, 243, 255], "border_field": [57, 57, 58, 255], "text_default": [255, 255, 255, 255], "text_disabled": [118, 118, 118, 255], "text_primary_button": [255, 255, 255, 255], "text_secondary_button": [255, 255, 255, 255], "text_link_hover": [156, 195, 255, 255], "text_lighter": [243, 243, 243, 255], "um_green_1": [233, 245, 237, 255], "um_green_5": [36, 162, 73, 255], "um_green_9": [31, 44, 36, 255], "um_red_1": [251, 232, 233, 255], "um_red_5": [218, 30, 40, 255], "um_red_9": [59, 31, 33, 255], "um_orange_1": [255, 235, 221, 255], "um_orange_5": [252, 123, 30, 255], "um_orange_9": [64, 45, 32, 255], "um_yellow_1": [255, 248, 225, 255], "um_yellow_5": [253, 209, 58, 255], "um_yellow_9": [64, 58, 36, 255]}, "colors": {"main_background": "background_1", "detail_background": "background_2", "message_background": "background_1", "wide_lining": [31, 36, 39, 255], "thick_lining": [255, 255, 255, 60], "lining": "border_main", "viewport_overlay": "background_1", "primary_text": "text_default", "secondary": [95, 95, 95, 255], "expandable_active": "background_2", "expandable_hover": "background_2", "secondary_button": "background_1", "secondary_button_hover": "background_3", "secondary_button_text": "text_secondary_button", "icon": "text_default", "toolbar_background": "background_1", "toolbar_button_active": "background_3", "toolbar_button_hover": "background_3", "toolbar_button_active_hover": "background_3", "main_window_header_button_background_inactive": "background_4", "main_window_header_button_text_inactive": "text_primary_button", "main_window_header_button_text_active": "background_4", "main_window_header_background": [126, 196, 193, 255], "main_window_header_background_gradient": "background_4", "main_window_header_button_background_hovered": [46, 46, 46, 255], "account_sync_state_icon": [255, 255, 255, 204], "machine_selector_printer_icon": [204, 204, 204, 255], "text": "text_default", "text_detail": [255, 255, 255, 172], "text_link": "accent_1", "text_inactive": [118, 118, 118, 255], "text_hover": [255, 255, 255, 255], "text_scene": [250, 250, 250, 255], "text_scene_hover": [255, 255, 255, 255], "printer_type_label_background": [95, 95, 95, 255], "error": [212, 31, 53, 255], "disabled": [32, 32, 32, 255], "button": [39, 44, 48, 255], "button_hover": [39, 44, 48, 255], "button_text": "text_default", "button_disabled": [39, 44, 48, 255], "button_disabled_text": [255, 255, 255, 101], "small_button_text": [255, 255, 255, 197], "small_button_text_hover": [255, 255, 255, 255], "button_tooltip": [39, 44, 48, 255], "tab_checked": [39, 44, 48, 255], "tab_checked_border": [255, 255, 255, 30], "tab_checked_text": [255, 255, 255, 255], "tab_unchecked": [39, 44, 48, 255], "tab_unchecked_border": [255, 255, 255, 30], "tab_unchecked_text": [255, 255, 255, 101], "tab_hovered": [39, 44, 48, 255], "tab_hovered_border": [255, 255, 255, 30], "tab_hovered_text": [255, 255, 255, 255], "tab_active": [39, 44, 48, 255], "tab_active_border": [255, 255, 255, 30], "tab_active_text": [255, 255, 255, 255], "tab_background": [39, 44, 48, 255], "action_button": "background_1", "action_button_text": [255, 255, 255, 200], "action_button_border": "border_main", "action_button_hovered": [79, 85, 89, 255], "action_button_hovered_text": "text_default", "action_button_hovered_border": "border_main", "action_button_active": [39, 44, 48, 30], "action_button_active_text": "text_default", "action_button_active_border": [255, 255, 255, 100], "action_button_disabled": "background_3", "action_button_disabled_text": "text_disabled", "action_button_disabled_border": [255, 255, 255, 30], "scrollbar_background": [39, 44, 48, 0], "scrollbar_handle": [255, 255, 255, 105], "scrollbar_handle_hover": [255, 255, 255, 255], "scrollbar_handle_down": [255, 255, 255, 255], "setting_category_disabled": [75, 80, 83, 255], "setting_category_disabled_text": [255, 255, 255, 101], "setting_control": "background_2", "setting_control_selected": [34, 39, 42, 38], "setting_control_highlight": "background_3", "setting_control_border": [255, 255, 255, 38], "setting_control_border_highlight": [12, 169, 227, 255], "setting_control_text": "text_default", "setting_control_button": [255, 255, 255, 127], "setting_control_button_hover": [255, 255, 255, 204], "setting_control_disabled": [34, 39, 42, 255], "setting_control_disabled_text": [255, 255, 255, 101], "setting_control_disabled_border": [255, 255, 255, 101], "setting_unit": [255, 255, 255, 127], "setting_validation_error_background": "um_red_9", "setting_validation_warning_background": "um_yellow_9", "setting_validation_ok": "background_2", "progressbar_background": [255, 255, 255, 48], "progressbar_control": [255, 255, 255, 197], "slider_groove": [127, 127, 127, 255], "slider_groove_border": [127, 127, 127, 255], "slider_groove_fill": [245, 245, 245, 255], "slider_handle": [255, 255, 255, 255], "slider_handle_active": [68, 192, 255, 255], "category_background": "background_3", "tooltip": "background_2", "tooltip_text": "text_default", "tool_panel_background": "background_1", "viewport_background": "background_1", "volume_outline": [12, 169, 227, 128], "buildplate": [169, 169, 169, 255], "buildplate_grid_minor": [154, 154, 155, 255], "disallowed_area": [0, 0, 0, 52], "model_selection_outline": [12, 169, 227, 255], "material_compatibility_warning": [255, 255, 255, 255], "core_compatibility_warning": [255, 255, 255, 255], "quality_slider_available": [255, 255, 255, 255], "monitor_printer_family_tag": [86, 86, 106, 255], "monitor_text_disabled": [102, 102, 102, 255], "monitor_icon_primary": [229, 229, 229, 255], "monitor_icon_accent": [51, 53, 54, 255], "monitor_icon_disabled": [102, 102, 102, 255], "monitor_secondary_button_hover": [80, 80, 80, 255], "monitor_card_border": [102, 102, 102, 255], "monitor_card_background": [51, 53, 54, 255], "monitor_card_hover": [84, 89, 95, 255], "monitor_stage_background": "background_1", "monitor_stage_background_fade": "background_1", "monitor_progress_bar_deactive": [102, 102, 102, 255], "monitor_progress_bar_empty": [67, 67, 67, 255], "monitor_tooltip_text": [229, 229, 229, 255], "monitor_context_menu": [67, 67, 67, 255], "monitor_context_menu_hover": [30, 102, 215, 255], "monitor_skeleton_loading": [102, 102, 102, 255], "monitor_placeholder_image": [102, 102, 102, 255], "monitor_shadow": [4, 10, 13, 255], "monitor_carousel_dot": [119, 119, 119, 255], "monitor_carousel_dot_current": [216, 216, 216, 255]}} \ No newline at end of file diff --git a/resources/themes/cura-light-colorblind/theme.json b/resources/themes/cura-light-colorblind/theme.json index 740bf977b2..c4fc89954c 100644 --- a/resources/themes/cura-light-colorblind/theme.json +++ b/resources/themes/cura-light-colorblind/theme.json @@ -1,29 +1 @@ -{ - "metadata": { - "name": "Colorblind Assist Light", - "inherits": "cura-light" - }, - - "colors": { - - "x_axis": [200, 0, 0, 255], - "y_axis": [64, 64, 255, 255], - "model_overhang": [200, 0, 255, 255], - "model_selection_outline": [12, 169, 227, 255], - - "xray_error_dark": [255, 0, 0, 255], - "xray_error_light": [255, 255, 0, 255], - "xray": [26, 26, 62, 255], - "xray_error": [255, 0, 0, 255], - - "layerview_inset_0": [255, 64, 0, 255], - "layerview_inset_x": [0, 156, 128, 255], - "layerview_skin": [255, 255, 86, 255], - "layerview_support": [255, 255, 0, 255], - - "layerview_infill": [0, 255, 255, 255], - "layerview_support_infill": [0, 200, 200, 255], - - "layerview_move_retraction": [0, 100, 255, 255] - } -} +{"metadata": {"name": "Colorblind Assist Light", "inherits": "cura-light"}, "colors": {"x_axis": [200, 0, 0, 255], "y_axis": [64, 64, 255, 255], "model_overhang": [200, 0, 255, 255], "model_selection_outline": [12, 169, 227, 255], "xray_error_dark": [255, 0, 0, 255], "xray_error_light": [255, 255, 0, 255], "xray": [26, 26, 62, 255], "xray_error": [255, 0, 0, 255], "layerview_inset_0": [255, 64, 0, 255], "layerview_inset_x": [0, 156, 128, 255], "layerview_skin": [255, 255, 86, 255], "layerview_support": [255, 255, 0, 255], "layerview_infill": [0, 255, 255, 255], "layerview_support_infill": [0, 200, 200, 255], "layerview_move_retraction": [0, 100, 255, 255], "main_window_header_background": [126, 196, 193, 255]}} \ No newline at end of file diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index d561f7e6dc..3ff026ec3c 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -1,694 +1 @@ -{ - "metadata": { - "name": "UltiMaker" - }, - - "fonts": { - "large": { - "size": 1.35, - "weight": 400, - "family": "Noto Sans" - }, - "large_ja_JP": { - "size": 1.35, - "weight": 400, - "family": "Noto Sans" - }, - "large_zh_CN": { - "size": 1.35, - "weight": 400, - "family": "Noto Sans" - }, - "large_zh_TW": { - "size": 1.35, - "weight": 400, - "family": "Noto Sans" - }, - "large_bold": { - "size": 1.35, - "weight": 600, - "family": "Noto Sans" - }, - "huge": { - "size": 1.8, - "weight": 400, - "family": "Noto Sans" - }, - "huge_bold": { - "size": 1.8, - "weight": 600, - "family": "Noto Sans" - }, - "medium": { - "size": 1.16, - "weight": 400, - "family": "Noto Sans" - }, - "medium_ja_JP": { - "size": 1.16, - "weight": 400, - "family": "Noto Sans" - }, - "medium_zh_CN": { - "size": 1.16, - "weight": 400, - "family": "Noto Sans" - }, - "medium_zh_TW": { - "size": 1.16, - "weight": 400, - "family": "Noto Sans" - }, - "medium_bold": { - "size": 1.16, - "weight": 600, - "family": "Noto Sans" - }, - "default": { - "size": 0.95, - "weight": 400, - "family": "Noto Sans" - }, - "default_ja_JP": { - "size": 1.0, - "weight": 400, - "family": "Noto Sans" - }, - "default_zh_CN": { - "size": 1.0, - "weight": 400, - "family": "Noto Sans" - }, - "default_zh_TW": { - "size": 1.0, - "weight": 400, - "family": "Noto Sans" - }, - "default_bold": { - "size": 0.95, - "weight": 600, - "family": "Noto Sans" - }, - "default_bold_ja_JP": { - "size": 1.0, - "weight": 600, - "family": "Noto Sans" - }, - "default_bold_zh_CN": { - "size": 1.0, - "weight": 600, - "family": "Noto Sans" - }, - "default_bold_zh_TW": { - "size": 1.0, - "weight": 600, - "family": "Noto Sans" - }, - "default_italic": { - "size": 0.95, - "weight": 400, - "italic": true, - "family": "Noto Sans" - }, - "medium_italic": { - "size": 1.16, - "weight": 400, - "italic": true, - "family": "Noto Sans" - }, - "default_italic_ja_JP": { - "size": 1.0, - "weight": 400, - "italic": true, - "family": "Noto Sans" - }, - "default_italic_zh_CN": { - "size": 1.0, - "weight": 400, - "italic": true, - "family": "Noto Sans" - }, - "default_italic_zh_TW": { - "size": 1.0, - "weight": 400, - "italic": true, - "family": "Noto Sans" - }, - "small": { - "size": 0.9, - "weight": 400, - "family": "Noto Sans" - }, - "small_bold": { - "size": 0.9, - "weight": 700, - "family": "Noto Sans" - }, - "small_ja_JP": { - "size": 0.9, - "weight": 400, - "family": "Noto Sans" - }, - "small_zh_CN": { - "size": 0.9, - "weight": 400, - "family": "Noto Sans" - }, - "small_zh_TW": { - "size": 0.9, - "weight": 400, - "family": "Noto Sans" - }, - "small_emphasis": { - "size": 0.9, - "weight": 700, - "family": "Noto Sans" - }, - "small_emphasis_ja_JP": { - "size": 0.9, - "weight": 700, - "family": "Noto Sans" - }, - "small_emphasis_zh_CN": { - "size": 0.9, - "weight": 700, - "family": "Noto Sans" - }, - "small_emphasis_zh_TW": { - "size": 0.9, - "weight": 700, - "family": "Noto Sans" - }, - "tiny_emphasis": { - "size": 0.7, - "weight": 700, - "family": "Noto Sans" - }, - "tiny_emphasis_ja_JP": { - "size": 0.7, - "weight": 700, - "family": "Noto Sans" - }, - "tiny_emphasis_zh_CN": { - "size": 0.7, - "weight": 700, - "family": "Noto Sans" - }, - "tiny_emphasis_zh_TW": { - "size": 0.7, - "weight": 700, - "family": "Noto Sans" - } - }, - - "base_colors": { - "background_1": [255, 255, 255, 255], - "background_2": [243, 243, 243, 255], - "background_3": [232, 240, 253, 255], - "background_4": [3, 12, 66, 255], - - "accent_1": [25, 110, 240, 255], - "accent_2": [16, 70, 156, 255], - "border_main": [212, 212, 212, 255], - "border_accent_1": [25, 110, 240, 255], - "border_accent_2": [16, 70, 156, 255], - "border_field": [180, 180, 180, 255], - - "text_default": [0, 14, 26, 255], - "text_disabled": [180, 180, 180, 255], - "text_primary_button": [255, 255, 255, 255], - "text_secondary_button": [25, 110, 240, 255], - "text_link_hover": [16, 70, 156, 255], - "text_lighter": [108, 108, 108, 255], - - "um_green_1": [233, 245, 237, 255], - "um_green_5": [36, 162, 73, 255], - "um_green_9": [31, 44, 36, 255], - "um_red_1": [251, 232, 233, 255], - "um_red_5": [218, 30, 40, 255], - "um_red_9": [59, 31, 33, 255], - "um_orange_1": [255, 235, 221, 255], - "um_orange_5": [252, 123, 30, 255], - "um_orange_9": [64, 45, 32, 255], - "um_yellow_1": [255, 248, 225, 255], - "um_yellow_5": [253, 209, 58, 255], - "um_yellow_9": [64, 58, 36, 255] - }, - - "colors": { - - "main_background": "background_1", - "detail_background": "background_2", - "wide_lining": [245, 245, 245, 255], - "thick_lining": [180, 180, 180, 255], - "lining": [192, 193, 194, 255], - "viewport_overlay": [246, 246, 246, 255], - - "primary": "accent_1", - "primary_hover": [48, 182, 231, 255], - "primary_text": [255, 255, 255, 255], - "text_selection": [156, 195, 255, 127], - "border": [127, 127, 127, 255], - "border_field": [180, 180, 180, 255], - "secondary": [240, 240, 240, 255], - - "expandable_active": [240, 240, 240, 255], - "expandable_hover": [232, 242, 252, 255], - - "icon": [8, 7, 63, 255], - - "primary_button": "accent_1", - "primary_button_hover": [16, 70, 156, 255], - "primary_button_text": [255, 255, 255, 255], - - "secondary_button": "background_1", - "secondary_button_shadow": [216, 216, 216, 255], - "secondary_button_hover": [232, 240, 253, 255], - "secondary_button_text": "accent_1", - - "main_window_header_background": [8, 7, 63, 255], - "main_window_header_background_gradient": [25, 23, 91, 255], - "main_window_header_button_text_active": [8, 7, 63, 255], - "main_window_header_button_text_inactive": [255, 255, 255, 255], - "main_window_header_button_text_hovered": [255, 255, 255, 255], - "main_window_header_button_background_active": [255, 255, 255, 255], - "main_window_header_button_background_inactive": [255, 255, 255, 0], - "main_window_header_button_background_hovered": [117, 114, 159, 255], - - "account_widget_outline_active": [70, 66, 126, 255], - "account_sync_state_icon": [25, 25, 25, 255], - - "machine_selector_printer_icon": [8, 7, 63, 255], - - "action_panel_secondary": "accent_1", - - "first_run_shadow": [50, 50, 50, 255], - - "toolbar_background": [255, 255, 255, 255], - - "notification_icon": [255, 0, 0, 255], - - "printer_type_label_background": [228, 228, 242, 255], - - "window_disabled_background": [0, 0, 0, 255], - - "text": [25, 25, 25, 255], - "text_disabled": [180, 180, 180, 255], - "text_detail": [174, 174, 174, 128], - "text_link": "accent_1", - "text_inactive": [174, 174, 174, 255], - "text_medium": [128, 128, 128, 255], - "text_scene": [102, 102, 102, 255], - "text_scene_hover": [123, 123, 113, 255], - - "error": [218, 30, 40, 255], - "warning": [253, 209, 58, 255], - "success": [36, 162, 73, 255], - "disabled": [229, 229, 229, 255], - - "toolbar_button_hover": [232, 242, 252, 255], - "toolbar_button_active": [232, 242, 252, 255], - "toolbar_button_active_hover": [232, 242, 252, 255], - - "button_text": [255, 255, 255, 255], - - "small_button_text": [102, 102, 102, 255], - "small_button_text_hover": [8, 7, 63, 255], - - "button_tooltip": [31, 36, 39, 255], - - "extruder_disabled": [255, 255, 255, 102], - - "action_button": [255, 255, 255, 255], - "action_button_hovered": [232, 242, 252, 255], - "action_button_disabled": [245, 245, 245, 255], - "action_button_disabled_text": [196, 196, 196, 255], - "action_button_shadow": [223, 223, 223, 255], - - "scrollbar_background": [255, 255, 255, 255], - "scrollbar_handle": [10, 8, 80, 255], - "scrollbar_handle_hover": [50, 130, 255, 255], - "scrollbar_handle_down": [50, 130, 255, 255], - - "setting_category": "background_1", - "setting_category_disabled": [255, 255, 255, 255], - "setting_category_hover": "background_2", - "setting_category_text": "text_default", - "setting_category_disabled_text": [24, 41, 77, 101], - "setting_category_active_text": "text_default", - - "setting_control": "background_2", - "setting_control_highlight": "background_3", - "setting_control_border": [199, 199, 199, 255], - "setting_control_border_highlight": [50, 130, 255, 255], - "setting_control_text": [35, 35, 35, 255], - "setting_control_button": [102, 102, 102, 255], - "setting_control_button_hover": [8, 7, 63, 255], - "setting_control_disabled": "background_2", - "setting_control_disabled_text": [127, 127, 127, 255], - "setting_control_disabled_border": [127, 127, 127, 255], - "setting_unit": [127, 127, 127, 255], - "setting_validation_error_background": "um_red_1", - "setting_validation_error": "um_red_5", - "setting_validation_warning_background": "um_yellow_1", - "setting_validation_warning": "um_yellow_5", - "setting_validation_ok": "background_2", - - "material_compatibility_warning": [243, 166, 59, 255], - "core_compatibility_warning": [243, 166, 59, 255], - - "progressbar_background": [245, 245, 245, 255], - "progressbar_control": [50, 130, 255, 255], - - "slider_groove": [223, 223, 223, 255], - "slider_groove_fill": [8, 7, 63, 255], - "slider_handle": [8, 7, 63, 255], - "slider_handle_active": [50, 130, 255, 255], - "slider_text_background": [255, 255, 255, 255], - - "quality_slider_unavailable": [179, 179, 179, 255], - "quality_slider_available": [0, 0, 0, 255], - - "checkbox": "background_1", - "checkbox_hover": "background_1", - "checkbox_disabled": "background_2", - "checkbox_border": [180, 180, 180, 255], - "checkbox_border_hover": "border_main", - "checkbox_border_disabled": "text_disabled", - "checkbox_mark": "text_default", - "checkbox_mark_disabled": "text_disabled", - "checkbox_square": [180, 180, 180, 255], - "checkbox_text": "text_default", - "checkbox_text_disabled": "text_disabled", - - "switch": "background_1", - "switch_state_checked": "accent_1", - "switch_state_unchecked": "text_disabled", - - "radio": "background_1", - "radio_disabled": "background_2", - "radio_selected": "accent_1", - "radio_selected_disabled": "text_disabled", - "radio_border": [180, 180, 180, 255], - "radio_border_hover": "border_main", - "radio_border_disabled": "text_disabled", - "radio_dot": "background_1", - "radio_dot_disabled": "background_2", - "radio_text": "text_default", - "radio_text_disabled": "text_disabled", - - "text_field": "background_1", - "text_field_border": [180, 180, 180, 255], - "text_field_border_hovered": "border_main", - "text_field_border_active": "border_accent_2", - "text_field_border_disabled": "background_2", - "text_field_text": "text_default", - "text_field_text_disabled": "text_disabled", - - "category_background": "background_2", - - "tooltip": [25, 25, 25, 255], - "tooltip_text": [255, 255, 255, 255], - - "message_background": [255, 255, 255, 255], - "message_border": [192, 193, 194, 255], - "message_close": [102, 102, 102, 255], - "message_close_hover": [8, 7, 63, 255], - "message_progressbar_background": [245, 245, 245, 255], - "message_progressbar_control": [50, 130, 255, 255], - "message_success_icon": [255, 255, 255, 255], - "message_warning_icon": [0, 0, 0, 255], - "message_error_icon": [255, 255, 255, 255], - - "tool_panel_background": [255, 255, 255, 255], - - "status_offline": [0, 0, 0, 255], - "status_ready": [0, 205, 0, 255], - "status_busy": [50, 130, 255, 255], - "status_paused": [255, 140, 0, 255], - "status_stopped": [236, 82, 80, 255], - - "disabled_axis": [127, 127, 127, 255], - "x_axis": [218, 30, 40, 255], - "y_axis": [25, 110, 240, 255], - "z_axis": [36, 162, 73, 255], - "all_axis": [255, 255, 255, 255], - - "viewport_background": [250, 250, 250, 255], - "volume_outline": [50, 130, 255, 255], - "buildplate": [244, 244, 244, 255], - "buildplate_grid": [180, 180, 180, 255], - "buildplate_grid_minor": [228, 228, 228, 255], - - "convex_hull": [35, 35, 35, 127], - "disallowed_area": [0, 0, 0, 40], - "error_area": [255, 0, 0, 127], - - "model_overhang": [255, 0, 0, 255], - "model_unslicable": [122, 122, 122, 255], - "model_unslicable_alt": [172, 172, 127, 255], - "model_selection_outline": [50, 130, 255, 255], - "model_non_printing": [122, 122, 122, 255], - - "xray": [26, 26, 62, 255], - - "layerview_ghost": [31, 31, 31, 95], - "layerview_none": [255, 255, 255, 255], - "layerview_inset_0": [230, 0, 0, 255], - "layerview_inset_x": [0, 230, 0, 255], - "layerview_skin": [230, 230, 0, 255], - "layerview_support": [0, 230, 230, 127], - "layerview_skirt": [0, 230, 230, 255], - "layerview_infill": [230, 115, 0, 255], - "layerview_support_infill": [0, 230, 230, 127], - "layerview_move_combing": [0, 0, 255, 255], - "layerview_move_retraction": [128, 127, 255, 255], - "layerview_move_while_retracting": [127, 255, 255, 255], - "layerview_move_while_unretracting": [255, 127, 255, 255], - "layerview_support_interface": [63, 127, 255, 127], - "layerview_prime_tower": [0, 255, 255, 255], - "layerview_nozzle": [224, 192, 16, 64], - "layerview_starts": [255, 255, 255, 255], - - - "monitor_printer_family_tag": [228, 228, 242, 255], - "monitor_text_disabled": [238, 238, 238, 255], - "monitor_icon_primary": [10, 8, 80, 255], - "monitor_icon_accent": [255, 255, 255, 255], - "monitor_icon_disabled": [238, 238, 238, 255], - - "monitor_card_border": [192, 193, 194, 255], - "monitor_card_background": [255, 255, 255, 255], - "monitor_card_hover": [232, 242, 252, 255], - - "monitor_stage_background": [246, 246, 246, 255], - "monitor_stage_background_fade": [246, 246, 246, 102], - - "monitor_tooltip": [25, 25, 25, 255], - "monitor_tooltip_text": [255, 255, 255, 255], - "monitor_context_menu": [255, 255, 255, 255], - "monitor_context_menu_hover": [245, 245, 245, 255], - - "monitor_skeleton_loading": [238, 238, 238, 255], - "monitor_placeholder_image": [230, 230, 230, 255], - "monitor_image_overlay": [0, 0, 0, 255], - "monitor_shadow": [200, 200, 200, 255], - - "monitor_carousel_dot": [216, 216, 216, 255], - "monitor_carousel_dot_current": [119, 119, 119, 255], - - "cloud_unavailable": [153, 153, 153, 255], - "cloud_inactive": [253, 209, 58, 255], - "connection_badge_background": [255, 255, 255, 255], - "warning_badge_background": [0, 0, 0, 255], - "error_badge_background": [255, 255, 255, 255], - - "border_field_light": [180, 180, 180, 255], - "border_main_light": [212, 212, 212, 255], - - "paint_normal_area": "background_3", - "paint_preferred_area": "um_green_5", - "paint_avoid_area": "um_red_5" - }, - - "sizes": { - "window_minimum_size": [80, 48], - "popup_dialog": [40, 36], - "small_popup_dialog": [36, 12], - - "main_window_header": [0.0, 4.0], - - "stage_menu": [0.0, 4.0], - - "account_button": [12, 2.5], - - "print_setup_widget": [38.0, 30.0], - "print_setup_extruder_box": [0.0, 6.0], - "slider_widget_groove": [0.16, 0.16], - "slider_widget_handle": [1.3, 1.3], - "slider_widget_tickmarks": [0.5, 0.5], - "print_setup_big_item": [28, 2.5], - "print_setup_icon": [1.2, 1.2], - "drag_icon": [1.416, 0.25], - - "application_switcher_item": [8, 9], - "application_switcher_icon": [3.75, 3.75], - - "expandable_component_content_header": [0.0, 3.0], - - "configuration_selector": [35.0, 4.0], - - "action_panel_widget": [26.0, 0.0], - "action_panel_information_widget": [20.0, 0.0], - - "machine_selector_widget": [20.0, 4.0], - "machine_selector_widget_content": [25.0, 32.0], - "machine_selector_icon": [2.5, 2.5], - - "views_selector": [16.0, 4.0], - - "printer_type_label": [3.5, 1.5], - - "default_radius": [0.25, 0.25], - - "wide_lining": [0.5, 0.5], - "thick_lining": [0.2, 0.2], - "default_lining": [0.08, 0.08], - - "default_arrow": [0.8, 0.8], - "logo": [16, 2], - - "wide_margin": [2.0, 2.0], - "thick_margin": [1.71, 1.43], - "default_margin": [1.0, 1.0], - "thin_margin": [0.71, 0.71], - "narrow_margin": [0.5, 0.5], - - "extruder_icon": [2.5, 2.5], - - "section": [0.0, 2], - "section_header": [0.0, 2.5], - - "section_control": [0, 1], - "section_icon": [1.5, 1.5], - "section_icon_column": [2.5, 2.5], - - "setting": [25.0, 1.8], - "setting_control": [9.0, 2.0], - "setting_control_radius": [0.15, 0.15], - "setting_control_depth_margin": [1.4, 0.0], - "setting_unit_margin": [0.5, 0.5], - - "standard_list_lineheight": [1.5, 1.5], - "standard_arrow": [1.0, 1.0], - - "card": [25.0, 10], - "card_icon": [6.0, 6.0], - "card_tiny_icon": [1.5, 1.5], - - "button": [4, 4], - "button_icon": [2.5, 2.5], - - "action_button": [15.0, 2.5], - "action_button_icon": [1.5, 1.5], - "action_button_icon_small": [1.0, 1.0], - "action_button_radius": [0.15, 0.15], - - "radio_button": [1.3, 1.3], - - "small_button": [2, 2], - "small_button_icon": [1.5, 1.5], - - "medium_button": [2.5, 2.5], - "medium_button_icon": [2, 2], - - "large_button": [3.0, 3.0], - "large_button_icon": [2.8, 2.8], - - "context_menu": [20, 2], - - "icon_indicator": [1, 1], - - "printer_status_icon": [1.0, 1.0], - - "button_tooltip": [1.0, 1.3], - "button_tooltip_arrow": [0.25, 0.25], - - "progressbar": [26.0, 0.75], - "progressbar_radius": [0.15, 0.15], - - "scrollbar": [0.75, 0.5], - - "slider_groove": [0.5, 0.5], - "slider_groove_radius": [0.15, 0.15], - "slider_handle": [1.5, 1.5], - "slider_layerview_size": [1.0, 34.0], - - "layerview_menu_size": [16.0, 4.0], - "layerview_legend_size": [1.0, 1.0], - "layerview_row": [11.0, 1.5], - "layerview_row_spacing": [0.0, 0.5], - - "checkbox": [1.33, 1.33], - "checkbox_mark": [1, 1], - "checkbox_radius": [0.25, 0.25], - - "spinbox": [6.0, 3.0], - "combobox": [14, 2], - "combobox_wide": [22, 2], - - "tooltip": [20.0, 10.0], - "tooltip_margins": [1.0, 1.0], - "tooltip_arrow_margins": [2.0, 2.0], - - "save_button_save_to_button": [0.3, 2.7], - "save_button_specs_icons": [1.4, 1.4], - - "first_run_shadow_radius": [1.2, 1.2], - - "monitor_preheat_temperature_control": [4.5, 2.0], - - "welcome_wizard_window": [46, 50], - "modal_window_minimum": [60.0, 50.0], - "wizard_progress": [10.0, 0.0], - - "message": [30.0, 5.0], - "message_close": [2, 2], - "message_radius": [0.25, 0.25], - "message_action_button": [0, 2.5], - "message_image": [15.0, 10.0], - "message_type_icon": [2, 2], - "menu": [18, 2], - - "jobspecs_line": [2.0, 2.0], - - "objects_menu_size": [15, 15], - - "notification_icon": [1.5, 1.5], - - "avatar_image": [6.8, 6.8], - - "monitor_shadow_radius": [0.4, 0.4], - "monitor_empty_state_offset": [5.6, 5.6], - "monitor_empty_state_size": [35.0, 25.0], - "monitor_column": [18.0, 1.0], - "monitor_progress_bar": [16.5, 1.0], - - "table_row": [2.0, 2.0], - - "welcome_wizard_content_image_big": [18, 15], - "welcome_wizard_cloud_content_image": [4, 4], - - "banner_icon_size": [2.0, 2.0], - - "marketplace_large_icon": [4.0, 4.0], - - "preferences_page_list_item": [8.0, 2.0], - - "recommended_button_icon": [1.7, 1.7], - - "recommended_section_setting_item": [14.0, 2.0], - - "reset_profile_icon": [1, 1] - } -} +{"metadata": {"name": "UltiMaker"}, "fonts": {"large": {"size": 1.35, "weight": 400, "family": "Noto Sans"}, "large_ja_JP": {"size": 1.35, "weight": 400, "family": "Noto Sans"}, "large_zh_CN": {"size": 1.35, "weight": 400, "family": "Noto Sans"}, "large_zh_TW": {"size": 1.35, "weight": 400, "family": "Noto Sans"}, "large_bold": {"size": 1.35, "weight": 600, "family": "Noto Sans"}, "huge": {"size": 1.8, "weight": 400, "family": "Noto Sans"}, "huge_bold": {"size": 1.8, "weight": 600, "family": "Noto Sans"}, "medium": {"size": 1.16, "weight": 400, "family": "Noto Sans"}, "medium_ja_JP": {"size": 1.16, "weight": 400, "family": "Noto Sans"}, "medium_zh_CN": {"size": 1.16, "weight": 400, "family": "Noto Sans"}, "medium_zh_TW": {"size": 1.16, "weight": 400, "family": "Noto Sans"}, "medium_bold": {"size": 1.16, "weight": 600, "family": "Noto Sans"}, "default": {"size": 0.95, "weight": 400, "family": "Noto Sans"}, "default_ja_JP": {"size": 1.0, "weight": 400, "family": "Noto Sans"}, "default_zh_CN": {"size": 1.0, "weight": 400, "family": "Noto Sans"}, "default_zh_TW": {"size": 1.0, "weight": 400, "family": "Noto Sans"}, "default_bold": {"size": 0.95, "weight": 600, "family": "Noto Sans"}, "default_bold_ja_JP": {"size": 1.0, "weight": 600, "family": "Noto Sans"}, "default_bold_zh_CN": {"size": 1.0, "weight": 600, "family": "Noto Sans"}, "default_bold_zh_TW": {"size": 1.0, "weight": 600, "family": "Noto Sans"}, "default_italic": {"size": 0.95, "weight": 400, "italic": true, "family": "Noto Sans"}, "medium_italic": {"size": 1.16, "weight": 400, "italic": true, "family": "Noto Sans"}, "default_italic_ja_JP": {"size": 1.0, "weight": 400, "italic": true, "family": "Noto Sans"}, "default_italic_zh_CN": {"size": 1.0, "weight": 400, "italic": true, "family": "Noto Sans"}, "default_italic_zh_TW": {"size": 1.0, "weight": 400, "italic": true, "family": "Noto Sans"}, "small": {"size": 0.9, "weight": 400, "family": "Noto Sans"}, "small_bold": {"size": 0.9, "weight": 700, "family": "Noto Sans"}, "small_ja_JP": {"size": 0.9, "weight": 400, "family": "Noto Sans"}, "small_zh_CN": {"size": 0.9, "weight": 400, "family": "Noto Sans"}, "small_zh_TW": {"size": 0.9, "weight": 400, "family": "Noto Sans"}, "small_emphasis": {"size": 0.9, "weight": 700, "family": "Noto Sans"}, "small_emphasis_ja_JP": {"size": 0.9, "weight": 700, "family": "Noto Sans"}, "small_emphasis_zh_CN": {"size": 0.9, "weight": 700, "family": "Noto Sans"}, "small_emphasis_zh_TW": {"size": 0.9, "weight": 700, "family": "Noto Sans"}, "tiny_emphasis": {"size": 0.7, "weight": 700, "family": "Noto Sans"}, "tiny_emphasis_ja_JP": {"size": 0.7, "weight": 700, "family": "Noto Sans"}, "tiny_emphasis_zh_CN": {"size": 0.7, "weight": 700, "family": "Noto Sans"}, "tiny_emphasis_zh_TW": {"size": 0.7, "weight": 700, "family": "Noto Sans"}}, "base_colors": {"background_1": [255, 255, 255, 255], "background_2": [243, 243, 243, 255], "background_3": [232, 240, 253, 255], "background_4": [3, 12, 66, 255], "accent_1": [25, 110, 240, 255], "accent_2": [16, 70, 156, 255], "border_main": [212, 212, 212, 255], "border_accent_1": [25, 110, 240, 255], "border_accent_2": [16, 70, 156, 255], "border_field": [180, 180, 180, 255], "text_default": [0, 14, 26, 255], "text_disabled": [180, 180, 180, 255], "text_primary_button": [255, 255, 255, 255], "text_secondary_button": [25, 110, 240, 255], "text_link_hover": [16, 70, 156, 255], "text_lighter": [108, 108, 108, 255], "um_green_1": [233, 245, 237, 255], "um_green_5": [36, 162, 73, 255], "um_green_9": [31, 44, 36, 255], "um_red_1": [251, 232, 233, 255], "um_red_5": [218, 30, 40, 255], "um_red_9": [59, 31, 33, 255], "um_orange_1": [255, 235, 221, 255], "um_orange_5": [252, 123, 30, 255], "um_orange_9": [64, 45, 32, 255], "um_yellow_1": [255, 248, 225, 255], "um_yellow_5": [253, 209, 58, 255], "um_yellow_9": [64, 58, 36, 255]}, "colors": {"main_background": "background_1", "detail_background": "background_2", "wide_lining": [245, 245, 245, 255], "thick_lining": [180, 180, 180, 255], "lining": [192, 193, 194, 255], "viewport_overlay": [246, 246, 246, 255], "primary": "accent_1", "primary_hover": [48, 182, 231, 255], "primary_text": [255, 255, 255, 255], "text_selection": [156, 195, 255, 127], "border": [127, 127, 127, 255], "border_field": [180, 180, 180, 255], "secondary": [240, 240, 240, 255], "expandable_active": [240, 240, 240, 255], "expandable_hover": [232, 242, 252, 255], "icon": [8, 7, 63, 255], "primary_button": "accent_1", "primary_button_hover": [16, 70, 156, 255], "primary_button_text": [255, 255, 255, 255], "secondary_button": "background_1", "secondary_button_shadow": [216, 216, 216, 255], "secondary_button_hover": [232, 240, 253, 255], "secondary_button_text": "accent_1", "main_window_header_background": [126, 196, 193, 255], "main_window_header_background_gradient": [25, 23, 91, 255], "main_window_header_button_text_active": [8, 7, 63, 255], "main_window_header_button_text_inactive": [255, 255, 255, 255], "main_window_header_button_text_hovered": [255, 255, 255, 255], "main_window_header_button_background_active": [255, 255, 255, 255], "main_window_header_button_background_inactive": [255, 255, 255, 0], "main_window_header_button_background_hovered": [117, 114, 159, 255], "account_widget_outline_active": [70, 66, 126, 255], "account_sync_state_icon": [25, 25, 25, 255], "machine_selector_printer_icon": [8, 7, 63, 255], "action_panel_secondary": "accent_1", "first_run_shadow": [50, 50, 50, 255], "toolbar_background": [255, 255, 255, 255], "notification_icon": [255, 0, 0, 255], "printer_type_label_background": [228, 228, 242, 255], "window_disabled_background": [0, 0, 0, 255], "text": [25, 25, 25, 255], "text_disabled": [180, 180, 180, 255], "text_detail": [174, 174, 174, 128], "text_link": "accent_1", "text_inactive": [174, 174, 174, 255], "text_medium": [128, 128, 128, 255], "text_scene": [102, 102, 102, 255], "text_scene_hover": [123, 123, 113, 255], "error": [218, 30, 40, 255], "warning": [253, 209, 58, 255], "success": [36, 162, 73, 255], "disabled": [229, 229, 229, 255], "toolbar_button_hover": [232, 242, 252, 255], "toolbar_button_active": [232, 242, 252, 255], "toolbar_button_active_hover": [232, 242, 252, 255], "button_text": [255, 255, 255, 255], "small_button_text": [102, 102, 102, 255], "small_button_text_hover": [8, 7, 63, 255], "button_tooltip": [31, 36, 39, 255], "extruder_disabled": [255, 255, 255, 102], "action_button": [255, 255, 255, 255], "action_button_hovered": [232, 242, 252, 255], "action_button_disabled": [245, 245, 245, 255], "action_button_disabled_text": [196, 196, 196, 255], "action_button_shadow": [223, 223, 223, 255], "scrollbar_background": [255, 255, 255, 255], "scrollbar_handle": [10, 8, 80, 255], "scrollbar_handle_hover": [50, 130, 255, 255], "scrollbar_handle_down": [50, 130, 255, 255], "setting_category": "background_1", "setting_category_disabled": [255, 255, 255, 255], "setting_category_hover": "background_2", "setting_category_text": "text_default", "setting_category_disabled_text": [24, 41, 77, 101], "setting_category_active_text": "text_default", "setting_control": "background_2", "setting_control_highlight": "background_3", "setting_control_border": [199, 199, 199, 255], "setting_control_border_highlight": [50, 130, 255, 255], "setting_control_text": [35, 35, 35, 255], "setting_control_button": [102, 102, 102, 255], "setting_control_button_hover": [8, 7, 63, 255], "setting_control_disabled": "background_2", "setting_control_disabled_text": [127, 127, 127, 255], "setting_control_disabled_border": [127, 127, 127, 255], "setting_unit": [127, 127, 127, 255], "setting_validation_error_background": "um_red_1", "setting_validation_error": "um_red_5", "setting_validation_warning_background": "um_yellow_1", "setting_validation_warning": "um_yellow_5", "setting_validation_ok": "background_2", "material_compatibility_warning": [243, 166, 59, 255], "core_compatibility_warning": [243, 166, 59, 255], "progressbar_background": [245, 245, 245, 255], "progressbar_control": [50, 130, 255, 255], "slider_groove": [223, 223, 223, 255], "slider_groove_fill": [8, 7, 63, 255], "slider_handle": [8, 7, 63, 255], "slider_handle_active": [50, 130, 255, 255], "slider_text_background": [255, 255, 255, 255], "quality_slider_unavailable": [179, 179, 179, 255], "quality_slider_available": [0, 0, 0, 255], "checkbox": "background_1", "checkbox_hover": "background_1", "checkbox_disabled": "background_2", "checkbox_border": [180, 180, 180, 255], "checkbox_border_hover": "border_main", "checkbox_border_disabled": "text_disabled", "checkbox_mark": "text_default", "checkbox_mark_disabled": "text_disabled", "checkbox_square": [180, 180, 180, 255], "checkbox_text": "text_default", "checkbox_text_disabled": "text_disabled", "switch": "background_1", "switch_state_checked": "accent_1", "switch_state_unchecked": "text_disabled", "radio": "background_1", "radio_disabled": "background_2", "radio_selected": "accent_1", "radio_selected_disabled": "text_disabled", "radio_border": [180, 180, 180, 255], "radio_border_hover": "border_main", "radio_border_disabled": "text_disabled", "radio_dot": "background_1", "radio_dot_disabled": "background_2", "radio_text": "text_default", "radio_text_disabled": "text_disabled", "text_field": "background_1", "text_field_border": [180, 180, 180, 255], "text_field_border_hovered": "border_main", "text_field_border_active": "border_accent_2", "text_field_border_disabled": "background_2", "text_field_text": "text_default", "text_field_text_disabled": "text_disabled", "category_background": "background_2", "tooltip": [25, 25, 25, 255], "tooltip_text": [255, 255, 255, 255], "message_background": [255, 255, 255, 255], "message_border": [192, 193, 194, 255], "message_close": [102, 102, 102, 255], "message_close_hover": [8, 7, 63, 255], "message_progressbar_background": [245, 245, 245, 255], "message_progressbar_control": [50, 130, 255, 255], "message_success_icon": [255, 255, 255, 255], "message_warning_icon": [0, 0, 0, 255], "message_error_icon": [255, 255, 255, 255], "tool_panel_background": [255, 255, 255, 255], "status_offline": [0, 0, 0, 255], "status_ready": [0, 205, 0, 255], "status_busy": [50, 130, 255, 255], "status_paused": [255, 140, 0, 255], "status_stopped": [236, 82, 80, 255], "disabled_axis": [127, 127, 127, 255], "x_axis": [218, 30, 40, 255], "y_axis": [25, 110, 240, 255], "z_axis": [36, 162, 73, 255], "all_axis": [255, 255, 255, 255], "viewport_background": [250, 250, 250, 255], "volume_outline": [50, 130, 255, 255], "buildplate": [244, 244, 244, 255], "buildplate_grid": [180, 180, 180, 255], "buildplate_grid_minor": [228, 228, 228, 255], "convex_hull": [35, 35, 35, 127], "disallowed_area": [0, 0, 0, 40], "error_area": [255, 0, 0, 127], "model_overhang": [255, 0, 0, 255], "model_unslicable": [122, 122, 122, 255], "model_unslicable_alt": [172, 172, 127, 255], "model_selection_outline": [50, 130, 255, 255], "model_non_printing": [122, 122, 122, 255], "xray": [26, 26, 62, 255], "layerview_ghost": [31, 31, 31, 95], "layerview_none": [255, 255, 255, 255], "layerview_inset_0": [230, 0, 0, 255], "layerview_inset_x": [0, 230, 0, 255], "layerview_skin": [230, 230, 0, 255], "layerview_support": [0, 230, 230, 127], "layerview_skirt": [0, 230, 230, 255], "layerview_infill": [230, 115, 0, 255], "layerview_support_infill": [0, 230, 230, 127], "layerview_move_combing": [0, 0, 255, 255], "layerview_move_retraction": [128, 127, 255, 255], "layerview_move_while_retracting": [127, 255, 255, 255], "layerview_move_while_unretracting": [255, 127, 255, 255], "layerview_support_interface": [63, 127, 255, 127], "layerview_prime_tower": [0, 255, 255, 255], "layerview_nozzle": [224, 192, 16, 64], "layerview_starts": [255, 255, 255, 255], "monitor_printer_family_tag": [228, 228, 242, 255], "monitor_text_disabled": [238, 238, 238, 255], "monitor_icon_primary": [10, 8, 80, 255], "monitor_icon_accent": [255, 255, 255, 255], "monitor_icon_disabled": [238, 238, 238, 255], "monitor_card_border": [192, 193, 194, 255], "monitor_card_background": [255, 255, 255, 255], "monitor_card_hover": [232, 242, 252, 255], "monitor_stage_background": [246, 246, 246, 255], "monitor_stage_background_fade": [246, 246, 246, 102], "monitor_tooltip": [25, 25, 25, 255], "monitor_tooltip_text": [255, 255, 255, 255], "monitor_context_menu": [255, 255, 255, 255], "monitor_context_menu_hover": [245, 245, 245, 255], "monitor_skeleton_loading": [238, 238, 238, 255], "monitor_placeholder_image": [230, 230, 230, 255], "monitor_image_overlay": [0, 0, 0, 255], "monitor_shadow": [200, 200, 200, 255], "monitor_carousel_dot": [216, 216, 216, 255], "monitor_carousel_dot_current": [119, 119, 119, 255], "cloud_unavailable": [153, 153, 153, 255], "cloud_inactive": [253, 209, 58, 255], "connection_badge_background": [255, 255, 255, 255], "warning_badge_background": [0, 0, 0, 255], "error_badge_background": [255, 255, 255, 255], "border_field_light": [180, 180, 180, 255], "border_main_light": [212, 212, 212, 255], "paint_normal_area": "background_3", "paint_preferred_area": "um_green_5", "paint_avoid_area": "um_red_5"}, "sizes": {"window_minimum_size": [80, 48], "popup_dialog": [40, 36], "small_popup_dialog": [36, 12], "main_window_header": [0.0, 4.0], "stage_menu": [0.0, 4.0], "account_button": [12, 2.5], "print_setup_widget": [38.0, 30.0], "print_setup_extruder_box": [0.0, 6.0], "slider_widget_groove": [0.16, 0.16], "slider_widget_handle": [1.3, 1.3], "slider_widget_tickmarks": [0.5, 0.5], "print_setup_big_item": [28, 2.5], "print_setup_icon": [1.2, 1.2], "drag_icon": [1.416, 0.25], "application_switcher_item": [8, 9], "application_switcher_icon": [3.75, 3.75], "expandable_component_content_header": [0.0, 3.0], "configuration_selector": [35.0, 4.0], "action_panel_widget": [26.0, 0.0], "action_panel_information_widget": [20.0, 0.0], "machine_selector_widget": [20.0, 4.0], "machine_selector_widget_content": [25.0, 32.0], "machine_selector_icon": [2.5, 2.5], "views_selector": [16.0, 4.0], "printer_type_label": [3.5, 1.5], "default_radius": [0.25, 0.25], "wide_lining": [0.5, 0.5], "thick_lining": [0.2, 0.2], "default_lining": [0.08, 0.08], "default_arrow": [0.8, 0.8], "logo": [16, 2], "wide_margin": [2.0, 2.0], "thick_margin": [1.71, 1.43], "default_margin": [1.0, 1.0], "thin_margin": [0.71, 0.71], "narrow_margin": [0.5, 0.5], "extruder_icon": [2.5, 2.5], "section": [0.0, 2], "section_header": [0.0, 2.5], "section_control": [0, 1], "section_icon": [1.5, 1.5], "section_icon_column": [2.5, 2.5], "setting": [25.0, 1.8], "setting_control": [9.0, 2.0], "setting_control_radius": [0.15, 0.15], "setting_control_depth_margin": [1.4, 0.0], "setting_unit_margin": [0.5, 0.5], "standard_list_lineheight": [1.5, 1.5], "standard_arrow": [1.0, 1.0], "card": [25.0, 10], "card_icon": [6.0, 6.0], "card_tiny_icon": [1.5, 1.5], "button": [4, 4], "button_icon": [2.5, 2.5], "action_button": [15.0, 2.5], "action_button_icon": [1.5, 1.5], "action_button_icon_small": [1.0, 1.0], "action_button_radius": [0.15, 0.15], "radio_button": [1.3, 1.3], "small_button": [2, 2], "small_button_icon": [1.5, 1.5], "medium_button": [2.5, 2.5], "medium_button_icon": [2, 2], "large_button": [3.0, 3.0], "large_button_icon": [2.8, 2.8], "context_menu": [20, 2], "icon_indicator": [1, 1], "printer_status_icon": [1.0, 1.0], "button_tooltip": [1.0, 1.3], "button_tooltip_arrow": [0.25, 0.25], "progressbar": [26.0, 0.75], "progressbar_radius": [0.15, 0.15], "scrollbar": [0.75, 0.5], "slider_groove": [0.5, 0.5], "slider_groove_radius": [0.15, 0.15], "slider_handle": [1.5, 1.5], "slider_layerview_size": [1.0, 34.0], "layerview_menu_size": [16.0, 4.0], "layerview_legend_size": [1.0, 1.0], "layerview_row": [11.0, 1.5], "layerview_row_spacing": [0.0, 0.5], "checkbox": [1.33, 1.33], "checkbox_mark": [1, 1], "checkbox_radius": [0.25, 0.25], "spinbox": [6.0, 3.0], "combobox": [14, 2], "combobox_wide": [22, 2], "tooltip": [20.0, 10.0], "tooltip_margins": [1.0, 1.0], "tooltip_arrow_margins": [2.0, 2.0], "save_button_save_to_button": [0.3, 2.7], "save_button_specs_icons": [1.4, 1.4], "first_run_shadow_radius": [1.2, 1.2], "monitor_preheat_temperature_control": [4.5, 2.0], "welcome_wizard_window": [46, 50], "modal_window_minimum": [60.0, 50.0], "wizard_progress": [10.0, 0.0], "message": [30.0, 5.0], "message_close": [2, 2], "message_radius": [0.25, 0.25], "message_action_button": [0, 2.5], "message_image": [15.0, 10.0], "message_type_icon": [2, 2], "menu": [18, 2], "jobspecs_line": [2.0, 2.0], "objects_menu_size": [15, 15], "notification_icon": [1.5, 1.5], "avatar_image": [6.8, 6.8], "monitor_shadow_radius": [0.4, 0.4], "monitor_empty_state_offset": [5.6, 5.6], "monitor_empty_state_size": [35.0, 25.0], "monitor_column": [18.0, 1.0], "monitor_progress_bar": [16.5, 1.0], "table_row": [2.0, 2.0], "welcome_wizard_content_image_big": [18, 15], "welcome_wizard_cloud_content_image": [4, 4], "banner_icon_size": [2.0, 2.0], "marketplace_large_icon": [4.0, 4.0], "preferences_page_list_item": [8.0, 2.0], "recommended_button_icon": [1.7, 1.7], "recommended_section_setting_item": [14.0, 2.0], "reset_profile_icon": [1, 1]}} \ No newline at end of file diff --git a/resources/themes/daily_test_colors.json b/resources/themes/daily_test_colors.json deleted file mode 100644 index 1cfa2baa74..0000000000 --- a/resources/themes/daily_test_colors.json +++ /dev/null @@ -1,16 +0,0 @@ -[ - [ 62, 33, 55, 255], - [126, 196, 193, 255], - [126, 196, 193, 255], - [215, 155, 125, 255], - [228, 148, 58, 255], - [192, 199, 65, 255], - [157, 48, 59, 255], - [140, 143, 174, 255], - [ 23, 67, 75, 255], - [ 23, 67, 75, 255], - [154, 99, 72, 255], - [112, 55, 127, 255], - [100, 125, 52, 255], - [210, 100, 113, 255] -]