diff --git a/cura/Settings/ExtruderManager.py b/cura/Settings/ExtruderManager.py index 81579f74d0..e57ff3115e 100644 --- a/cura/Settings/ExtruderManager.py +++ b/cura/Settings/ExtruderManager.py @@ -96,8 +96,9 @@ class ExtruderManager(QObject): # \param index The index of the new active extruder. @pyqtSlot(int) def setActiveExtruderIndex(self, index): - self._active_extruder_index = index - self.activeExtruderChanged.emit() + if self._active_extruder_index != index: + self._active_extruder_index = index + self.activeExtruderChanged.emit() @pyqtProperty(int, notify = activeExtruderChanged) def activeExtruderIndex(self): diff --git a/docs/How_to_use_the_flame_graph_profiler.md b/docs/How_to_use_the_flame_graph_profiler.md new file mode 100644 index 0000000000..b1cdaf358e --- /dev/null +++ b/docs/How_to_use_the_flame_graph_profiler.md @@ -0,0 +1,37 @@ + +How to Profile Cura and See What It is Doing +============================================ +Cura has a simple flame graph profiler available as a plugin which can be used to see what Cura is doing as it runs and how much time it takes. A flame graph profile shows its output as a timeline and stacks of "blocks" which represent parts of the code and are stacked up to show call depth. These often form little peaks which look like flames. It is a simple yet powerful way to visualise the activity of a program. + + +Setting up and installing the profiler +-------------------------------------- + +The profiler plugin is kept outside of the Cura source code here: https://github.com/sedwards2009/cura-big-flame-graph + +To install it do: + +* Use `git clone https://github.com/sedwards2009/cura-big-flame-graph.git` to grab a copy of the code. +* Copy the `BigFlameGraph` directory into the `plugins` directory in your local Cura. +* Set the `URANIUM_FLAME_PROFILER` environment variable to something before starting Cura. This flags to the profiler code in Cura to activate and insert the needed hooks into the code. + + +Using the profiler +------------------ +To open the profiler go to the Extensions menu and select "Start BFG" from the "Big Flame Graph" menu. A page will open up in your default browser. This is the profiler UI. Click on "Record" to start recording, go to Cura and perform an action and then back in the profiler click on "Stop". The results should now load in. + +The time scale is at the top of the window. The blocks should be read as meaning the blocks at the bottom call the blocks which are stacked on top of them. Hover the mouse to get more detailed information about a block such as the name of the code involved and its duration. Use the zoom buttons or mouse wheel to zoom in. The display can be panned by dragging with the left mouse button. + +Note: The profiler front-end itself is quite "heavy" (ok, not optimised). It runs much better in Google Chrome or Chromium than Firefox. It is also a good idea to keep recording sessions short for the same reason. + + +What the Profiler Sees +---------------------- +The profiler doesn't capture every function call in Cura. It hooks into a number of important systems which give a good picture of activity without too much run time overhead. The most important system is Uranium's signal mechanism and PyQt5 slots. Functions which are called via the signal mechanism are recorded and thier names appear in the results. PyQt5 slots appear in the results with the prefix `[SLOT]`. + +Note that not all slots are captured. Only those slots which belong to classes which use the `pyqtSlot` decorator from the `UM.FlameProfiler` module. + + +Manually adding profiling code to more detail +--------------------------------------------- +It is also possible to manually add decorators to methods to make them appear in the profiler results. The `UM.FlameProfiler` module contains the `profile` decorator which can be applied to methods. There is also a `profileCall` context manager which can be used with Python's `with` statement to measure a block of code. `profileCall` takes one argument, a label to use in the results. diff --git a/plugins/3MFReader/WorkspaceDialog.py b/plugins/3MFReader/WorkspaceDialog.py index 8e16404b0a..1bae9575f2 100644 --- a/plugins/3MFReader/WorkspaceDialog.py +++ b/plugins/3MFReader/WorkspaceDialog.py @@ -70,8 +70,9 @@ class WorkspaceDialog(QObject): return self._variant_type def setVariantType(self, variant_type): - self._variant_type = variant_type - self.variantTypeChanged.emit() + if self._variant_type != variant_type: + self._variant_type = variant_type + self.variantTypeChanged.emit() @pyqtProperty(str, notify=machineTypeChanged) def machineType(self): @@ -82,8 +83,9 @@ class WorkspaceDialog(QObject): self.machineTypeChanged.emit() def setNumUserSettings(self, num_user_settings): - self._num_user_settings = num_user_settings - self.numVisibleSettingsChanged.emit() + if self._num_user_settings != num_user_settings: + self._num_user_settings = num_user_settings + self.numVisibleSettingsChanged.emit() @pyqtProperty(int, notify=numUserSettingsChanged) def numUserSettings(self): @@ -94,40 +96,45 @@ class WorkspaceDialog(QObject): return self._objects_on_plate def setHasObjectsOnPlate(self, objects_on_plate): - self._objects_on_plate = objects_on_plate - self.objectsOnPlateChanged.emit() + if self._objects_on_plate != objects_on_plate: + self._objects_on_plate = objects_on_plate + self.objectsOnPlateChanged.emit() @pyqtProperty("QVariantList", notify = materialLabelsChanged) def materialLabels(self): return self._material_labels def setMaterialLabels(self, material_labels): - self._material_labels = material_labels - self.materialLabelsChanged.emit() + if self._material_labels != material_labels: + self._material_labels = material_labels + self.materialLabelsChanged.emit() @pyqtProperty("QVariantList", notify=extrudersChanged) def extruders(self): return self._extruders def setExtruders(self, extruders): - self._extruders = extruders - self.extrudersChanged.emit() + if self._extruders != extruders: + self._extruders = extruders + self.extrudersChanged.emit() @pyqtProperty(str, notify = machineNameChanged) def machineName(self): return self._machine_name def setMachineName(self, machine_name): - self._machine_name = machine_name - self.machineNameChanged.emit() + if self._machine_name != machine_name: + self._machine_name = machine_name + self.machineNameChanged.emit() @pyqtProperty(str, notify=qualityTypeChanged) def qualityType(self): return self._quality_type def setQualityType(self, quality_type): - self._quality_type = quality_type - self.qualityTypeChanged.emit() + if self._quality_type != quality_type: + self._quality_type = quality_type + self.qualityTypeChanged.emit() @pyqtProperty(int, notify=numSettingsOverridenByQualityChangesChanged) def numSettingsOverridenByQualityChanges(self): @@ -142,8 +149,9 @@ class WorkspaceDialog(QObject): return self._quality_name def setQualityName(self, quality_name): - self._quality_name = quality_name - self.qualityNameChanged.emit() + if self._quality_name != quality_name: + self._quality_name = quality_name + self.qualityNameChanged.emit() @pyqtProperty(str, notify=activeModeChanged) def activeMode(self): @@ -165,8 +173,9 @@ class WorkspaceDialog(QObject): return self._num_visible_settings def setNumVisibleSettings(self, num_visible_settings): - self._num_visible_settings = num_visible_settings - self.numVisibleSettingsChanged.emit() + if self._num_visible_settings != num_visible_settings: + self._num_visible_settings = num_visible_settings + self.numVisibleSettingsChanged.emit() @pyqtProperty(bool, notify = machineConflictChanged) def machineConflict(self): @@ -191,16 +200,19 @@ class WorkspaceDialog(QObject): Application.getInstance().getBackend().close() def setMaterialConflict(self, material_conflict): - self._has_material_conflict = material_conflict - self.materialConflictChanged.emit() + if self._has_material_conflict != material_conflict: + self._has_material_conflict = material_conflict + self.materialConflictChanged.emit() def setMachineConflict(self, machine_conflict): - self._has_machine_conflict = machine_conflict - self.machineConflictChanged.emit() + if self._has_machine_conflict != machine_conflict: + self._has_machine_conflict = machine_conflict + self.machineConflictChanged.emit() def setQualityChangesConflict(self, quality_changes_conflict): - self._has_quality_changes_conflict = quality_changes_conflict - self.qualityChangesConflictChanged.emit() + if self._has_quality_changes_conflict != quality_changes_conflict: + self._has_quality_changes_conflict = quality_changes_conflict + self.qualityChangesConflictChanged.emit() def getResult(self): if "machine" in self._result and not self._has_machine_conflict: diff --git a/plugins/ChangeLogPlugin/ChangeLog.txt b/plugins/ChangeLogPlugin/ChangeLog.txt index c98f0a7c6b..265a076d25 100644 --- a/plugins/ChangeLogPlugin/ChangeLog.txt +++ b/plugins/ChangeLogPlugin/ChangeLog.txt @@ -98,6 +98,9 @@ Use a mesh to specify a volume within which to classify nothing as overhang for *Delta printer support This release adds support for printers with elliptic buildplates. This feature has not been extensively tested so please let us know if it works or get involved in improving it. +*AppImage for Linux +The Linux distribution is now in AppImage format, which makes Cura easier to install. + *bugfixes The user is now notified when a new version of Cura is available. When searching in the setting visibility preferences, the category for each setting is always displayed. diff --git a/plugins/ImageReader/ConfigUI.qml b/plugins/ImageReader/ConfigUI.qml index 1df57d74c0..893f8e248c 100644 --- a/plugins/ImageReader/ConfigUI.qml +++ b/plugins/ImageReader/ConfigUI.qml @@ -12,11 +12,9 @@ UM.Dialog { width: 350 * Screen.devicePixelRatio; minimumWidth: 350 * Screen.devicePixelRatio; - maximumWidth: 350 * Screen.devicePixelRatio; height: 250 * Screen.devicePixelRatio; minimumHeight: 250 * Screen.devicePixelRatio; - maximumHeight: 250 * Screen.devicePixelRatio; title: catalog.i18nc("@title:window", "Convert Image...") diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json index 6dd30800be..a85a50b37c 100644 --- a/resources/definitions/fdmprinter.def.json +++ b/resources/definitions/fdmprinter.def.json @@ -1239,6 +1239,16 @@ "default_value": true, "enabled": "infill_sparse_density > 0", "settable_per_mesh": true + }, + "min_infill_area": + { + "label": "Minimum Infill Area", + "description": "Don't generate areas of infill smaller than this (use skin instead).", + "unit": "mm²", + "type": "float", + "minimum_value": "0", + "default_value": 0, + "settable_per_mesh": true } } }, diff --git a/resources/definitions/kossel_pro.def.json b/resources/definitions/kossel_pro.def.json index a0774b3a06..e8403a1727 100644 --- a/resources/definitions/kossel_pro.def.json +++ b/resources/definitions/kossel_pro.def.json @@ -45,7 +45,7 @@ "default_value": "RepRap (Marlin/Sprinter)" }, "machine_start_gcode": { - "default_value": "; info: M303 E0 S200 C8 ; Pid auto-tune \n\nM140 S{{material_bed_temperature}}; Start heating up the base\nG28 ; Home to top 3 endstops\n; Autolevel and adjust first layer\n; Adjust this value to fit your own printer! (positive is thicker)\n; This default value is intentionally very high to accommodate the\n; variety of print heads used with this printer. Many of you will\n; need tiny values like Z0 or Z0.1. Use feeler gauges to dial this\n; in as accurately as possible.\nG29 Z10\n\n; Squirt and wipe ;\nM109 S220 ; Wait for the temp to hit 220\nG00 X125 Y-60 Z0.1 ;\nG92 E0 ;\nG01 E25 F100 ; Extrude a little bit to replace oozage from auto levelling\nG01 X90 Y-50 F6000 ;\nG01 Z5 ;\n\n; Set the extruder to the requested print temperature\nM104 S{{material_print_temperature}}\n" + "default_value": "; info: M303 E0 S200 C8 ; Pid auto-tune \n\nM140 S{material_bed_temperature}; Start heating up the base\nG28 ; Home to top 3 endstops\n; Autolevel and adjust first layer\n; Adjust this value to fit your own printer! (positive is thicker)\n; This default value is intentionally very high to accommodate the\n; variety of print heads used with this printer. Many of you will\n; need tiny values like Z0 or Z0.1. Use feeler gauges to dial this\n; in as accurately as possible.\nG29 Z10\n\n; Squirt and wipe ;\nM109 S220 ; Wait for the temp to hit 220\nG00 X125 Y-60 Z0.1 ;\nG92 E0 ;\nG01 E25 F100 ; Extrude a little bit to replace oozage from auto levelling\nG01 X90 Y-50 F6000 ;\nG01 Z5 ;\n\n; Set the extruder to the requested print temperature\nM104 S{material_print_temperature}\n" }, "machine_end_gcode": { "default_value": "M104 S0 ; turn off temperature\nM140 S0 ; turn off bed\nG28 ; home all axes\nM84 ; disable motors\n" @@ -54,4 +54,4 @@ "default_value": "elliptic" } } -} \ No newline at end of file +} diff --git a/resources/qml/WorkspaceSummaryDialog.qml b/resources/qml/WorkspaceSummaryDialog.qml index 2072ddb1e7..4c0a7bf86d 100644 --- a/resources/qml/WorkspaceSummaryDialog.qml +++ b/resources/qml/WorkspaceSummaryDialog.qml @@ -15,11 +15,10 @@ UM.Dialog width: 550 minimumWidth: 550 - maximumWidth: 550 height: 350 minimumHeight: 350 - maximumHeight: 350 + property int spacerHeight: 10 property bool dontShowAgain: true