Example #1
0
    def __init__(self, parent = None):
        super().__init__(parent)

        self._current_print_time = Duration(None, self)

        self._material_lengths = []
        self._material_weights = []
        self._material_costs = []

        self._pre_sliced = False

        self._backend = Application.getInstance().getBackend()
        if self._backend:
            self._backend.printDurationMessage.connect(self._onPrintDurationMessage)

        self._job_name = ""
        self._abbr_machine = ""

        Application.getInstance().globalContainerStackChanged.connect(self._setAbbreviatedMachineName)
        Application.getInstance().fileLoaded.connect(self.setJobName)

        Preferences.getInstance().preferenceChanged.connect(self._onPreferencesChanged)

        self._active_material_container = None
        Application.getInstance().getMachineManager().activeMaterialChanged.connect(self._onActiveMaterialChanged)
        self._onActiveMaterialChanged()

        self._material_amounts = []
Example #2
0
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._current_print_time = Duration(None, self)

        self._material_amount = -1

        self._backend = Application.getInstance().getBackend()
        if self._backend:
            self._backend.printDurationMessage.connect(self._onPrintDurationMessage)

    currentPrintTimeChanged = pyqtSignal()
    
    @pyqtProperty(Duration, notify = currentPrintTimeChanged)
    def currentPrintTime(self):
        return self._current_print_time

    materialAmountChanged = pyqtSignal()
    
    @pyqtProperty(float, notify = materialAmountChanged)
    def materialAmount(self):
        return self._material_amount

    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 = Application.getInstance().getGlobalContainerStack().getProperty("material_diameter", "value") / 2
        self._material_amount = round((amount / (math.pi * r ** 2)) / 1000, 2)
        self.materialAmountChanged.emit()
Example #3
0
    def __init__(self, parent = None):
        super().__init__(parent)

        self._current_print_time = Duration(None, self)

        self._material_amount = -1

        self._backend = Application.getInstance().getBackend()
        if self._backend:
            self._backend.printDurationMessage.connect(self._onPrintDurationMessage)
Example #4
0
    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)
Example #5
0
    def __init__(self, parent = None):
        super().__init__(parent)

        self._current_print_time = Duration(None, self)

        self._material_amounts = []

        self._backend = Application.getInstance().getBackend()
        if self._backend:
            self._backend.printDurationMessage.connect(self._onPrintDurationMessage)

        self._job_name = ""
        self._abbr_machine = ""

        Application.getInstance().globalContainerStackChanged.connect(self._setAbbreviatedMachineName)
        Application.getInstance().fileLoaded.connect(self.setJobName)
Example #6
0
 def _initPrintTimeMessageValues(self, build_plate_number):
     # Full fill message values using keys from _print_time_message_translations
     self._print_time_message_values[build_plate_number] = {}
     for key in self._print_time_message_translations.keys():
         self._print_time_message_values[build_plate_number][key] = Duration(None, self)
Example #7
0
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()

            self._material_amount = round(amount / 10) / 100
            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
