mirror of
https://git.mirrors.martin98.com/https://github.com/Ultimaker/Cura
synced 2025-04-16 18:59:39 +08:00
355 lines
14 KiB
Python
355 lines
14 KiB
Python
# Copyright (c) 2019 Ultimaker B.V.
|
|
# Cura is released under the terms of the LGPLv3 or higher.
|
|
|
|
from PyQt6.QtCore import pyqtSignal, pyqtProperty, QObject, QVariant, pyqtSlot, QUrl
|
|
from typing import List, Dict, Optional, TYPE_CHECKING
|
|
from UM.Math.Vector import Vector
|
|
from cura.PrinterOutput.Peripheral import Peripheral
|
|
from cura.PrinterOutput.Models.PrinterConfigurationModel import PrinterConfigurationModel
|
|
from cura.PrinterOutput.Models.ExtruderOutputModel import ExtruderOutputModel
|
|
from UM.Logger import Logger
|
|
|
|
if TYPE_CHECKING:
|
|
from cura.PrinterOutput.Models.PrintJobOutputModel import PrintJobOutputModel
|
|
from cura.PrinterOutput.PrinterOutputController import PrinterOutputController
|
|
|
|
|
|
class PrinterOutputModel(QObject):
|
|
bedTemperatureChanged = pyqtSignal()
|
|
targetBedTemperatureChanged = pyqtSignal()
|
|
isPreheatingChanged = pyqtSignal()
|
|
stateChanged = pyqtSignal()
|
|
activePrintJobChanged = pyqtSignal()
|
|
nameChanged = pyqtSignal()
|
|
headPositionChanged = pyqtSignal()
|
|
keyChanged = pyqtSignal()
|
|
typeChanged = pyqtSignal()
|
|
buildplateChanged = pyqtSignal()
|
|
cameraUrlChanged = pyqtSignal()
|
|
configurationChanged = pyqtSignal()
|
|
canUpdateFirmwareChanged = pyqtSignal()
|
|
|
|
def __init__(self, output_controller: "PrinterOutputController", number_of_extruders: int = 1, parent=None, firmware_version = "") -> None:
|
|
super().__init__(parent)
|
|
self._bed_temperature = -1 # type: float # Use -1 for no heated bed.
|
|
self._target_bed_temperature = 0 # type: float
|
|
self._name = ""
|
|
self._key = "" # Unique identifier
|
|
self._unique_name = "" # Unique name (used in Connect)
|
|
self._controller = output_controller
|
|
self._controller.canUpdateFirmwareChanged.connect(self._onControllerCanUpdateFirmwareChanged)
|
|
self._extruders = [ExtruderOutputModel(printer = self, position = i) for i in range(number_of_extruders)]
|
|
self._active_printer_configuration = PrinterConfigurationModel() # Indicates the current configuration setup in this printer
|
|
self._head_position = Vector(0, 0, 0)
|
|
self._active_print_job = None # type: Optional[PrintJobOutputModel]
|
|
self._firmware_version = firmware_version
|
|
self._printer_state = "unknown"
|
|
self._is_preheating = False
|
|
self._printer_type = ""
|
|
self._buildplate = ""
|
|
self._peripherals = [] # type: List[Peripheral]
|
|
|
|
self._active_printer_configuration.extruderConfigurations = [extruder.extruderConfiguration for extruder in
|
|
self._extruders]
|
|
self._active_printer_configuration.configurationChanged.connect(self.configurationChanged)
|
|
self._available_printer_configurations = [] # type: List[PrinterConfigurationModel]
|
|
|
|
self._camera_url = QUrl() # type: QUrl
|
|
|
|
@pyqtProperty(str, constant = True)
|
|
def firmwareVersion(self) -> str:
|
|
return self._firmware_version
|
|
|
|
def setCameraUrl(self, camera_url: "QUrl") -> None:
|
|
if self._camera_url != camera_url:
|
|
self._camera_url = camera_url
|
|
self.cameraUrlChanged.emit()
|
|
|
|
@pyqtProperty(QUrl, fset = setCameraUrl, notify = cameraUrlChanged)
|
|
def cameraUrl(self) -> "QUrl":
|
|
return self._camera_url
|
|
|
|
def updateIsPreheating(self, pre_heating: bool) -> None:
|
|
if self._is_preheating != pre_heating:
|
|
self._is_preheating = pre_heating
|
|
self.isPreheatingChanged.emit()
|
|
|
|
@pyqtProperty(bool, notify=isPreheatingChanged)
|
|
def isPreheating(self) -> bool:
|
|
return self._is_preheating
|
|
|
|
@pyqtProperty(str, notify = typeChanged)
|
|
def type(self) -> str:
|
|
return self._printer_type
|
|
|
|
def updateType(self, printer_type: str) -> None:
|
|
if self._printer_type != printer_type:
|
|
self._printer_type = printer_type
|
|
self._active_printer_configuration.printerType = self._printer_type
|
|
self.typeChanged.emit()
|
|
self.configurationChanged.emit()
|
|
|
|
@pyqtProperty(str, notify = buildplateChanged)
|
|
def buildplate(self) -> str:
|
|
return self._buildplate
|
|
|
|
def updateBuildplate(self, buildplate: str) -> None:
|
|
if self._buildplate != buildplate:
|
|
self._buildplate = buildplate
|
|
self._active_printer_configuration.buildplateConfiguration = self._buildplate
|
|
self.buildplateChanged.emit()
|
|
self.configurationChanged.emit()
|
|
|
|
@pyqtProperty(str, notify=keyChanged)
|
|
def key(self) -> str:
|
|
return self._key
|
|
|
|
def updateKey(self, key: str) -> None:
|
|
if self._key != key:
|
|
self._key = key
|
|
self.keyChanged.emit()
|
|
|
|
@pyqtSlot()
|
|
def homeHead(self) -> None:
|
|
self._controller.homeHead(self)
|
|
|
|
@pyqtSlot()
|
|
def homeBed(self) -> None:
|
|
self._controller.homeBed(self)
|
|
|
|
@pyqtSlot(str)
|
|
def sendRawCommand(self, command: str) -> None:
|
|
self._controller.sendRawCommand(self, command)
|
|
|
|
@pyqtProperty("QVariantList", constant = True)
|
|
def extruders(self) -> List["ExtruderOutputModel"]:
|
|
return self._extruders
|
|
|
|
@pyqtProperty(QVariant, notify = headPositionChanged)
|
|
def headPosition(self) -> Dict[str, float]:
|
|
return {"x": self._head_position.x, "y": self._head_position.y, "z": self.head_position.z}
|
|
|
|
def updateHeadPosition(self, x: float, y: float, z: float) -> None:
|
|
if self._head_position.x != x or self._head_position.y != y or self._head_position.z != z:
|
|
self._head_position = Vector(x, y, z)
|
|
self.headPositionChanged.emit()
|
|
|
|
@pyqtProperty(float, float, float)
|
|
@pyqtProperty(float, float, float, float)
|
|
def setHeadPosition(self, x: float, y: float, z: float, speed: float = 3000) -> None:
|
|
self.updateHeadPosition(x, y, z)
|
|
self._controller.setHeadPosition(self, x, y, z, speed)
|
|
|
|
@pyqtProperty(float)
|
|
@pyqtProperty(float, float)
|
|
def setHeadX(self, x: float, speed: float = 3000) -> None:
|
|
self.updateHeadPosition(x, self._head_position.y, self._head_position.z)
|
|
self._controller.setHeadPosition(self, x, self._head_position.y, self._head_position.z, speed)
|
|
|
|
@pyqtProperty(float)
|
|
@pyqtProperty(float, float)
|
|
def setHeadY(self, y: float, speed: float = 3000) -> None:
|
|
self.updateHeadPosition(self._head_position.x, y, self._head_position.z)
|
|
self._controller.setHeadPosition(self, self._head_position.x, y, self._head_position.z, speed)
|
|
|
|
@pyqtProperty(float)
|
|
@pyqtProperty(float, float)
|
|
def setHeadZ(self, z: float, speed:float = 3000) -> None:
|
|
self.updateHeadPosition(self._head_position.x, self._head_position.y, z)
|
|
self._controller.setHeadPosition(self, self._head_position.x, self._head_position.y, z, speed)
|
|
|
|
@pyqtSlot(float, float, float)
|
|
@pyqtSlot(float, float, float, float)
|
|
def moveHead(self, x: float = 0, y: float = 0, z: float = 0, speed: float = 3000) -> None:
|
|
self._controller.moveHead(self, x, y, z, speed)
|
|
|
|
@pyqtSlot(float, float)
|
|
def preheatBed(self, temperature: float, duration: float) -> None:
|
|
"""Pre-heats the heated bed of the printer.
|
|
|
|
:param temperature: The temperature to heat the bed to, in degrees
|
|
Celsius.
|
|
:param duration: How long the bed should stay warm, in seconds.
|
|
"""
|
|
|
|
self._controller.preheatBed(self, temperature, duration)
|
|
|
|
@pyqtSlot()
|
|
def cancelPreheatBed(self) -> None:
|
|
self._controller.cancelPreheatBed(self)
|
|
|
|
def getController(self) -> "PrinterOutputController":
|
|
return self._controller
|
|
|
|
@pyqtProperty(str, notify = nameChanged)
|
|
def name(self) -> str:
|
|
return self._name
|
|
|
|
def setName(self, name: str) -> None:
|
|
self.updateName(name)
|
|
|
|
def updateName(self, name: str) -> None:
|
|
if self._name != name:
|
|
self._name = name
|
|
self.nameChanged.emit()
|
|
|
|
@pyqtProperty(str, notify = nameChanged)
|
|
def uniqueName(self) -> str:
|
|
return self._unique_name
|
|
|
|
def updateUniqueName(self, unique_name: str) -> None:
|
|
if self._unique_name != unique_name:
|
|
self._unique_name = unique_name
|
|
self.nameChanged.emit()
|
|
|
|
def updateBedTemperature(self, temperature: float) -> None:
|
|
"""Update the bed temperature. This only changes it locally."""
|
|
|
|
if self._bed_temperature != temperature:
|
|
self._bed_temperature = temperature
|
|
self.bedTemperatureChanged.emit()
|
|
|
|
def updateTargetBedTemperature(self, temperature: float) -> None:
|
|
if self._target_bed_temperature != temperature:
|
|
self._target_bed_temperature = temperature
|
|
self.targetBedTemperatureChanged.emit()
|
|
|
|
@pyqtSlot(float)
|
|
def setTargetBedTemperature(self, temperature: float) -> None:
|
|
"""Set the target bed temperature. This ensures that it's actually sent to the remote."""
|
|
|
|
self._controller.setTargetBedTemperature(self, temperature)
|
|
self.updateTargetBedTemperature(temperature)
|
|
|
|
def updateActivePrintJob(self, print_job: Optional["PrintJobOutputModel"]) -> None:
|
|
if self._active_print_job != print_job:
|
|
old_print_job = self._active_print_job
|
|
|
|
if print_job is not None:
|
|
print_job.updateAssignedPrinter(self)
|
|
self._active_print_job = print_job
|
|
|
|
if old_print_job is not None:
|
|
old_print_job.updateAssignedPrinter(None)
|
|
self.activePrintJobChanged.emit()
|
|
|
|
def updateState(self, printer_state: str) -> None:
|
|
if self._printer_state != printer_state:
|
|
self._printer_state = printer_state
|
|
self.stateChanged.emit()
|
|
|
|
@pyqtProperty(QObject, notify = activePrintJobChanged)
|
|
def activePrintJob(self) -> Optional["PrintJobOutputModel"]:
|
|
return self._active_print_job
|
|
|
|
@pyqtProperty(str, notify = stateChanged)
|
|
def state(self) -> str:
|
|
return self._printer_state
|
|
|
|
@pyqtProperty(float, notify = bedTemperatureChanged)
|
|
def bedTemperature(self) -> float:
|
|
return self._bed_temperature
|
|
|
|
@pyqtProperty(float, notify = targetBedTemperatureChanged)
|
|
def targetBedTemperature(self) -> float:
|
|
return self._target_bed_temperature
|
|
|
|
# Does the printer support pre-heating the bed at all
|
|
@pyqtProperty(bool, constant = True)
|
|
def canPreHeatBed(self) -> bool:
|
|
if self._controller:
|
|
return self._controller.can_pre_heat_bed
|
|
return False
|
|
|
|
# Does the printer support pre-heating the bed at all
|
|
@pyqtProperty(bool, constant = True)
|
|
def canPreHeatHotends(self) -> bool:
|
|
if self._controller:
|
|
return self._controller.can_pre_heat_hotends
|
|
return False
|
|
|
|
# Does the printer support sending raw G-code at all
|
|
@pyqtProperty(bool, constant = True)
|
|
def canSendRawGcode(self) -> bool:
|
|
if self._controller:
|
|
return self._controller.can_send_raw_gcode
|
|
return False
|
|
|
|
# Does the printer support pause at all
|
|
@pyqtProperty(bool, constant = True)
|
|
def canPause(self) -> bool:
|
|
if self._controller:
|
|
return self._controller.can_pause
|
|
return False
|
|
|
|
# Does the printer support abort at all
|
|
@pyqtProperty(bool, constant = True)
|
|
def canAbort(self) -> bool:
|
|
if self._controller:
|
|
return self._controller.can_abort
|
|
return False
|
|
|
|
# Does the printer support manual control at all
|
|
@pyqtProperty(bool, constant = True)
|
|
def canControlManually(self) -> bool:
|
|
if self._controller:
|
|
return self._controller.can_control_manually
|
|
return False
|
|
|
|
# Does the printer support upgrading firmware
|
|
@pyqtProperty(bool, notify = canUpdateFirmwareChanged)
|
|
def canUpdateFirmware(self) -> bool:
|
|
if self._controller:
|
|
return self._controller.can_update_firmware
|
|
return False
|
|
|
|
# Stub to connect UM.Signal to pyqtSignal
|
|
def _onControllerCanUpdateFirmwareChanged(self) -> None:
|
|
self.canUpdateFirmwareChanged.emit()
|
|
|
|
# Returns the active configuration (material, variant and buildplate) of the current printer
|
|
@pyqtProperty(QObject, notify = configurationChanged)
|
|
def printerConfiguration(self) -> Optional[PrinterConfigurationModel]:
|
|
if self._active_printer_configuration.isValid():
|
|
return self._active_printer_configuration
|
|
return None
|
|
|
|
peripheralsChanged = pyqtSignal()
|
|
|
|
@pyqtProperty(str, notify = peripheralsChanged)
|
|
def peripherals(self) -> str:
|
|
return ", ".join([peripheral.name for peripheral in self._peripherals])
|
|
|
|
def addPeripheral(self, peripheral: Peripheral) -> None:
|
|
self._peripherals.append(peripheral)
|
|
self.peripheralsChanged.emit()
|
|
|
|
def removePeripheral(self, peripheral: Peripheral) -> None:
|
|
self._peripherals.remove(peripheral)
|
|
self.peripheralsChanged.emit()
|
|
|
|
availableConfigurationsChanged = pyqtSignal()
|
|
|
|
# The availableConfigurations are configuration options that a printer can switch to, but doesn't currently have
|
|
# active (eg; Automatic tool changes, material loaders, etc).
|
|
@pyqtProperty("QVariantList", notify = availableConfigurationsChanged)
|
|
def availableConfigurations(self) -> List[PrinterConfigurationModel]:
|
|
return self._available_printer_configurations
|
|
|
|
def addAvailableConfiguration(self, new_configuration: PrinterConfigurationModel) -> None:
|
|
if new_configuration not in self._available_printer_configurations:
|
|
self._available_printer_configurations.append(new_configuration)
|
|
self.availableConfigurationsChanged.emit()
|
|
|
|
def removeAvailableConfiguration(self, config_to_remove: PrinterConfigurationModel) -> None:
|
|
try:
|
|
self._available_printer_configurations.remove(config_to_remove)
|
|
except ValueError:
|
|
Logger.log("w", "Unable to remove configuration that isn't in the list of available configurations")
|
|
else:
|
|
self.availableConfigurationsChanged.emit()
|
|
|
|
def setAvailableConfigurations(self, new_configurations: List[PrinterConfigurationModel]) -> None:
|
|
self._available_printer_configurations = new_configurations
|
|
self.availableConfigurationsChanged.emit()
|