PrintJobOutputModel.py 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. # Copyright (c) 2022 Ultimaker B.V.
  2. # Cura is released under the terms of the LGPLv3 or higher.
  3. from typing import Optional, TYPE_CHECKING, List
  4. from PyQt6.QtCore import pyqtSignal, pyqtProperty, QObject, pyqtSlot, QUrl
  5. from PyQt6.QtGui import QImage
  6. from cura.CuraApplication import CuraApplication
  7. if TYPE_CHECKING:
  8. from cura.PrinterOutput.PrinterOutputController import PrinterOutputController
  9. from cura.PrinterOutput.Models.PrinterOutputModel import PrinterOutputModel
  10. from cura.PrinterOutput.Models.PrinterConfigurationModel import PrinterConfigurationModel
  11. class PrintJobOutputModel(QObject):
  12. stateChanged = pyqtSignal()
  13. timeTotalChanged = pyqtSignal()
  14. timeElapsedChanged = pyqtSignal()
  15. nameChanged = pyqtSignal()
  16. keyChanged = pyqtSignal()
  17. assignedPrinterChanged = pyqtSignal()
  18. ownerChanged = pyqtSignal()
  19. configurationChanged = pyqtSignal()
  20. previewImageChanged = pyqtSignal()
  21. compatibleMachineFamiliesChanged = pyqtSignal()
  22. def __init__(self, output_controller: "PrinterOutputController", key: str = "", name: str = "", parent = None) -> None:
  23. super().__init__(parent)
  24. self._output_controller = output_controller
  25. self._state = ""
  26. self._time_total = 0
  27. self._time_elapsed = 0
  28. self._name = name # Human readable name
  29. self._key = key # Unique identifier
  30. self._assigned_printer = None # type: Optional[PrinterOutputModel]
  31. self._owner = "" # Who started/owns the print job?
  32. self._configuration = None # type: Optional[PrinterConfigurationModel]
  33. self._compatible_machine_families = [] # type: List[str]
  34. self._preview_image_id = 0
  35. self._preview_image = None # type: Optional[QImage]
  36. @pyqtProperty("QStringList", notify=compatibleMachineFamiliesChanged)
  37. def compatibleMachineFamilies(self) -> List[str]:
  38. # Hack; Some versions of cluster will return a family more than once...
  39. return list(set(self._compatible_machine_families))
  40. def setCompatibleMachineFamilies(self, compatible_machine_families: List[str]) -> None:
  41. if self._compatible_machine_families != compatible_machine_families:
  42. self._compatible_machine_families = compatible_machine_families
  43. self.compatibleMachineFamiliesChanged.emit()
  44. @pyqtProperty(QUrl, notify=previewImageChanged)
  45. def previewImageUrl(self):
  46. self._preview_image_id += 1
  47. # There is an image provider that is called "print_job_preview". In order to ensure that the image qml object, that
  48. # requires a QUrl to function, updates correctly we add an increasing number. This causes to see the QUrl
  49. # as new (instead of relying on cached version and thus forces an update.
  50. temp = "image://print_job_preview/" + str(self._preview_image_id) + "/" + self._key
  51. return QUrl(temp, QUrl.ParsingMode.TolerantMode)
  52. def getPreviewImage(self) -> Optional[QImage]:
  53. return self._preview_image
  54. def updatePreviewImage(self, preview_image: Optional[QImage]) -> None:
  55. if self._preview_image != preview_image:
  56. self._preview_image = preview_image
  57. self.previewImageChanged.emit()
  58. @pyqtProperty(QObject, notify=configurationChanged)
  59. def configuration(self) -> Optional["PrinterConfigurationModel"]:
  60. return self._configuration
  61. def updateConfiguration(self, configuration: Optional["PrinterConfigurationModel"]) -> None:
  62. if self._configuration != configuration:
  63. self._configuration = configuration
  64. self.configurationChanged.emit()
  65. @pyqtProperty(str, notify = ownerChanged)
  66. def owner(self) -> str:
  67. return self._owner
  68. def updateOwner(self, owner: str) -> None:
  69. if self._owner != owner:
  70. self._owner = owner
  71. self.ownerChanged.emit()
  72. @pyqtProperty(bool, notify = ownerChanged)
  73. def isMine(self) -> bool:
  74. """
  75. Returns whether this print job was sent by the currently logged in user.
  76. This checks the owner of the print job with the owner of the currently
  77. logged in account. Both of these are human-readable account names which
  78. may be duplicate. In practice the harm here is limited, but it's the
  79. best we can do with the information available to the API.
  80. """
  81. return self._owner == CuraApplication.getInstance().getCuraAPI().account.userName
  82. @pyqtProperty(QObject, notify=assignedPrinterChanged)
  83. def assignedPrinter(self):
  84. return self._assigned_printer
  85. def updateAssignedPrinter(self, assigned_printer: Optional["PrinterOutputModel"]) -> None:
  86. if self._assigned_printer != assigned_printer:
  87. old_printer = self._assigned_printer
  88. self._assigned_printer = assigned_printer
  89. if old_printer is not None:
  90. # If the previously assigned printer is set, this job is moved away from it.
  91. old_printer.updateActivePrintJob(None)
  92. self.assignedPrinterChanged.emit()
  93. @pyqtProperty(str, notify=keyChanged)
  94. def key(self):
  95. return self._key
  96. def updateKey(self, key: str):
  97. if self._key != key:
  98. self._key = key
  99. self.keyChanged.emit()
  100. @pyqtProperty(str, notify = nameChanged)
  101. def name(self):
  102. return self._name
  103. def updateName(self, name: str):
  104. if self._name != name:
  105. self._name = name
  106. self.nameChanged.emit()
  107. @pyqtProperty(int, notify = timeTotalChanged)
  108. def timeTotal(self) -> int:
  109. return int(self._time_total)
  110. @pyqtProperty(int, notify = timeElapsedChanged)
  111. def timeElapsed(self) -> int:
  112. return int(self._time_elapsed)
  113. @pyqtProperty(int, notify = timeElapsedChanged)
  114. def timeRemaining(self) -> int:
  115. # Never get a negative time remaining
  116. return int(max(self.timeTotal - self.timeElapsed, 0))
  117. @pyqtProperty(float, notify = timeElapsedChanged)
  118. def progress(self) -> float:
  119. result = float(self.timeElapsed) / max(self.timeTotal, 1.0) # Prevent a division by zero exception.
  120. return min(result, 1.0) # Never get a progress past 1.0
  121. @pyqtProperty(str, notify=stateChanged)
  122. def state(self) -> str:
  123. return self._state
  124. @pyqtProperty(bool, notify=stateChanged)
  125. def isActive(self) -> bool:
  126. inactive_states = [
  127. "pausing",
  128. "paused",
  129. "resuming",
  130. "wait_cleanup"
  131. ]
  132. if self.state in inactive_states and self.timeRemaining > 0:
  133. return False
  134. return True
  135. def updateTimeTotal(self, new_time_total: int) -> None:
  136. if self._time_total != new_time_total:
  137. self._time_total = new_time_total
  138. self.timeTotalChanged.emit()
  139. def updateTimeElapsed(self, new_time_elapsed: int) -> None:
  140. if self._time_elapsed != new_time_elapsed:
  141. self._time_elapsed = new_time_elapsed
  142. self.timeElapsedChanged.emit()
  143. def updateState(self, new_state: str) -> None:
  144. if self._state != new_state:
  145. self._state = new_state
  146. self.stateChanged.emit()
  147. @pyqtSlot(str)
  148. def setState(self, state):
  149. self._output_controller.setJobState(self, state)