123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233 |
- # Copyright (c) 2015 Ultimaker B.V.
- # Cura is released under the terms of the AGPLv3 or higher.
- from PyQt5.QtCore import QObject, QDateTime, QTimer, pyqtSignal, pyqtSlot, pyqtProperty
- from UM.Application import Application
- from UM.Settings.MachineSettings import MachineSettings
- from UM.Resources import Resources
- from UM.Scene.SceneNode import SceneNode
- from UM.Qt.Duration import Duration
- import math
- ## A class for processing and calculating minimum, currrent and maximum print time.
- #
- # This class contains all the logic relating to calculation and slicing for the
- # time/quality slider concept. It is a rather tricky combination of event handling
- # and state management. The logic behind this is as follows:
- #
- # - A scene change or settting change event happens.
- # We track what the source was of the change, either a scene change, a setting change, an active machine change or something else.
- # - This triggers a new slice with the current settings - this is the "current settings pass".
- # - When the slice is done, we update the current print time and material amount.
- # - If the source of the slice was not a Setting change, we start the second slice pass, the "low quality settings pass". Otherwise we stop here.
- # - When that is done, we update the minimum print time and start the final slcice pass, the "high quality settings pass".
- # - When the high quality pass is done, we update the maximum print time.
- #
- class PrintInformation(QObject):
- class SlicePass:
- CurrentSettings = 1
- LowQualitySettings = 2
- HighQualitySettings = 3
- class SliceReason:
- SceneChanged = 1
- SettingChanged = 2
- ActiveMachineChanged = 3
- Other = 4
- def __init__(self, parent = None):
- super().__init__(parent)
- self._enabled = False
- self._minimum_print_time = Duration(None, self)
- self._current_print_time = Duration(None, self)
- self._maximum_print_time = Duration(None, self)
- self._material_amount = -1
- self._time_quality_value = 50
- self._time_quality_changed_timer = QTimer()
- self._time_quality_changed_timer.setInterval(500)
- self._time_quality_changed_timer.setSingleShot(True)
- self._time_quality_changed_timer.timeout.connect(self._updateTimeQualitySettings)
- self._interpolation_settings = {
- "layer_height": { "minimum": "low", "maximum": "high", "curve": "linear", "precision": 2 },
- "fill_sparse_density": { "minimum": "low", "maximum": "high", "curve": "linear", "precision": 0 }
- }
- self._low_quality_settings = None
- self._current_settings = None
- self._high_quality_settings = None
- self._slice_pass = None
- self._slice_reason = None
- Application.getInstance().activeMachineChanged.connect(self._onActiveMachineChanged)
- self._onActiveMachineChanged()
- Application.getInstance().getController().getScene().sceneChanged.connect(self._onSceneChanged)
- self._backend = Application.getInstance().getBackend()
- if self._backend:
- self._backend.printDurationMessage.connect(self._onPrintDurationMessage)
- self._backend.slicingStarted.connect(self._onSlicingStarted)
- self._backend.slicingCancelled.connect(self._onSlicingCancelled)
- minimumPrintTimeChanged = pyqtSignal()
-
- @pyqtProperty(Duration, notify = minimumPrintTimeChanged)
- def minimumPrintTime(self):
- return self._minimum_print_time
- currentPrintTimeChanged = pyqtSignal()
-
- @pyqtProperty(Duration, notify = currentPrintTimeChanged)
- def currentPrintTime(self):
- return self._current_print_time
- maximumPrintTimeChanged = pyqtSignal()
-
- @pyqtProperty(Duration, notify = maximumPrintTimeChanged)
- def maximumPrintTime(self):
- return self._maximum_print_time
- materialAmountChanged = pyqtSignal()
-
- @pyqtProperty(float, notify = materialAmountChanged)
- def materialAmount(self):
- return self._material_amount
- timeQualityValueChanged = pyqtSignal()
-
- @pyqtProperty(int, notify = timeQualityValueChanged)
- def timeQualityValue(self):
- return self._time_quality_value
- def setEnabled(self, enabled):
- if enabled != self._enabled:
- self._enabled = enabled
- if self._enabled:
- self._updateTimeQualitySettings()
- self._onSlicingStarted()
- self.enabledChanged.emit()
- enabledChanged = pyqtSignal()
- @pyqtProperty(bool, fset = setEnabled, notify = enabledChanged)
- def enabled(self):
- return self._enabled
- @pyqtSlot(int)
- def setTimeQualityValue(self, value):
- if value != self._time_quality_value:
- self._time_quality_value = value
- self.timeQualityValueChanged.emit()
- self._time_quality_changed_timer.start()
- def _onSlicingStarted(self):
- if self._slice_pass is None:
- self._slice_pass = self.SlicePass.CurrentSettings
- if self._slice_reason is None:
- self._slice_reason = self.SliceReason.Other
- if self._slice_pass == self.SlicePass.CurrentSettings and self._slice_reason != self.SliceReason.SettingChanged:
- self._minimum_print_time.setDuration(-1)
- self.minimumPrintTimeChanged.emit()
- self._maximum_print_time.setDuration(-1)
- self.maximumPrintTimeChanged.emit()
- def _onPrintDurationMessage(self, time, amount):
- if self._slice_pass == self.SlicePass.CurrentSettings:
- self._current_print_time.setDuration(time)
- self.currentPrintTimeChanged.emit()
- # Material amount is sent as an amount of mm^3, so calculate length from that
- r = self._current_settings.getSettingValueByKey("material_diameter") / 2
- self._material_amount = round((amount / (math.pi * r ** 2)) / 1000, 2)
- self.materialAmountChanged.emit()
- if not self._enabled:
- return
- if self._slice_reason != self.SliceReason.SettingChanged or not self._minimum_print_time.valid or not self._maximum_print_time.valid:
- self._slice_pass = self.SlicePass.LowQualitySettings
- self._backend.slice(settings = self._low_quality_settings, save_gcode = False, save_polygons = False, force_restart = False, report_progress = False)
- else:
- self._slice_pass = None
- self._slice_reason = None
- elif self._slice_pass == self.SlicePass.LowQualitySettings:
- self._minimum_print_time.setDuration(time)
- self.minimumPrintTimeChanged.emit()
- self._slice_pass = self.SlicePass.HighQualitySettings
- self._backend.slice(settings = self._high_quality_settings, save_gcode = False, save_polygons = False, force_restart = False, report_progress = False)
- elif self._slice_pass == self.SlicePass.HighQualitySettings:
- self._maximum_print_time.setDuration(time)
- self.maximumPrintTimeChanged.emit()
- self._slice_pass = None
- self._slice_reason = None
- def _onActiveMachineChanged(self):
- if self._current_settings:
- self._current_settings.settingChanged.disconnect(self._onSettingChanged)
- self._current_settings = Application.getInstance().getActiveMachine()
- if self._current_settings:
- self._current_settings.settingChanged.connect(self._onSettingChanged)
- self._low_quality_settings = None
- self._high_quality_settings = None
- self._updateTimeQualitySettings()
- self._slice_reason = self.SliceReason.ActiveMachineChanged
- def _updateTimeQualitySettings(self):
- if not self._current_settings or not self._enabled:
- return
- if not self._low_quality_settings:
- self._low_quality_settings = MachineSettings()
- self._low_quality_settings.loadSettingsFromFile(Resources.getPath(Resources.SettingsLocation, self._current_settings.getTypeID() + ".json"))
- self._low_quality_settings.loadValuesFromFile(Resources.getPath(Resources.SettingsLocation, "profiles", "low_quality.conf"))
- if not self._high_quality_settings:
- self._high_quality_settings = MachineSettings()
- self._high_quality_settings.loadSettingsFromFile(Resources.getPath(Resources.SettingsLocation, self._current_settings.getTypeID() + ".json"))
- self._high_quality_settings.loadValuesFromFile(Resources.getPath(Resources.SettingsLocation, "profiles", "high_quality.conf"))
- for key, options in self._interpolation_settings.items():
- minimum_value = None
- if options["minimum"] == "low":
- minimum_value = self._low_quality_settings.getSettingValueByKey(key)
- elif options["minimum"] == "high":
- minimum_value = self._high_quality_settings.getSettingValueByKey(key)
- else:
- continue
- maximum_value = None
- if options["maximum"] == "low":
- maximum_value = self._low_quality_settings.getSettingValueByKey(key)
- elif options["maximum"] == "high":
- maximum_value = self._high_quality_settings.getSettingValueByKey(key)
- else:
- continue
- setting_value = round(minimum_value + (maximum_value - minimum_value) * (self._time_quality_value / 100), options["precision"])
- self._current_settings.setSettingValueByKey(key, setting_value)
- def _onSceneChanged(self, source):
- self._slice_reason = self.SliceReason.SceneChanged
- def _onSettingChanged(self, source):
- self._slice_reason = self.SliceReason.SettingChanged
- def _onSlicingCancelled(self):
- self._slice_pass = None
|