Example #8
0
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._current_print_time = Duration(None, self)

        self._material_lengths = []
        self._material_weights = []
        self._material_costs = []

        self._pre_sliced = False

        self._backend = Application.getInstance().getBackend()
        if self._backend:
            self._backend.printDurationMessage.connect(self._onPrintDurationMessage)

        self._job_name = ""
        self._abbr_machine = ""

        Application.getInstance().globalContainerStackChanged.connect(self._setAbbreviatedMachineName)
        Application.getInstance().fileLoaded.connect(self.setJobName)

        Preferences.getInstance().preferenceChanged.connect(self._onPreferencesChanged)

        self._active_material_container = None
        Application.getInstance().getMachineManager().activeMaterialChanged.connect(self._onActiveMaterialChanged)
        self._onActiveMaterialChanged()

        self._material_amounts = []

    currentPrintTimeChanged = pyqtSignal()

    preSlicedChanged = pyqtSignal()

    @pyqtProperty(bool, notify=preSlicedChanged)
    def preSliced(self):
        return self._pre_sliced

    def setPreSliced(self, pre_sliced):
        self._pre_sliced = pre_sliced
        self.preSlicedChanged.emit()

    @pyqtProperty(Duration, notify = currentPrintTimeChanged)
    def currentPrintTime(self):
        return self._current_print_time

    materialLengthsChanged = pyqtSignal()

    @pyqtProperty("QVariantList", notify = materialLengthsChanged)
    def materialLengths(self):
        return self._material_lengths

    materialWeightsChanged = pyqtSignal()

    @pyqtProperty("QVariantList", notify = materialWeightsChanged)
    def materialWeights(self):
        return self._material_weights

    materialCostsChanged = pyqtSignal()

    @pyqtProperty("QVariantList", notify = materialCostsChanged)
    def materialCosts(self):
        return self._material_costs

    def _onPrintDurationMessage(self, total_time, material_amounts):
        if total_time != total_time:  # Check for NaN. Engine can sometimes give us weird values.
            Logger.log("w", "Received NaN for print duration message")
            self._current_print_time.setDuration(0)
        else:
            self._current_print_time.setDuration(total_time)

        self.currentPrintTimeChanged.emit()

        self._material_amounts = material_amounts
        self._calculateInformation()

    def _calculateInformation(self):
        if Application.getInstance().getGlobalContainerStack() is None:
            return

        # Material amount is sent as an amount of mm^3, so calculate length from that
        radius = Application.getInstance().getGlobalContainerStack().getProperty("material_diameter", "value") / 2
        self._material_lengths = []
        self._material_weights = []
        self._material_costs = []

        material_preference_values = json.loads(Preferences.getInstance().getValue("cura/material_settings"))

        extruder_stacks = list(ExtruderManager.getInstance().getMachineExtruders(Application.getInstance().getGlobalContainerStack().getId()))
        for index, amount in enumerate(self._material_amounts):
            ## Find the right extruder stack. As the list isn't sorted because it's a annoying generator, we do some
            #  list comprehension filtering to solve this for us.
            material = None
            if extruder_stacks:  # Multi extrusion machine
                extruder_stack = [extruder for extruder in extruder_stacks if extruder.getMetaDataEntry("position") == str(index)][0]
                density = extruder_stack.getMetaDataEntry("properties", {}).get("density", 0)
                material = extruder_stack.findContainer({"type": "material"})
            else:  # Machine with no extruder stacks
                density = Application.getInstance().getGlobalContainerStack().getMetaDataEntry("properties", {}).get("density", 0)
                material = Application.getInstance().getGlobalContainerStack().findContainer({"type": "material"})

            weight = float(amount) * float(density) / 1000
            cost = 0
            if material:
                material_guid = material.getMetaDataEntry("GUID")
                if material_guid in material_preference_values:
                    material_values = material_preference_values[material_guid]

                    weight_per_spool = float(material_values["spool_weight"] if material_values and "spool_weight" in material_values else 0)
                    cost_per_spool = float(material_values["spool_cost"] if material_values and "spool_cost" in material_values else 0)

                    if weight_per_spool != 0:
                        cost = cost_per_spool * weight / weight_per_spool
                    else:
                        cost = 0

            if radius != 0:
                length = round((amount / (math.pi * radius ** 2)) / 1000, 2)
            else:
                length = 0
            self._material_weights.append(weight)
            self._material_lengths.append(length)
            self._material_costs.append(cost)

        self.materialLengthsChanged.emit()
        self.materialWeightsChanged.emit()
        self.materialCostsChanged.emit()

    def _onPreferencesChanged(self, preference):
        if preference != "cura/material_settings":
            return

        self._calculateInformation()

    def _onActiveMaterialChanged(self):
        if self._active_material_container:
            self._active_material_container.metaDataChanged.disconnect(self._onMaterialMetaDataChanged)

        active_material_id = Application.getInstance().getMachineManager().activeMaterialId
        active_material_containers = ContainerRegistry.getInstance().findInstanceContainers(id=active_material_id)

        if active_material_containers:
            self._active_material_container = active_material_containers[0]
            self._active_material_container.metaDataChanged.connect(self._onMaterialMetaDataChanged)

    def _onMaterialMetaDataChanged(self, *args, **kwargs):
        self._calculateInformation()

    @pyqtSlot(str)
    def setJobName(self, name):
        # Ensure that we don't use entire path but only filename
        name = os.path.basename(name)

        # when a file is opened using the terminal; the filename comes from _onFileLoaded and still contains its
        # extension. This cuts the extension off if necessary.
        name = os.path.splitext(name)[0]
        if self._job_name != name:
            self._job_name = name
            self.jobNameChanged.emit()

    jobNameChanged = pyqtSignal()

    @pyqtProperty(str, notify = jobNameChanged)
    def jobName(self):
        return self._job_name

    @pyqtSlot(str, result = str)
    def createJobName(self, base_name):
        if base_name == "":
            return ""
        base_name = self._stripAccents(base_name)
        self._setAbbreviatedMachineName()
        if self._pre_sliced:
            return catalog.i18nc("@label", "Pre-sliced file {0}", base_name)
        elif Preferences.getInstance().getValue("cura/jobname_prefix"):
            # Don't add abbreviation if it already has the exact same abbreviation.
            if base_name.startswith(self._abbr_machine + "_"):
                return base_name
            return self._abbr_machine + "_" + base_name
        else:
            return base_name

    ##  Created an acronymn-like abbreviated machine name from the currently active machine name
    #   Called each time the global stack is switched
    def _setAbbreviatedMachineName(self):
        global_container_stack = Application.getInstance().getGlobalContainerStack()
        if not global_container_stack:
            self._abbr_machine = ""
            return

        global_stack_name = global_container_stack.getName()
        split_name = global_stack_name.split(" ")
        abbr_machine = ""
        for word in split_name:
            if word.lower() == "ultimaker":
                abbr_machine += "UM"
            elif word.isdigit():
                abbr_machine += word
            else:
                abbr_machine += self._stripAccents(word.strip("()[]{}#").upper())[0]

        self._abbr_machine = abbr_machine

    ##  Utility method that strips accents from characters (eg: â -> a)
    def _stripAccents(self, str):
       return ''.join(char for char in unicodedata.normalize('NFD', str) if unicodedata.category(char) != 'Mn')
Example #9
0
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._current_print_time = Duration(None, self)

        self._material_amount = -1

        self._backend = Application.getInstance().getBackend()
        if self._backend:
            self._backend.printDurationMessage.connect(
                self._onPrintDurationMessage)

        self._job_name = ""
        self._abbr_machine = ""

        Application.getInstance().globalContainerStackChanged.connect(
            self._setAbbreviatedMachineName)
        Application.getInstance().fileLoaded.connect(self.setJobName)

    currentPrintTimeChanged = pyqtSignal()

    @pyqtProperty(Duration, notify=currentPrintTimeChanged)
    def currentPrintTime(self):
        return self._current_print_time

    materialAmountChanged = pyqtSignal()

    @pyqtProperty(float, notify=materialAmountChanged)
    def materialAmount(self):
        return self._material_amount

    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 = Application.getInstance().getGlobalContainerStack().getProperty(
            "material_diameter", "value") / 2
        self._material_amount = round((amount / (math.pi * r**2)) / 1000, 2)
        self.materialAmountChanged.emit()

    @pyqtSlot(str)
    def setJobName(self, name):
        # when a file is opened using the terminal; the filename comes from _onFileLoaded and still contains its
        # extension. This cuts the extension off if necessary.
        name = os.path.splitext(name)[0]
        if self._job_name != name:
            self._job_name = name
            self.jobNameChanged.emit()

    jobNameChanged = pyqtSignal()

    @pyqtProperty(str, notify=jobNameChanged)
    def jobName(self):
        return self._job_name

    @pyqtSlot(str, result=str)
    def createJobName(self, base_name):
        base_name = self._stripAccents(base_name)
        if Preferences.getInstance().getValue("cura/jobname_prefix"):
            return self._abbr_machine + "_" + base_name
        else:
            return base_name

    ##  Created an acronymn-like abbreviated machine name from the currently active machine name
    #   Called each time the global stack is switched
    def _setAbbreviatedMachineName(self):
        global_stack_name = Application.getInstance().getGlobalContainerStack(
        ).getName()
        split_name = global_stack_name.split(" ")
        abbr_machine = ""
        for word in split_name:
            if word.lower() == "ultimaker":
                abbr_machine += "UM"
            elif word.isdigit():
                abbr_machine += word
            else:
                abbr_machine += self._stripAccents(
                    word.strip("()[]{}#").upper())[0]

        self._abbr_machine = abbr_machine

    ##  Utility method that strips accents from characters (eg: â -> a)
    def _stripAccents(self, str):
        return ''.join(char for char in unicodedata.normalize('NFD', str)
                       if unicodedata.category(char) != 'Mn')
Example #10
0
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._current_print_time = Duration(None, self)

        self._material_amounts = []

        self._backend = Application.getInstance().getBackend()
        if self._backend:
            self._backend.printDurationMessage.connect(self._onPrintDurationMessage)

        self._job_name = ""
        self._abbr_machine = ""

        Application.getInstance().globalContainerStackChanged.connect(self._setAbbreviatedMachineName)
        Application.getInstance().fileLoaded.connect(self.setJobName)

    currentPrintTimeChanged = pyqtSignal()

    @pyqtProperty(Duration, notify = currentPrintTimeChanged)
    def currentPrintTime(self):
        return self._current_print_time

    materialAmountsChanged = pyqtSignal()

    @pyqtProperty("QVariantList", notify = materialAmountsChanged)
    def materialAmounts(self):
        return self._material_amounts

    def _onPrintDurationMessage(self, total_time, material_amounts):
        self._current_print_time.setDuration(total_time)
        self.currentPrintTimeChanged.emit()

        # Material amount is sent as an amount of mm^3, so calculate length from that
        r = Application.getInstance().getGlobalContainerStack().getProperty("material_diameter", "value") / 2
        self._material_amounts = []
        for amount in material_amounts:
            self._material_amounts.append(round((amount / (math.pi * r ** 2)) / 1000, 2))
        self.materialAmountsChanged.emit()

    @pyqtSlot(str)
    def setJobName(self, name):
        # when a file is opened using the terminal; the filename comes from _onFileLoaded and still contains its
        # extension. This cuts the extension off if necessary.
        name = os.path.splitext(name)[0]
        if self._job_name != name:
            self._job_name = name
            self.jobNameChanged.emit()

    jobNameChanged = pyqtSignal()

    @pyqtProperty(str, notify = jobNameChanged)
    def jobName(self):
        return self._job_name

    @pyqtSlot(str, result = str)
    def createJobName(self, base_name):
        base_name = self._stripAccents(base_name)
        if Preferences.getInstance().getValue("cura/jobname_prefix"):
            return self._abbr_machine + "_" + base_name
        else:
            return base_name

    ##  Created an acronymn-like abbreviated machine name from the currently active machine name
    #   Called each time the global stack is switched
    def _setAbbreviatedMachineName(self):
        global_stack_name = Application.getInstance().getGlobalContainerStack().getName()
        split_name = global_stack_name.split(" ")
        abbr_machine = ""
        for word in split_name:
            if word.lower() == "ultimaker":
                abbr_machine += "UM"
            elif word.isdigit():
                abbr_machine += word
            else:
                abbr_machine += self._stripAccents(word.strip("()[]{}#").upper())[0]

        self._abbr_machine = abbr_machine

    ##  Utility method that strips accents from characters (eg: â -> a)
    def _stripAccents(self, str):
       return ''.join(char for char in unicodedata.normalize('NFD', str) if unicodedata.category(char) != 'Mn')
Example #11
0
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()

            self._material_amount = round(amount / 10) / 100
            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
Example #12
0
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._current_print_time = Duration(None, self)

        self._material_lengths = []
        self._material_weights = []

        self._backend = Application.getInstance().getBackend()
        if self._backend:
            self._backend.printDurationMessage.connect(self._onPrintDurationMessage)

        self._job_name = ""
        self._abbr_machine = ""

        Application.getInstance().globalContainerStackChanged.connect(self._setAbbreviatedMachineName)
        Application.getInstance().fileLoaded.connect(self.setJobName)

    currentPrintTimeChanged = pyqtSignal()

    @pyqtProperty(Duration, notify = currentPrintTimeChanged)
    def currentPrintTime(self):
        return self._current_print_time

    materialLengthsChanged = pyqtSignal()

    @pyqtProperty("QVariantList", notify = materialLengthsChanged)
    def materialLengths(self):
        return self._material_lengths

    materialWeightsChanged = pyqtSignal()

    @pyqtProperty("QVariantList", notify = materialWeightsChanged)
    def materialWeights(self):
        return self._material_weights

    def _onPrintDurationMessage(self, total_time, material_amounts):
        self._current_print_time.setDuration(total_time)
        self.currentPrintTimeChanged.emit()

        # Material amount is sent as an amount of mm^3, so calculate length from that
        r = Application.getInstance().getGlobalContainerStack().getProperty("material_diameter", "value") / 2
        self._material_lengths = []
        self._material_weights = []
        extruder_stacks = list(cura.Settings.ExtruderManager.getInstance().getMachineExtruders(Application.getInstance().getGlobalContainerStack().getId()))
        for index, amount in enumerate(material_amounts):
            ## Find the right extruder stack. As the list isn't sorted because it's a annoying generator, we do some
            #  list comprehension filtering to solve this for us.
            if extruder_stacks:  # Multi extrusion machine
                extruder_stack = [extruder for extruder in extruder_stacks if extruder.getMetaDataEntry("position") == str(index)][0]
                density = extruder_stack.getMetaDataEntry("properties", {}).get("density", 0)
            else:  # Machine with no extruder stacks
                density = Application.getInstance().getGlobalContainerStack().getMetaDataEntry("properties", {}).get("density", 0)

            self._material_weights.append(float(amount) * float(density) / 1000)
            self._material_lengths.append(round((amount / (math.pi * r ** 2)) / 1000, 2))
        self.materialLengthsChanged.emit()
        self.materialWeightsChanged.emit()

    @pyqtSlot(str)
    def setJobName(self, name):
        # Ensure that we don't use entire path but only filename
        name = os.path.basename(name)

        # when a file is opened using the terminal; the filename comes from _onFileLoaded and still contains its
        # extension. This cuts the extension off if necessary.
        name = os.path.splitext(name)[0]
        if self._job_name != name:
            self._job_name = name
            self.jobNameChanged.emit()

    jobNameChanged = pyqtSignal()

    @pyqtProperty(str, notify = jobNameChanged)
    def jobName(self):
        return self._job_name

    @pyqtSlot(str, result = str)
    def createJobName(self, base_name):
        base_name = self._stripAccents(base_name)
        self._setAbbreviatedMachineName()
        if Preferences.getInstance().getValue("cura/jobname_prefix"):
            return self._abbr_machine + "_" + base_name
        else:
            return base_name

    ##  Created an acronymn-like abbreviated machine name from the currently active machine name
    #   Called each time the global stack is switched
    def _setAbbreviatedMachineName(self):
        global_container_stack = Application.getInstance().getGlobalContainerStack()
        if not global_container_stack:
            self._abbr_machine = ""
            return

        global_stack_name = global_container_stack.getName()
        split_name = global_stack_name.split(" ")
        abbr_machine = ""
        for word in split_name:
            if word.lower() == "ultimaker":
                abbr_machine += "UM"
            elif word.isdigit():
                abbr_machine += word
            else:
                abbr_machine += self._stripAccents(word.strip("()[]{}#").upper())[0]

        self._abbr_machine = abbr_machine

    ##  Utility method that strips accents from characters (eg: â -> a)
    def _stripAccents(self, str):
       return ''.join(char for char in unicodedata.normalize('NFD', str) if unicodedata.category(char) != 'Mn')
Example #13
0
    def __call__(self, job: pywim.http.thor.JobInfo) -> bool:
        Logger.log("d", "Current job status: {}".format(job.status))
        self.connector.api_connection.clearErrorMessage()
        self.connector._proxy.jobProgress = job.progress
        if job.status == pywim.http.thor.JobInfo.Status.queued and self.connector.status is not SmartSliceCloudStatus.Queued:
            self.connector.status = SmartSliceCloudStatus.Queued
            self.connector.updateSliceWidget()
        elif job.status == pywim.http.thor.JobInfo.Status.running and self.connector.status not in (SmartSliceCloudStatus.BusyOptimizing, SmartSliceCloudStatus.BusyValidating):
            self.connector.status = self._previous_status
            self.connector.updateSliceWidget()

        if job.status == pywim.http.thor.JobInfo.Status.running and self.connector.status is SmartSliceCloudStatus.BusyOptimizing:
            self.connector._proxy.sliceStatus = "Optimizing...&nbsp;&nbsp;&nbsp;&nbsp;(<i>Remaining Time: {}</i>)".format(Duration(job.runtime_remaining).getDisplayString())

        return self.connector.cloudJob.canceled if self.connector.cloudJob else True
    def __init__(self) -> None:
        super().__init__()

        # Primary Button (Slice/Validate/Optimize)
        self._sliceStatusEnum = SmartSliceCloudStatus.Errors
        self._sliceStatus = "_Status"
        self._sliceHint = "_Hint"
        self._sliceButtonText = "_ButtonText"
        self._sliceButtonEnabled = False
        self._sliceButtonVisible = True
        self._sliceButtonFillWidth = True
        self._sliceIconImage = ""
        self._sliceIconVisible = False
        self._sliceInfoOpen = False
        self._errors = {}

        self._job_progress = 0
        self._progress_bar_visible = False

        # Secondary Button (Preview/Cancel)
        self._secondaryButtonText = "_SecondaryText"
        self._secondaryButtonFillWidth = False
        self._secondaryButtonVisible = False

        self._loadDialog = Dialog.Dialog()
        self._resultsTableDialog = Dialog.Dialog()

        # Proxy Values (DO NOT USE DIRECTLY)
        self._targetFactorOfSafety = 2.0
        self._targetMaximalDisplacement = 1.0

        self._safetyFactorColor = "#000000"
        self._maxDisplaceColor = "#000000"

        #  Use-case & Requirements Cache
        self.reqsMaxDeflect = self._targetMaximalDisplacement

        # Results table
        self._resultsTable = ResultTableData()
        self._resultsTable.updateDisplaySignal.connect(
            self.updatePropertiesFromResults)
        self._resultsTable.resultsUpdated.connect(self._resultsTableUpdated)

        # Properties (mainly) for the sliceinfo widget
        self._resultSafetyFactor = 0.0  #copy.copy(self._targetFactorOfSafety)
        self._resultMaximalDisplacement = 0.0  #copy.copy(self._targetMaximalDisplacement)
        self._resultTimeTotal = Duration()
        self._resultTimeInfill = Duration()
        self._resultTimeInnerWalls = Duration()
        self._resultTimeOuterWalls = Duration()
        self._resultTimeRetractions = Duration()
        self._resultTimeSkin = Duration()
        self._resultTimeSkirt = Duration()
        self._resultTimeTravel = Duration()
        self._resultTimes = (self._resultTimeInfill,
                             self._resultTimeInnerWalls,
                             self._resultTimeOuterWalls,
                             self._resultTimeRetractions, self._resultTimeSkin,
                             self._resultTimeSkirt, self._resultTimeTravel)
        self._percentageTimeInfill = 0.0
        self._percentageTimeInnerWalls = 0.0
        self._percentageTimeOuterWalls = 0.0
        self._percentageTimeRetractions = 0.0
        self._percentageTimeSkin = 0.0
        self._percentageTimeSkirt = 0.0
        self._percentageTimeTravel = 0.0

        self.resultTimeInfillChanged.connect(self._onResultTimeChanged)
        self.resultTimeInnerWallsChanged.connect(self._onResultTimeChanged)
        self.resultTimeOuterWallsChanged.connect(self._onResultTimeChanged)
        self.resultTimeRetractionsChanged.connect(self._onResultTimeChanged)
        self.resultTimeSkinChanged.connect(self._onResultTimeChanged)
        self.resultTimeSkirtChanged.connect(self._onResultTimeChanged)
        self.resultTimeTravelChanged.connect(self._onResultTimeChanged)

        self._materialName = None
        self._materialCost = 0.0
        self._materialLength = 0.0
        self._materialWeight = 0.0
Example #15
0
 def formatDuration(self, seconds: int) -> str:
     return Duration(seconds).getDisplayString(DurationFormat.Format.Short)
Example #16
0
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._current_print_time = Duration(None, self)

        self._material_lengths = []
        self._material_weights = []

        self._backend = Application.getInstance().getBackend()
        if self._backend:
            self._backend.printDurationMessage.connect(self._onPrintDurationMessage)

        self._job_name = ""
        self._abbr_machine = ""

        Application.getInstance().globalContainerStackChanged.connect(self._setAbbreviatedMachineName)
        Application.getInstance().fileLoaded.connect(self.setJobName)

    currentPrintTimeChanged = pyqtSignal()

    @pyqtProperty(Duration, notify=currentPrintTimeChanged)
    def currentPrintTime(self):
        return self._current_print_time

    materialLengthsChanged = pyqtSignal()

    @pyqtProperty("QVariantList", notify=materialLengthsChanged)
    def materialLengths(self):
        return self._material_lengths

    materialWeightsChanged = pyqtSignal()

    @pyqtProperty("QVariantList", notify=materialWeightsChanged)
    def materialWeights(self):
        return self._material_weights

    def _onPrintDurationMessage(self, total_time, material_amounts):
        self._current_print_time.setDuration(total_time)
        self.currentPrintTimeChanged.emit()

        # Material amount is sent as an amount of mm^3, so calculate length from that
        r = Application.getInstance().getGlobalContainerStack().getProperty("material_diameter", "value") / 2
        self._material_lengths = []
        self._material_weights = []
        extruder_stacks = list(
            cura.Settings.ExtruderManager.getInstance().getMachineExtruders(
                Application.getInstance().getGlobalContainerStack().getId()
            )
        )
        for index, amount in enumerate(material_amounts):
            ## Find the right extruder stack. As the list isn't sorted because it's a annoying generator, we do some
            #  list comprehension filtering to solve this for us.
            if extruder_stacks:  # Multi extrusion machine
                extruder_stack = [
                    extruder for extruder in extruder_stacks if extruder.getMetaDataEntry("position") == str(index)
                ][0]
                density = extruder_stack.getMetaDataEntry("properties", {}).get("density", 0)
            else:  # Machine with no extruder stacks
                density = (
                    Application.getInstance()
                    .getGlobalContainerStack()
                    .getMetaDataEntry("properties", {})
                    .get("density", 0)
                )

            self._material_weights.append(float(amount) * float(density) / 1000)
            self._material_lengths.append(round((amount / (math.pi * r ** 2)) / 1000, 2))
        self.materialLengthsChanged.emit()
        self.materialWeightsChanged.emit()

    @pyqtSlot(str)
    def setJobName(self, name):
        # Ensure that we don't use entire path but only filename
        name = os.path.basename(name)

        # when a file is opened using the terminal; the filename comes from _onFileLoaded and still contains its
        # extension. This cuts the extension off if necessary.
        name = os.path.splitext(name)[0]
        if self._job_name != name:
            self._job_name = name
            self.jobNameChanged.emit()

    jobNameChanged = pyqtSignal()

    @pyqtProperty(str, notify=jobNameChanged)
    def jobName(self):
        return self._job_name

    @pyqtSlot(str, result=str)
    def createJobName(self, base_name):
        base_name = self._stripAccents(base_name)
        if Preferences.getInstance().getValue("cura/jobname_prefix"):
            return self._abbr_machine + "_" + base_name
        else:
            return base_name

    ##  Created an acronymn-like abbreviated machine name from the currently active machine name
    #   Called each time the global stack is switched
    def _setAbbreviatedMachineName(self):
        global_stack_name = Application.getInstance().getGlobalContainerStack().getName()
        split_name = global_stack_name.split(" ")
        abbr_machine = ""
        for word in split_name:
            if word.lower() == "ultimaker":
                abbr_machine += "UM"
            elif word.isdigit():
                abbr_machine += word
            else:
                abbr_machine += self._stripAccents(word.strip("()[]{}#").upper())[0]

        self._abbr_machine = abbr_machine

    ##  Utility method that strips accents from characters (eg: â -> a)
    def _stripAccents(self, str):
        return "".join(char for char in unicodedata.normalize("NFD", str) if unicodedata.category(char) != "Mn")
Example #17
0
 def _initPrintTimesPerFeature(self, build_plate_number: int) -> None:
     # Full fill message values using keys from _print_time_message_translations
     self._print_times_per_feature[build_plate_number] = {}
     for key in self._print_time_message_translations.keys():
         self._print_times_per_feature[build_plate_number][key] = Duration(
             None, self)
Example #18
0
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.initializeCuraMessagePrintTimeProperties()

        self._material_lengths = []
        self._material_weights = []
        self._material_costs = []
        self._material_names = []

        self._pre_sliced = False

        self._backend = Application.getInstance().getBackend()
        if self._backend:
            self._backend.printDurationMessage.connect(
                self._onPrintDurationMessage)

        self._base_name = ""
        self._abbr_machine = ""
        self._job_name = ""

        Application.getInstance().globalContainerStackChanged.connect(
            self._updateJobName)
        Application.getInstance().fileLoaded.connect(self.setBaseName)
        Application.getInstance().workspaceLoaded.connect(self.setProjectName)
        Preferences.getInstance().preferenceChanged.connect(
            self._onPreferencesChanged)

        self._active_material_container = None
        Application.getInstance().getMachineManager(
        ).activeMaterialChanged.connect(self._onActiveMaterialChanged)
        self._onActiveMaterialChanged()

        self._material_amounts = []

    # Crate cura message translations and using translation keys initialize empty time Duration object for total time
    # and time for each feature
    def initializeCuraMessagePrintTimeProperties(self):
        self._current_print_time = Duration(None, self)

        self._print_time_message_translations = {
            "inset_0": catalog.i18nc("@tooltip", "Outer Wall"),
            "inset_x": catalog.i18nc("@tooltip", "Inner Walls"),
            "skin": catalog.i18nc("@tooltip", "Skin"),
            "infill": catalog.i18nc("@tooltip", "Infill"),
            "support_infill": catalog.i18nc("@tooltip", "Support Infill"),
            "support_interface": catalog.i18nc("@tooltip",
                                               "Support Interface"),
            "support": catalog.i18nc("@tooltip", "Support"),
            "skirt": catalog.i18nc("@tooltip", "Skirt"),
            "travel": catalog.i18nc("@tooltip", "Travel"),
            "retract": catalog.i18nc("@tooltip", "Retractions"),
            "none": catalog.i18nc("@tooltip", "Other")
        }

        self._print_time_message_values = {}

        # Full fill message values using keys from _print_time_message_translations
        for key in self._print_time_message_translations.keys():
            self._print_time_message_values[key] = Duration(None, self)

    currentPrintTimeChanged = pyqtSignal()

    preSlicedChanged = pyqtSignal()

    @pyqtProperty(bool, notify=preSlicedChanged)
    def preSliced(self):
        return self._pre_sliced

    def setPreSliced(self, pre_sliced):
        self._pre_sliced = pre_sliced
        self.preSlicedChanged.emit()

    @pyqtProperty(Duration, notify=currentPrintTimeChanged)
    def currentPrintTime(self):
        return self._current_print_time

    materialLengthsChanged = pyqtSignal()

    @pyqtProperty("QVariantList", notify=materialLengthsChanged)
    def materialLengths(self):
        return self._material_lengths

    materialWeightsChanged = pyqtSignal()

    @pyqtProperty("QVariantList", notify=materialWeightsChanged)
    def materialWeights(self):
        return self._material_weights

    materialCostsChanged = pyqtSignal()

    @pyqtProperty("QVariantList", notify=materialCostsChanged)
    def materialCosts(self):
        return self._material_costs

    materialNamesChanged = pyqtSignal()

    @pyqtProperty("QVariantList", notify=materialNamesChanged)
    def materialNames(self):
        return self._material_names

    def _onPrintDurationMessage(self, print_time, material_amounts):

        self._updateTotalPrintTimePerFeature(print_time)
        self.currentPrintTimeChanged.emit()

        self._material_amounts = material_amounts
        self._calculateInformation()

    def _updateTotalPrintTimePerFeature(self, print_time):
        total_estimated_time = 0

        for feature, time in print_time.items():
            if time != time:  # Check for NaN. Engine can sometimes give us weird values.
                self._print_time_message_values.get(feature).setDuration(0)
                Logger.log("w", "Received NaN for print duration message")
                continue

            total_estimated_time += time
            self._print_time_message_values.get(feature).setDuration(time)

        self._current_print_time.setDuration(total_estimated_time)

    def _calculateInformation(self):
        if Application.getInstance().getGlobalContainerStack() is None:
            return

        # Material amount is sent as an amount of mm^3, so calculate length from that
        radius = Application.getInstance().getGlobalContainerStack(
        ).getProperty("material_diameter", "value") / 2
        self._material_lengths = []
        self._material_weights = []
        self._material_costs = []
        self._material_names = []

        material_preference_values = json.loads(
            Preferences.getInstance().getValue("cura/material_settings"))

        extruder_stacks = list(
            ExtruderManager.getInstance().getMachineExtruders(
                Application.getInstance().getGlobalContainerStack().getId()))
        for index, amount in enumerate(self._material_amounts):
            ## Find the right extruder stack. As the list isn't sorted because it's a annoying generator, we do some
            #  list comprehension filtering to solve this for us.
            material = None
            if extruder_stacks:  # Multi extrusion machine
                extruder_stack = [
                    extruder for extruder in extruder_stacks
                    if extruder.getMetaDataEntry("position") == str(index)
                ][0]
                density = extruder_stack.getMetaDataEntry("properties",
                                                          {}).get(
                                                              "density", 0)
                material = extruder_stack.findContainer({"type": "material"})
            else:  # Machine with no extruder stacks
                density = Application.getInstance().getGlobalContainerStack(
                ).getMetaDataEntry("properties", {}).get("density", 0)
                material = Application.getInstance().getGlobalContainerStack(
                ).findContainer({"type": "material"})

            weight = float(amount) * float(density) / 1000
            cost = 0
            material_name = catalog.i18nc("@label unknown material", "Unknown")
            if material:
                material_guid = material.getMetaDataEntry("GUID")
                material_name = material.getName()
                if material_guid in material_preference_values:
                    material_values = material_preference_values[material_guid]

                    weight_per_spool = float(
                        material_values["spool_weight"] if material_values
                        and "spool_weight" in material_values else 0)
                    cost_per_spool = float(
                        material_values["spool_cost"] if material_values
                        and "spool_cost" in material_values else 0)

                    if weight_per_spool != 0:
                        cost = cost_per_spool * weight / weight_per_spool
                    else:
                        cost = 0

            if radius != 0:
                length = round((amount / (math.pi * radius**2)) / 1000, 2)
            else:
                length = 0
            self._material_weights.append(weight)
            self._material_lengths.append(length)
            self._material_costs.append(cost)
            self._material_names.append(material_name)

        self.materialLengthsChanged.emit()
        self.materialWeightsChanged.emit()
        self.materialCostsChanged.emit()
        self.materialNamesChanged.emit()

    def _onPreferencesChanged(self, preference):
        if preference != "cura/material_settings":
            return

        self._calculateInformation()

    def _onActiveMaterialChanged(self):
        if self._active_material_container:
            try:
                self._active_material_container.metaDataChanged.disconnect(
                    self._onMaterialMetaDataChanged)
            except TypeError:  #pyQtSignal gives a TypeError when disconnecting from something that is already disconnected.
                pass

        active_material_id = Application.getInstance().getMachineManager(
        ).activeMaterialId
        active_material_containers = ContainerRegistry.getInstance(
        ).findInstanceContainers(id=active_material_id)

        if active_material_containers:
            self._active_material_container = active_material_containers[0]
            self._active_material_container.metaDataChanged.connect(
                self._onMaterialMetaDataChanged)

    def _onMaterialMetaDataChanged(self, *args, **kwargs):
        self._calculateInformation()

    @pyqtSlot(str)
    def setJobName(self, name):
        self._job_name = name
        self.jobNameChanged.emit()

    jobNameChanged = pyqtSignal()

    @pyqtProperty(str, notify=jobNameChanged)
    def jobName(self):
        return self._job_name

    def _updateJobName(self):
        if self._base_name == "":
            self._job_name = ""
            self.jobNameChanged.emit()
            return

        base_name = self._stripAccents(self._base_name)
        self._setAbbreviatedMachineName()
        if self._pre_sliced:
            self._job_name = catalog.i18nc("@label", "Pre-sliced file {0}",
                                           base_name)
        elif Preferences.getInstance().getValue("cura/jobname_prefix"):
            # Don't add abbreviation if it already has the exact same abbreviation.
            if base_name.startswith(self._abbr_machine + "_"):
                self._job_name = base_name
            else:
                self._job_name = self._abbr_machine + "_" + base_name
        else:
            self._job_name = base_name

        self.jobNameChanged.emit()

    @pyqtProperty(str)
    def baseName(self):
        return self._base_name

    @pyqtSlot(str)
    def setProjectName(self, name):
        self.setBaseName(name, is_project_file=True)

    @pyqtSlot(str)
    def setBaseName(self, base_name, is_project_file=False):
        # Ensure that we don't use entire path but only filename
        name = os.path.basename(base_name)

        # when a file is opened using the terminal; the filename comes from _onFileLoaded and still contains its
        # extension. This cuts the extension off if necessary.
        name = os.path.splitext(name)[0]

        # if this is a profile file, always update the job name
        # name is "" when I first had some meshes and afterwards I deleted them so the naming should start again
        is_empty = name == ""
        if is_project_file or (is_empty or (self._base_name == ""
                                            and self._base_name != name)):
            # remove ".curaproject" suffix from (imported) the file name
            if name.endswith(".curaproject"):
                name = name[:name.rfind(".curaproject")]
            self._base_name = name
            self._updateJobName()

    ##  Created an acronymn-like abbreviated machine name from the currently active machine name
    #   Called each time the global stack is switched
    def _setAbbreviatedMachineName(self):
        global_container_stack = Application.getInstance(
        ).getGlobalContainerStack()
        if not global_container_stack:
            self._abbr_machine = ""
            return

        global_stack_name = global_container_stack.getName()
        abbr_machine = ""
        for word in re.findall(r"[\w']+", global_stack_name):
            if word.lower() == "ultimaker":
                abbr_machine += "UM"
            elif word.isdigit():
                abbr_machine += word
            else:
                stripped_word = self._stripAccents(word.upper())
                # - use only the first character if the word is too long (> 3 characters)
                # - use the whole word if it's not too long (<= 3 characters)
                if len(stripped_word) > 3:
                    stripped_word = stripped_word[0]
                abbr_machine += stripped_word

        self._abbr_machine = abbr_machine

    ##  Utility method that strips accents from characters (eg: â -> a)
    def _stripAccents(self, str):
        return ''.join(char for char in unicodedata.normalize('NFD', str)
                       if unicodedata.category(char) != 'Mn')

    @pyqtSlot(result="QVariantMap")
    def getFeaturePrintTimes(self):
        result = {}
        for feature, time in self._print_time_message_values.items():
            if feature in self._print_time_message_translations:
                result[self._print_time_message_translations[feature]] = time
            else:
                result[feature] = time
        return result

    # Simulate message with zero time duration
    def setToZeroPrintInformation(self):
        temp_message = {}
        for key in self._print_time_message_values.keys():
            temp_message[key] = 0

        temp_material_amounts = [0]
        self._onPrintDurationMessage(temp_message, temp_material_amounts)
Example #19
0
    def __init__(self, parent = None):
        super().__init__(parent)

        self._current_print_time = Duration(None, self)
        self._print_times_per_feature = {
            "none": Duration(None, self),
            "inset_0": Duration(None, self),
            "inset_x": Duration(None, self),
            "skin": Duration(None, self),
            "support": Duration(None, self),
            "skirt": Duration(None, self),
            "infill": Duration(None, self),
            "support_infill": Duration(None, self),
            "travel": Duration(None, self),
            "retract": Duration(None, self),
            "support_interface": Duration(None, self)
        }

        self._material_lengths = []
        self._material_weights = []
        self._material_costs = []

        self._pre_sliced = False

        self._backend = Application.getInstance().getBackend()
        if self._backend:
            self._backend.printDurationMessage.connect(self._onPrintDurationMessage)

        self._job_name = ""
        self._abbr_machine = ""

        Application.getInstance().globalContainerStackChanged.connect(self._setAbbreviatedMachineName)
        Application.getInstance().fileLoaded.connect(self.setJobName)

        Preferences.getInstance().preferenceChanged.connect(self._onPreferencesChanged)

        self._active_material_container = None
        Application.getInstance().getMachineManager().activeMaterialChanged.connect(self._onActiveMaterialChanged)
        self._onActiveMaterialChanged()

        self._material_amounts = []