From 65781eee847751ac072d0e90b9d1d11f305da5ba Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Wed, 27 May 2015 15:13:36 +0200 Subject: [PATCH 001/117] Set version to 15.05.92 --- cura/CuraApplication.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 1b608dd222..62318fb200 100644 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -48,7 +48,7 @@ class CuraApplication(QtApplication): if not hasattr(sys, "frozen"): Resources.addResourcePath(os.path.join(os.path.abspath(os.path.dirname(__file__)), "..")) - super().__init__(name = "cura", version = "15.05.90") + super().__init__(name = "cura", version = "15.05.92") self.setRequiredPlugins([ "CuraEngineBackend", From 68500ed1d0a36f1f2c7f9582f740daeef5d13b08 Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Wed, 27 May 2015 15:37:32 +0200 Subject: [PATCH 002/117] Position the setting tooltip to the left of the settings instead of below This makes the tooltip not cover the settings. Fixes Asana issue 39 --- resources/qml/Sidebar.qml | 4 ++-- resources/qml/SidebarTooltip.qml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/resources/qml/Sidebar.qml b/resources/qml/Sidebar.qml index 0d4d0df599..e803406369 100644 --- a/resources/qml/Sidebar.qml +++ b/resources/qml/Sidebar.qml @@ -16,10 +16,10 @@ UM.AngledCornerRectangle { property alias saveAction: saveButton.saveAction; cornerSize: UM.Theme.sizes.default_margin.width; - + function showTooltip(item, position, text) { tooltip.text = text; - position = item.mapToItem(base, position.x, position.y); + position = item.mapToItem(base, position.x, position.y / 2); tooltip.show(position); } diff --git a/resources/qml/SidebarTooltip.qml b/resources/qml/SidebarTooltip.qml index 9003faf995..6e0ba74404 100644 --- a/resources/qml/SidebarTooltip.qml +++ b/resources/qml/SidebarTooltip.qml @@ -22,10 +22,10 @@ Rectangle { function show(position) { if(position.y + base.height > parent.height) { - x = position.x; + x = position.x - base.width; y = parent.height - base.height; } else { - x = position.x; + x = position.x - base.width; y = position.y; } base.opacity = 1; From 86920f450725968eab31443c0338bec65852f30a Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Wed, 27 May 2015 16:24:13 +0200 Subject: [PATCH 003/117] Start adding some more proper error/warning values to settings Contributes to #7 --- resources/settings/fdmprinter.json | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/resources/settings/fdmprinter.json b/resources/settings/fdmprinter.json index 657102c5d2..03ee5d8ee3 100644 --- a/resources/settings/fdmprinter.json +++ b/resources/settings/fdmprinter.json @@ -41,8 +41,9 @@ "unit": "mm", "type": "float", "default": 0.1, - "min_value": 0.06, - "max_value": 2.0, + "min_value": 0.00001, + "min_value_warning": 0.04, + "max_value_warning": 2.0, "always_visible": true, "children": { "layer_height_0": { @@ -51,8 +52,9 @@ "unit": "mm", "type": "float", "default": 0.3, - "min_value": 0.06, - "max_value": 2.0, + "min_value": 0.0, + "min_value_warning": 0.04, + "max_value_warning": 2.0, "visible": false } } From 1471a6815fd9a37b57b87542e44fc4abac9788b1 Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Wed, 27 May 2015 16:25:39 +0200 Subject: [PATCH 004/117] Do not slice if there is any setting with an error value Fixes #7 --- plugins/CuraEngineBackend/CuraEngineBackend.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/plugins/CuraEngineBackend/CuraEngineBackend.py b/plugins/CuraEngineBackend/CuraEngineBackend.py index 7c52a3c6cb..45a2148892 100644 --- a/plugins/CuraEngineBackend/CuraEngineBackend.py +++ b/plugins/CuraEngineBackend/CuraEngineBackend.py @@ -110,6 +110,9 @@ class CuraEngineBackend(Backend): if not objects: return #No point in slicing an empty build plate + if kwargs.get("settings", self._settings).hasErrorValue(): + return #No slicing if we have error values since those are by definition illegal values. + self._slicing = True self.slicingStarted.emit() From 0885d1433d62c5f7f46ffdbbede9e5e68a97163f Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Wed, 27 May 2015 16:26:40 +0200 Subject: [PATCH 005/117] Move Sidebar so it is on top of everything else This fixes an issue where the tooltip would be displayed below the layer view slider. Additional fix for Asana issue 39 --- resources/qml/Cura.qml | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index 7e48765f17..a2eeba59cd 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -144,23 +144,6 @@ UM.MainWindow { } } - Sidebar { - id: sidebar; - - anchors { - top: parent.top; - bottom: parent.bottom; - right: parent.right; - rightMargin: UM.Theme.sizes.window_margin.width; - } - - width: UM.Theme.sizes.panel.width; - - addMachineAction: actions.addMachine; - configureMachinesAction: actions.configureMachines; - saveAction: actions.save; - } - UM.MessageStack { anchors { left: toolbar.right; @@ -265,6 +248,23 @@ UM.MainWindow { bottomMargin: UM.Theme.sizes.window_margin.height; } } + + Sidebar { + id: sidebar; + + anchors { + top: parent.top; + bottom: parent.bottom; + right: parent.right; + rightMargin: UM.Theme.sizes.window_margin.width; + } + + width: UM.Theme.sizes.panel.width; + + addMachineAction: actions.addMachine; + configureMachinesAction: actions.configureMachines; + saveAction: actions.save; + } } } From 830dc45cceec50c1a592360e43f3e3a3528492b6 Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Wed, 27 May 2015 16:30:34 +0200 Subject: [PATCH 006/117] Make cura_app executable Contributes to Ultimaker/Uranium#42 --- cura_app.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 cura_app.py diff --git a/cura_app.py b/cura_app.py old mode 100644 new mode 100755 From 98a527b015e9e98d197fd45af5efef88f4c43394 Mon Sep 17 00:00:00 2001 From: Adam Goldsmith Date: Sun, 31 May 2015 07:35:33 -0400 Subject: [PATCH 007/117] add setting for sidebar background color --- resources/qml/Sidebar.qml | 2 ++ resources/themes/cura/theme.json | 2 ++ 2 files changed, 4 insertions(+) diff --git a/resources/qml/Sidebar.qml b/resources/qml/Sidebar.qml index e803406369..3b641a4538 100644 --- a/resources/qml/Sidebar.qml +++ b/resources/qml/Sidebar.qml @@ -17,6 +17,8 @@ UM.AngledCornerRectangle { cornerSize: UM.Theme.sizes.default_margin.width; + color: UM.Theme.colors.sidebar; + function showTooltip(item, position, text) { tooltip.text = text; position = item.mapToItem(base, position.x, position.y / 2); diff --git a/resources/themes/cura/theme.json b/resources/themes/cura/theme.json index 2ea7f138f6..f73360d439 100644 --- a/resources/themes/cura/theme.json +++ b/resources/themes/cura/theme.json @@ -44,6 +44,8 @@ }, "colors": { + "sidebar": [255, 255, 255, 255], + "primary": [12, 169, 227, 255], "primary_hover": [34, 150, 190, 255], "primary_text": [255, 255, 255, 255], From cc8f974793f72ba521619e520eae1656135434c7 Mon Sep 17 00:00:00 2001 From: Adam Goldsmith Date: Sun, 31 May 2015 09:18:36 -0400 Subject: [PATCH 008/117] add missing color to tool_button --- resources/themes/cura/styles.qml | 1 + 1 file changed, 1 insertion(+) diff --git a/resources/themes/cura/styles.qml b/resources/themes/cura/styles.qml index d17560d976..bdf332871c 100644 --- a/resources/themes/cura/styles.qml +++ b/resources/themes/cura/styles.qml @@ -73,6 +73,7 @@ QtObject { anchors.horizontalCenter: parent.horizontalCenter; text: control.text; font: UM.Theme.fonts.button_tooltip; + color: UM.Theme.colors.button_text; } } } From 350e3312bdf3cc638ff1490749b7400769ee202d Mon Sep 17 00:00:00 2001 From: Adam Goldsmith Date: Sun, 31 May 2015 09:24:41 -0400 Subject: [PATCH 009/117] add missing width and height to section icon --- resources/themes/cura/styles.qml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/resources/themes/cura/styles.qml b/resources/themes/cura/styles.qml index bdf332871c..9e9cc11e42 100644 --- a/resources/themes/cura/styles.qml +++ b/resources/themes/cura/styles.qml @@ -122,6 +122,8 @@ QtObject { Image { anchors.verticalCenter: parent.verticalCenter; source: control.iconSource; + width: UM.Theme.sizes.section_icon.width; + height: UM.Theme.sizes.section_icon.height; } Label { From 7fda5c84e70a414705d1b3c632a10fdf977d9948 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Tue, 2 Jun 2015 15:35:15 +0200 Subject: [PATCH 010/117] Added extra exception catching for thread joining --- plugins/USBPrinting/PrinterConnection.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/plugins/USBPrinting/PrinterConnection.py b/plugins/USBPrinting/PrinterConnection.py index ad231f5207..3a953c3744 100644 --- a/plugins/USBPrinting/PrinterConnection.py +++ b/plugins/USBPrinting/PrinterConnection.py @@ -241,7 +241,11 @@ class PrinterConnection(SignalEmitter): ## Close the printer connection def close(self): if self._connect_thread.isAlive(): - self._connect_thread.join() + try: + self._connect_thread.join() + except Exception as e: + pass # This should work, but it does fail sometimes for some reason + if self._serial is not None: self.setIsConnected(False) self._listen_thread.join() From 6330be5f817d4e40a29ea15bb46284465c2a7e23 Mon Sep 17 00:00:00 2001 From: Tim Kuipers Date: Tue, 2 Jun 2015 17:08:13 +0200 Subject: [PATCH 011/117] updated settings --- resources/settings/fdmprinter.json | 90 ++++++++++++++++-------------- 1 file changed, 49 insertions(+), 41 deletions(-) diff --git a/resources/settings/fdmprinter.json b/resources/settings/fdmprinter.json index 657102c5d2..f6a4fa1cb6 100644 --- a/resources/settings/fdmprinter.json +++ b/resources/settings/fdmprinter.json @@ -149,6 +149,13 @@ } } }, + "alternate_extra_perimeter": { + "label": "Alternate Extra Wall", + "description": "Make an extra wall at every second layer, so that infill will be caught between an extra wall above and one below. This results in a better cohesion between infill and walls, but might have an impact on the surface quality.", + "type": "boolean", + "default": false, + "visible": false + }, "top_bottom_thickness": { "label": "Bottom/Top Thickness", "description": "This controls the thickness of the bottom and top layers, the amount of solid layers put down is calculated by the layer thickness and this value. Having this value a multiple of the layer thickness makes sense. And keep it near your wall thickness to make an evenly strong part.", @@ -174,7 +181,7 @@ "top_layers": { "label": "Top Layers", "description": "This controls the amount of top layers.", - "default": 4, + "default": 8, "type": "int", "visible": false, "inherit_function": "math.ceil(parent_value / layer_height)" @@ -193,7 +200,7 @@ "bottom_layers": { "label": "Bottom Layers", "description": "This controls the amount of bottom layers.", - "default": 4, + "default": 8, "type": "int", "visible": false, "inherit_function": "math.ceil(parent_value / layer_height)" @@ -211,6 +218,22 @@ "default": true, "visible": false }, + "fill_perimeter_gaps":{ + "label": "Fill Gaps Between Walls", + "description": "Fill the gaps created by walls where they would otherwise be overlapping. This will also fill thin walls. Optionally only the gaps occurring within the top and bottom skin can be filled.", + "type": "enum", + "options": [ + "Nowhere", + "Everywhere", + "Skin" + ], + "default": "Everywhere", + "visible": false, + "active_if": { + "setting": "wall_overlap_avoid_enabled", + "value": true + } + }, "top_bottom_pattern": { "label": "Bottom/Top Pattern", "description": "Pattern of the top/bottom solid fill. This normally is done with lines to get the best possible finish, but in some cases a concentric fill gives a nicer end result.", @@ -346,15 +369,23 @@ "visible": false, "inherit": false }, - "retraction_minimal_extrusion": { - "label": "Minimal Extrusion Before Retraction", - "description": "The minimum amount of extrusion that needs to happen between retractions. If a retraction should happen before this minimum is reached, it will be ignored. This avoids retracting repeatedly on the same piece of filament as that can flatten the filament and cause grinding issues.", - "unit": "mm", - "type": "float", - "default": 0.02, + "retraction_count_max": { + "label": "Maximal Retraction Count", + "description": "This settings limits the number of retractions occuring within the Minimal Extrusion Distance Window. Further retractions within this window will be ignored. This avoids retracting repeatedly on the same piece of filament as that can flatten the filament and cause grinding issues.", + "default": 6, + "type": "int", "visible": false, "inherit": false }, + "retraction_extrusion_window": { + "label": "Minimal Extrusion Distance Window", + "description": "The window in which the Maximal Retraction Count is enforced. This window should be approximately the size of the Retraction distance, so that effectively the number of times a retraction passes the same patch of material is limited.", + "unit": "mm", + "type": "float", + "default": 4.5, + "visible": false, + "inherit_function": "retraction_amount" + }, "retraction_hop": { "label": "Z Hop when Retracting", "description": "Whenever a retraction is done, the head is lifted by this amount to travel over the print. A value of 0.075 works well. This feature has a lot of positive effect on delta towers.", @@ -647,12 +678,12 @@ "description": "Where to place support structures. The placement can be restricted such that the support structures won't rest on the model, which could otherwise cause scarring.", "type": "enum", "options": [ - "None", "Touching Buildplate", "Everywhere" ], - "default": "Touching Buildplate", + "default": "Everywhere", "visible": true, + "inherit_function": "'Everywhere' if support_enable else 'None'", "active_if": { "setting": "support_enable", "value": true @@ -700,11 +731,7 @@ "unit": "mm", "default": 0.15, "type": "float", - "visible": false, - "active_if": { - "setting": "support_enable", - "value": true - } + "visible": false }, "support_bottom_distance": { "label": "Bottom Distance", @@ -712,11 +739,7 @@ "unit": "mm", "default": 0.15, "type": "float", - "visible": false, - "active_if": { - "setting": "support_enable", - "value": true - } + "visible": false } } }, @@ -737,12 +760,8 @@ "description": "The maximum distance between support blocks, in the X/Y directions, such that the blocks will merge into a single block.", "unit": "mm", "type": "float", - "default": 0.7, - "visible": false, - "active_if": { - "setting": "support_enable", - "value": true - } + "default": 2.0, + "visible": false }, "support_area_smoothing": { "label": "Area Smoothing", @@ -750,22 +769,14 @@ "unit": "mm", "type": "float", "default": 0.6, - "visible": false, - "active_if": { - "setting": "support_enable", - "value": true - } + "visible": false }, "support_use_towers": { "label": "Use towers.", "description": "Use specialized towers to support tiny overhang areas. These towers have a larger diameter than the region they support. Near the overhang the towers' diameter decreases, forming a roof.", "type": "boolean", "default": true, - "visible": true, - "active_if": { - "setting": "support_enable", - "value": true - } + "visible": true }, "support_minimal_diameter": { "label": "Minimal Diameter", @@ -812,7 +823,7 @@ "Lines", "ZigZag" ], - "default": "Lines", + "default": "ZigZag", "visible": true, "active_if": { "setting": "support_enable", @@ -841,7 +852,6 @@ "setting": "support_enable", "value": true }, - "children": { "support_line_distance": { "label": "Line distance", @@ -1314,6 +1324,4 @@ } } } -} - - +} \ No newline at end of file From 5ee82c8de51ae7866a48f33be03060142e083001 Mon Sep 17 00:00:00 2001 From: Tim Kuipers Date: Wed, 3 Jun 2015 12:42:03 +0200 Subject: [PATCH 012/117] settings restructure --- resources/settings/fdmprinter.json | 644 ++-- .../themes/cura/icons/category_blackmagic.svg | 149 + ..._quality.svg => category_layer_height.svg} | 0 .../themes/cura/icons/category_shell.svg | 3384 ++++++++++++++++ .../themes/cura/icons/category_travel.svg | 3422 +++++++++++++++++ 5 files changed, 7301 insertions(+), 298 deletions(-) create mode 100644 resources/themes/cura/icons/category_blackmagic.svg rename resources/themes/cura/icons/{category_quality.svg => category_layer_height.svg} (100%) create mode 100644 resources/themes/cura/icons/category_shell.svg create mode 100644 resources/themes/cura/icons/category_travel.svg diff --git a/resources/settings/fdmprinter.json b/resources/settings/fdmprinter.json index f6a4fa1cb6..8bd5446a91 100644 --- a/resources/settings/fdmprinter.json +++ b/resources/settings/fdmprinter.json @@ -30,10 +30,10 @@ "machine_nozzle_expansion_angle": { "default": 45 } }, "categories": { - "resolution": { - "label": "Quality", + "layer_height": { + "label": "Layer Height", "visible": true, - "icon": "category_quality", + "icon": "category_layer_height", "settings": { "layer_height": { "label": "Layer Height", @@ -46,8 +46,8 @@ "always_visible": true, "children": { "layer_height_0": { - "label": "Initial Layer Thickness", - "description": "The layer thickness of the bottom layer. A thicker bottom layer makes sticking to the bed easier.", + "label": "Initial Layer Height", + "description": "The layer height of the bottom layer. A thicker bottom layer makes for better bed adhesion.", "unit": "mm", "type": "float", "default": 0.3, @@ -56,7 +56,14 @@ "visible": false } } - }, + } + } + }, + "shell": { + "label": "Shell", + "visible": true, + "icon": "category_shell", + "settings": { "shell_thickness": { "label": "Shell Thickness", "description": "The thickness of the outside shell in the horizontal and vertical direction. This is used in combination with the nozzle size to define the number of perimeter lines and the thickness of those perimeter lines. This is also used to define the number of solid top and bottom layers.", @@ -266,7 +273,73 @@ } } }, + "infill": { + "label": "Infill", + "visible": true, + "icon": "category_infill", + "settings": { + "fill_sparse_density": { + "label": "Infill Density", + "description": "This controls how densely filled the insides of your print will be. For a solid part use 100%, for an hollow part use 0%. A value around 20% is usually enough. This won't affect the outside of the print and only adjusts how strong the part becomes.", + "unit": "%", + "type": "float", + "default": 20.0, + "children": { + "fill_pattern": { + "label": "Infill Pattern", + "description": "Cura defaults to switching between grid and line infill. But with this setting visible you can control this yourself. The line infill swaps direction on alternate layers of infill, while the grid prints the full cross-hatching on each layer of infill.", + "type": "enum", + "visible": false, + "options": [ + "Grid", + "Lines", + "Concentric", + "ZigZag" + ], + "default": "Grid", + "inherit_function": "'Lines' if parent_value > 25 else 'Grid'" + }, + "infill_line_distance": { + "label": "Line distance", + "description": "Distance between the printed infill lines.", + "unit": "mm", + "type": "float", + "default": 2.0, + "visible": false, + "inherit_function": "(infill_line_width * 100) / parent_value" + } + } + }, + "fill_overlap": { + "label": "Infill Overlap", + "description": "The amount of overlap between the infill and the walls. A slight overlap allows the walls to connect firmly to the infill.", + "unit": "%", + "type": "float", + "default": 15.0, + "visible": false + }, + "fill_sparse_thickness": { + "label": "Infill Thickness", + "description": "The thickness of the sparse infill. This is rounded to a multiple of the layerheight and used to print the sparse-infill in fewer, thicker layers to save printing time.", + "unit": "mm", + "type": "float", + "default": 0.1, + "visible": false, + + "children": { + "fill_sparse_combine": { + "label": "Infill Layers", + "description": "Amount of layers that are combined together to form sparse infill.", + "type": "int", + "default": 1, + "visible": false, + "inherit_function": "math.floor((parent_value + 0.001) / layer_height)" + } + } + } + } + }, "material": { "label": "Material", "visible": true, @@ -307,95 +380,6 @@ "type": "float", "min_value": 5.0, "max_value": 300.0 - }, - "retraction_enable": { - "label": "Enable Retraction", - "description": "Retract the filament when the nozzle is moving over a non-printed area. Details about the retraction can be configured in the advanced tab.", - "type": "boolean", - "default": true, - - "children": { - "retraction_speed": { - "label": "Retraction Speed", - "description": "The speed at which the filament is retracted. A higher retraction speed works better, but a very high retraction speed can lead to filament grinding.", - "unit": "mm/s", - "type": "float", - "default": 25.0, - "visible": false, - "inherit": false, - - "children": { - "retraction_retract_speed": { - "label": "Retraction Retract Speed", - "description": "The speed at which the filament is retracted. A higher retraction speed works better, but a very high retraction speed can lead to filament grinding.", - "unit": "mm/s", - "type": "float", - "default": 25.0, - "visible": false - }, - "retraction_prime_speed": { - "label": "Retraction Prime Speed", - "description": "The speed at which the filament is pushed back after retraction.", - "unit": "mm/s", - "type": "float", - "default": 25.0, - "visible": false - } - } - }, - "retraction_amount": { - "label": "Retraction Distance", - "description": "The amount of retraction: Set at 0 for no retraction at all. A value of 4.5mm seems to generate good results for 3mm filament in Bowden-tube fed printers.", - "unit": "mm", - "type": "float", - "default": 4.5, - "visible": false, - "inherit": false - }, - "retraction_min_travel": { - "label": "Retraction Minimum Travel", - "description": "The minimum distance of travel needed for a retraction to happen at all. This helps ensure you do not get a lot of retractions in a small area.", - "unit": "mm", - "type": "float", - "default": 4.5, - "visible": false, - "inherit": false - }, - "retraction_combing": { - "label": "Enable Combing", - "description": "Combing keeps the head within the interior of the print whenever possible when traveling from one part of the print to another, and does not use retraction. If combing is disabled the printer head moves straight from the start point to the end point and it will always retract.", - "type": "boolean", - "default": true, - "visible": false, - "inherit": false - }, - "retraction_count_max": { - "label": "Maximal Retraction Count", - "description": "This settings limits the number of retractions occuring within the Minimal Extrusion Distance Window. Further retractions within this window will be ignored. This avoids retracting repeatedly on the same piece of filament as that can flatten the filament and cause grinding issues.", - "default": 6, - "type": "int", - "visible": false, - "inherit": false - }, - "retraction_extrusion_window": { - "label": "Minimal Extrusion Distance Window", - "description": "The window in which the Maximal Retraction Count is enforced. This window should be approximately the size of the Retraction distance, so that effectively the number of times a retraction passes the same patch of material is limited.", - "unit": "mm", - "type": "float", - "default": 4.5, - "visible": false, - "inherit_function": "retraction_amount" - }, - "retraction_hop": { - "label": "Z Hop when Retracting", - "description": "Whenever a retraction is done, the head is lifted by this amount to travel over the print. A value of 0.075 works well. This feature has a lot of positive effect on delta towers.", - "unit": "mm", - "type": "float", - "default": 0.0, - "visible": false, - "inherit": false - } - } } } }, @@ -501,68 +485,97 @@ } } }, - "infill": { - "label": "Infill", + "travel": { + "label": "Travel", "visible": true, - "icon": "category_infill", + "icon": "category_travel", "settings": { - "fill_sparse_density": { - "label": "Infill Density", - "description": "This controls how densely filled the insides of your print will be. For a solid part use 100%, for an hollow part use 0%. A value around 20% is usually enough. This won't affect the outside of the print and only adjusts how strong the part becomes.", - "unit": "%", - "type": "float", - "default": 20.0, + "retraction_enable": { + "label": "Enable Retraction", + "description": "Retract the filament when the nozzle is moving over a non-printed area. Details about the retraction can be configured in the advanced tab.", + "type": "boolean", + "default": true, "children": { - "fill_pattern": { - "label": "Infill Pattern", - "description": "Cura defaults to switching between grid and line infill. But with this setting visible you can control this yourself. The line infill swaps direction on alternate layers of infill, while the grid prints the full cross-hatching on each layer of infill.", - "type": "enum", + "retraction_speed": { + "label": "Retraction Speed", + "description": "The speed at which the filament is retracted. A higher retraction speed works better, but a very high retraction speed can lead to filament grinding.", + "unit": "mm/s", + "type": "float", + "default": 25.0, "visible": false, - "options": [ - "Grid", - "Lines", - "Concentric", - "ZigZag" - ], - "default": "Grid", - "inherit_function": "'Lines' if parent_value > 25 else 'Grid'" + "inherit": false, + + "children": { + "retraction_retract_speed": { + "label": "Retraction Retract Speed", + "description": "The speed at which the filament is retracted. A higher retraction speed works better, but a very high retraction speed can lead to filament grinding.", + "unit": "mm/s", + "type": "float", + "default": 25.0, + "visible": false + }, + "retraction_prime_speed": { + "label": "Retraction Prime Speed", + "description": "The speed at which the filament is pushed back after retraction.", + "unit": "mm/s", + "type": "float", + "default": 25.0, + "visible": false + } + } }, - "infill_line_distance": { - "label": "Line distance", - "description": "Distance between the printed infill lines.", + "retraction_amount": { + "label": "Retraction Distance", + "description": "The amount of retraction: Set at 0 for no retraction at all. A value of 4.5mm seems to generate good results for 3mm filament in Bowden-tube fed printers.", "unit": "mm", "type": "float", - "default": 2.0, + "default": 4.5, "visible": false, - "inherit_function": "(infill_line_width * 100) / parent_value" - } - } - }, - "fill_overlap": { - "label": "Infill Overlap", - "description": "The amount of overlap between the infill and the walls. A slight overlap allows the walls to connect firmly to the infill.", - "unit": "%", - "type": "float", - "default": 15.0, - "visible": false - }, - "fill_sparse_thickness": { - "label": "Infill Thickness", - "description": "The thickness of the sparse infill. This is rounded to a multiple of the layerheight and used to print the sparse-infill in fewer, thicker layers to save printing time.", - "unit": "mm", - "type": "float", - "default": 0.1, - "visible": false, - - "children": { - "fill_sparse_combine": { - "label": "Infill Layers", - "description": "Amount of layers that are combined together to form sparse infill.", + "inherit": false + }, + "retraction_min_travel": { + "label": "Retraction Minimum Travel", + "description": "The minimum distance of travel needed for a retraction to happen at all. This helps ensure you do not get a lot of retractions in a small area.", + "unit": "mm", + "type": "float", + "default": 4.5, + "visible": false, + "inherit": false + }, + "retraction_combing": { + "label": "Enable Combing", + "description": "Combing keeps the head within the interior of the print whenever possible when traveling from one part of the print to another, and does not use retraction. If combing is disabled the printer head moves straight from the start point to the end point and it will always retract.", + "type": "boolean", + "default": true, + "visible": false, + "inherit": false + }, + "retraction_count_max": { + "label": "Maximal Retraction Count", + "description": "This settings limits the number of retractions occuring within the Minimal Extrusion Distance Window. Further retractions within this window will be ignored. This avoids retracting repeatedly on the same piece of filament as that can flatten the filament and cause grinding issues.", + "default": 6, "type": "int", - "default": 1, "visible": false, - "inherit_function": "math.floor((parent_value + 0.001) / layer_height)" + "inherit": false + }, + "retraction_extrusion_window": { + "label": "Minimal Extrusion Distance Window", + "description": "The window in which the Maximal Retraction Count is enforced. This window should be approximately the size of the Retraction distance, so that effectively the number of times a retraction passes the same patch of material is limited.", + "unit": "mm", + "type": "float", + "default": 4.5, + "visible": false, + "inherit_function": "retraction_amount" + }, + "retraction_hop": { + "label": "Z Hop when Retracting", + "description": "Whenever a retraction is done, the head is lifted by this amount to travel over the print. A value of 0.075 works well. This feature has a lot of positive effect on delta towers.", + "unit": "mm", + "type": "float", + "default": 0.0, + "visible": false, + "inherit": false } } } @@ -662,6 +675,164 @@ } } }, + "platform_adhesion": { + "label": "Platform Adhesion", + "visible": true, + "icon": "category_adhesion", + "settings": { + "adhesion_type": { + "label": "Type", + "description": "Different options that help in preventing corners from lifting due to warping. Brim adds a single-layer-thick flat area around your object which is easy to cut off afterwards, and it is the recommended option. Raft adds a thick grid below the object and a thin interface between this and your object. (Note that enabling the brim or raft disables the skirt.)", + "type": "enum", + "options": [ + "None", + "Brim", + "Raft" + ], + "default": "None" + }, + "skirt_line_count": { + "label": "Skirt Line Count", + "description": "The skirt is a line drawn around the first layer of the. This helps to prime your extruder, and to see if the object fits on your platform. Setting this to 0 will disable the skirt. Multiple skirt lines can help to prime your extruder better for small objects.", + "type": "int", + "default": 1, + "active_if": { + "setting": "adhesion_type", + "value": "None" + } + }, + "skirt_gap": { + "label": "Skirt Distance", + "description": "The horizontal distance between the skirt and the first layer of the print.\nThis is the minimum distance, multiple skirt lines will extend outwards from this distance.", + "unit": "mm", + "type": "float", + "default": 3.0, + "active_if": { + "setting": "adhesion_type", + "value": "None" + } + }, + "skirt_minimal_length": { + "label": "Skirt Minimum Length", + "description": "The minimum length of the skirt. If this minimum length is not reached, more skirt lines will be added to reach this minimum length. Note: If the line count is set to 0 this is ignored.", + "unit": "mm", + "type": "float", + "default": 250, + "active_if": { + "setting": "adhesion_type", + "value": "None" + } + }, + "brim_line_count": { + "label": "Brim Line Count", + "description": "The amount of lines used for a brim: More lines means a larger brim which sticks better, but this also makes your effective print area smaller.", + "type": "int", + "default": 10, + "active_if": { + "setting": "adhesion_type", + "value": "Brim" + } + }, + "raft_margin": { + "label": "Raft Extra Margin", + "description": "If the raft is enabled, this is the extra raft area around the object which is also given a raft. Increasing this margin will create a stronger raft while using more material and leaving less area for your print.", + "unit": "mm", + "type": "float", + "default": 5.0, + "active_if": { + "setting": "adhesion_type", + "value": "Raft" + } + }, + "raft_line_spacing": { + "label": "Raft Line Spacing", + "description": "The distance between the raft lines. The first 2 layers of the raft have this amount of spacing between the raft lines.", + "unit": "mm", + "type": "float", + "default": 5.0, + "active_if": { + "setting": "adhesion_type", + "value": "Raft" + } + }, + "raft_base_thickness": { + "label": "Raft Base Thickness", + "description": "Layer thickness of the first raft layer. This should be a thick layer which sticks firmly to the printer bed.", + "unit": "mm", + "type": "float", + "default": 0.3, + "active_if": { + "setting": "adhesion_type", + "value": "Raft" + } + }, + "raft_base_linewidth": { + "label": "Raft Base Line Width", + "description": "Width of the lines in the first raft layer. These should be thick lines to assist in bed adhesion.", + "unit": "mm", + "type": "float", + "default": 1.0, + "active_if": { + "setting": "adhesion_type", + "value": "Raft" + } + }, + "raft_base_speed": { + "label": "Raft Base Print Speed", + "description": "The speed at which the first raft layer is printed. This should be printed quite slowly, as the amount of material coming out of the nozzle is quite high.", + "unit": "mm/s", + "type": "float", + "default": 15.0, + "active_if": { + "setting": "adhesion_type", + "value": "Raft" + } + }, + "raft_interface_thickness": { + "label": "Raft Interface Thickness", + "description": "Thickness of the 2nd raft layer.", + "unit": "mm", + "type": "float", + "default": 0.27, + "active_if": { + "setting": "adhesion_type", + "value": "Raft" + } + }, + "raft_interface_linewidth": { + "label": "Raft Interface Line Width", + "description": "Width of the 2nd raft layer lines. These lines should be thinner than the first layer, but strong enough to attach the object to.", + "unit": "mm", + "type": "float", + "default": 0.4, + "active_if": { + "setting": "adhesion_type", + "value": "Raft" + } + }, + "raft_airgap": { + "label": "Raft Air-gap", + "description": "The gap between the final raft layer and the first layer of the object. Only the first layer is raised by this amount to lower the bonding between the raft layer and the object. Makes it easier to peel off the raft.", + "unit": "mm", + "type": "float", + "default": 0.22, + "active_if": { + "setting": "adhesion_type", + "value": "Raft" + } + }, + "raft_surface_layers": { + "label": "Raft Surface Layers", + "description": "The number of surface layers on top of the 2nd raft layer. These are fully filled layers that the object sits on. 2 layers usually works fine.", + "type": "int", + "default": 2, + "active_if": { + "setting": "adhesion_type", + "value": "Raft" + } + } + } + }, "support": { "label": "Support", "visible": true, @@ -870,168 +1041,45 @@ } } }, - "platform_adhesion": { - "label": "Platform Adhesion", + "meshfix": { + "label": "Fixes", "visible": true, - "icon": "category_adhesion", + "icon": "category_fixes", "settings": { - "adhesion_type": { - "label": "Type", - "description": "Different options that help in preventing corners from lifting due to warping. Brim adds a single-layer-thick flat area around your object which is easy to cut off afterwards, and it is the recommended option. Raft adds a thick grid below the object and a thin interface between this and your object. (Note that enabling the brim or raft disables the skirt.)", - "type": "enum", - "options": [ - "None", - "Brim", - "Raft" - ], - "default": "None" + "meshfix_union_all": { + "label": "Union Overlapping Volumes", + "description": "Ignore the internal geometry arising from overlapping volumes and print the volumes as one. This may cause internal cavaties to disappear.", + "type": "boolean", + "default": true, + "visible": false }, - "skirt_line_count": { - "label": "Skirt Line Count", - "description": "The skirt is a line drawn around the first layer of the. This helps to prime your extruder, and to see if the object fits on your platform. Setting this to 0 will disable the skirt. Multiple skirt lines can help to prime your extruder better for small objects.", - "type": "int", - "default": 1, - "active_if": { - "setting": "adhesion_type", - "value": "None" - } + "meshfix_union_all_remove_holes": { + "label": "Remove All Holes", + "description": "Remove the holes in each layer and keep only the outside shape. This will ignore any invisible internal geometry. However, it also ignores layer holes which can be viewed from above or below.", + "type": "boolean", + "default": false, + "visible": false }, - "skirt_gap": { - "label": "Skirt Distance", - "description": "The horizontal distance between the skirt and the first layer of the print.\nThis is the minimum distance, multiple skirt lines will extend outwards from this distance.", - "unit": "mm", - "type": "float", - "default": 3.0, - "active_if": { - "setting": "adhesion_type", - "value": "None" - } + "meshfix_extensive_stitching": { + "label": "Extensive Stitching", + "description": "Extensive stitching tries to stitch up open holes in the mesh by closing the hole with touching polygons. This option can introduce a lot of processing time.", + "type": "boolean", + "default": false, + "visible": false }, - "skirt_minimal_length": { - "label": "Skirt Minimum Length", - "description": "The minimum length of the skirt. If this minimum length is not reached, more skirt lines will be added to reach this minimum length. Note: If the line count is set to 0 this is ignored.", - "unit": "mm", - "type": "float", - "default": 250, - "active_if": { - "setting": "adhesion_type", - "value": "None" - } - }, - "brim_line_count": { - "label": "Brim Line Count", - "description": "The amount of lines used for a brim: More lines means a larger brim which sticks better, but this also makes your effective print area smaller.", - "type": "int", - "default": 10, - "active_if": { - "setting": "adhesion_type", - "value": "Brim" - } - }, - "raft_margin": { - "label": "Raft Extra Margin", - "description": "If the raft is enabled, this is the extra raft area around the object which is also given a raft. Increasing this margin will create a stronger raft while using more material and leaving less area for your print.", - "unit": "mm", - "type": "float", - "default": 5.0, - "active_if": { - "setting": "adhesion_type", - "value": "Raft" - } - }, - "raft_line_spacing": { - "label": "Raft Line Spacing", - "description": "The distance between the raft lines. The first 2 layers of the raft have this amount of spacing between the raft lines.", - "unit": "mm", - "type": "float", - "default": 5.0, - "active_if": { - "setting": "adhesion_type", - "value": "Raft" - } - }, - "raft_base_thickness": { - "label": "Raft Base Thickness", - "description": "Layer thickness of the first raft layer. This should be a thick layer which sticks firmly to the printer bed.", - "unit": "mm", - "type": "float", - "default": 0.3, - "active_if": { - "setting": "adhesion_type", - "value": "Raft" - } - }, - "raft_base_linewidth": { - "label": "Raft Base Line Width", - "description": "Width of the lines in the first raft layer. These should be thick lines to assist in bed adhesion.", - "unit": "mm", - "type": "float", - "default": 1.0, - "active_if": { - "setting": "adhesion_type", - "value": "Raft" - } - }, - "raft_base_speed": { - "label": "Raft Base Print Speed", - "description": "The speed at which the first raft layer is printed. This should be printed quite slowly, as the amount of material coming out of the nozzle is quite high.", - "unit": "mm/s", - "type": "float", - "default": 15.0, - "active_if": { - "setting": "adhesion_type", - "value": "Raft" - } - }, - "raft_interface_thickness": { - "label": "Raft Interface Thickness", - "description": "Thickness of the 2nd raft layer.", - "unit": "mm", - "type": "float", - "default": 0.27, - "active_if": { - "setting": "adhesion_type", - "value": "Raft" - } - }, - "raft_interface_linewidth": { - "label": "Raft Interface Line Width", - "description": "Width of the 2nd raft layer lines. These lines should be thinner than the first layer, but strong enough to attach the object to.", - "unit": "mm", - "type": "float", - "default": 0.4, - "active_if": { - "setting": "adhesion_type", - "value": "Raft" - } - }, - "raft_airgap": { - "label": "Raft Air-gap", - "description": "The gap between the final raft layer and the first layer of the object. Only the first layer is raised by this amount to lower the bonding between the raft layer and the object. Makes it easier to peel off the raft.", - "unit": "mm", - "type": "float", - "default": 0.22, - "active_if": { - "setting": "adhesion_type", - "value": "Raft" - } - }, - "raft_surface_layers": { - "label": "Raft Surface Layers", - "description": "The number of surface layers on top of the 2nd raft layer. These are fully filled layers that the object sits on. 2 layers usually works fine.", - "type": "int", - "default": 2, - "active_if": { - "setting": "adhesion_type", - "value": "Raft" - } + "meshfix_keep_open_polygons": { + "label": "Keep Disconnected Faces", + "description": "Normally Cura tries to stitch up small holes in the mesh and remove parts of a layer with big holes. Enabling this option keeps those parts which cannot be stitched. This option should be used as a last resort option when all else doesn produce proper GCode.", + "type": "boolean", + "default": false, + "visible": false } } }, "blackmagic": { - "label": "Fixes", + "label": "Special Modes", "visible": true, - "icon": "category_fixes", + "icon": "category_blackmagic", "settings": { "magic_spiralize": { "label": "Spiralize the Outer Contour", diff --git a/resources/themes/cura/icons/category_blackmagic.svg b/resources/themes/cura/icons/category_blackmagic.svg new file mode 100644 index 0000000000..a16a303908 --- /dev/null +++ b/resources/themes/cura/icons/category_blackmagic.svg @@ -0,0 +1,149 @@ + + + +image/svg+xml? + \ No newline at end of file diff --git a/resources/themes/cura/icons/category_quality.svg b/resources/themes/cura/icons/category_layer_height.svg similarity index 100% rename from resources/themes/cura/icons/category_quality.svg rename to resources/themes/cura/icons/category_layer_height.svg diff --git a/resources/themes/cura/icons/category_shell.svg b/resources/themes/cura/icons/category_shell.svg new file mode 100644 index 0000000000..9fa439878d --- /dev/null +++ b/resources/themes/cura/icons/category_shell.svg @@ -0,0 +1,3384 @@ + + + +image/svg+xml \ No newline at end of file diff --git a/resources/themes/cura/icons/category_travel.svg b/resources/themes/cura/icons/category_travel.svg new file mode 100644 index 0000000000..cb6412d769 --- /dev/null +++ b/resources/themes/cura/icons/category_travel.svg @@ -0,0 +1,3422 @@ + + + +image/svg+xml \ No newline at end of file From 3fa71e3e6dbc074e4e1d284355cae437298f45a5 Mon Sep 17 00:00:00 2001 From: Tim Kuipers Date: Wed, 3 Jun 2015 12:52:04 +0200 Subject: [PATCH 013/117] introduces new combing settings: avoid objects during travel --- resources/settings/fdmprinter.json | 35 +++++++++++++++++++++++------- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/resources/settings/fdmprinter.json b/resources/settings/fdmprinter.json index 8bd5446a91..acab4b84bd 100644 --- a/resources/settings/fdmprinter.json +++ b/resources/settings/fdmprinter.json @@ -543,14 +543,6 @@ "visible": false, "inherit": false }, - "retraction_combing": { - "label": "Enable Combing", - "description": "Combing keeps the head within the interior of the print whenever possible when traveling from one part of the print to another, and does not use retraction. If combing is disabled the printer head moves straight from the start point to the end point and it will always retract.", - "type": "boolean", - "default": true, - "visible": false, - "inherit": false - }, "retraction_count_max": { "label": "Maximal Retraction Count", "description": "This settings limits the number of retractions occuring within the Minimal Extrusion Distance Window. Further retractions within this window will be ignored. This avoids retracting repeatedly on the same piece of filament as that can flatten the filament and cause grinding issues.", @@ -578,6 +570,33 @@ "inherit": false } } + }, + "retraction_combing": { + "label": "Enable Combing", + "description": "Combing keeps the head within the interior of the print whenever possible when traveling from one part of the print to another, and does not use retraction. If combing is disabled the printer head moves straight from the start point to the end point and it will always retract.", + "type": "boolean", + "default": true, + "visible": false, + "children": { + "travel_avoid_other_parts": { + "label": "Avoid Printed Parts", + "description": "Avoid other parts when traveling between parts.", + "type": "boolean", + "default": true, + "visible": false, + "children": { + "travel_avoid_distance": { + "label": "Avoid Distance", + "description": "The distance to stay clear of parts which are avoided during travel.", + "unit": "mm", + "type": "float", + "default": 1.5, + "visible": false, + "inherit": false + } + } + } + } } } }, From fb6216b99fc6f2369b54f5fb55fd1524f0621eab Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Wed, 3 Jun 2015 15:14:06 +0200 Subject: [PATCH 014/117] Add a changelog file --- CHANGES | 127 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 127 insertions(+) create mode 100644 CHANGES diff --git a/CHANGES b/CHANGES new file mode 100644 index 0000000000..616f906b34 --- /dev/null +++ b/CHANGES @@ -0,0 +1,127 @@ +Cura 15.06 Beta +=============== + +This is the *Beta* version of Cura 15.06. + +Cura 15.06 is a new release built from the ground up on a completely new +framework called Uranium. This framework has been designed to make it easier to +extend Cura with additional functionality as well as provide a cleaner UI. + +Changes since 15.05.91 +---------------------- + +* There is now a working MacOSX version. Currently it supports OSX 10.7 and + higher. +* Fixed: Need to deselect before selecting a different object. +* Fixed: Object can be moved on Z axis. +* Fixed: Error values should be considered invalid values and will not trigger a + slice. +* Fixed: Text fields used a locale-aware validator while the underlying code did + not. +* Fixed: Text fields will trigger a slice on text change, not only after focus + change/enter press. +* Fixed: Rotate Tool snaps to incorrect value. + +Changes since 15.05.90 +---------------------- + +* Fixed: Additional UI elements for tools and views not loading. +* Fixed: Double click needed to change setting dialog page. +* Fixed: Context menu entries (reload, center object, etc.) not working. +* Fixed: "Open With" or passing files from command line not working. +* Fixed: "Reload All" would not reload files. + +In addition, a lot of work has gone into getting a usable Mac OSX version. + +New Features +------------ + +* Plugin based system + The Uranium framework provides us with a plugin-based system + that provides additional flexibility when extending Cura. Think + of new views, tools, file formats, etc. This is probably the + biggest new feature. +* Improved UI + The UI has received a complete overhaul. +* Time-Quality Slider + The 4 static quick print profiles have been replaced with + a slider that should make it easier to find the right spot + between print time and print quality. +* More Settings + The Advanced mode is now configurable and can show many + additional settings that were previously not available, while at + the same time not overwhelming new users with too many settings. + Custom set of visible settings can be created by the user. +* Support for high-DPI screens + The refreshed UI has been designed with high-DPI screens in + mind which should improve the experience of Cura on such + devices. +* Improved language support + (Not yet available for the Beta release.) +* Improved support structure generation + The new version of the CuraEngine now features improved + support generation algorithms and additional options for support + structure generation. +* Experimental Feature: Wire Printing + Wire Printing has been added as an experimental new feature. It + will print objects as a structure of lines. It can be enabled by + from Advanced Mode -> Fixes -> Wire Printing. +* Undo/Redo + It is now possible to undo and redo most scene operations, like + moving or rotating objects. + +Features from earlier versions not (yet) in this release +-------------------------------------------------------- + +* The All-at-once/One-at-a-time toggle is not available. + We are working on an improved implementation of this mechanism + but it will not be available for this release. +* No dual extrusion features are available yet. + We are working on a completely new workflow for this but this + needs additional time. +* “Lay Flat” has been removed. + The existing implementation was unfortunately not salvageable. + We will be looking into an improved implementation for this + feature. +* "Split Object Into Parts" has been removed. + Due to the same reason as Lay Flat. +* Support for AMF and DAE file formats has been removed. + Both of these will be implemented as plugins in the future. +* Support for directly loading a GCode file is not yet available. + This will be implemented as a plugin in the future. +* Support for PNG, JPG and other image formats has been removed. + These can be supported by a plugin with an improved UI. +* Support for loading Minecraft levels has been removed. + This can be implemented as a plugin. +* Windows XP support has been dropped. + Microsoft is no longer supporting xp, so they no longer back + port certain features that we require. +* X-Ray view is missing. + Will be implemented as a (you might have guessed it) plugin. +* Infill display in the layer view is missing. + Like several other features, the existing implementation of this + functionality is not salvageable and will need to be reimplemented. + + +Known Issues +------------ + +For an up to date list of all known issues, please see +https://github.com/Ultimaker/Cura/issues and +https://github.com/Ultimaker/Uranium/issues . + +* The application has no application icon yet. +* The Windows version starts a console before starting the + application. This is intentional for the beta and it will be + removed for the final version. +* Opening the machine preferences page will switch to the first + available machine instead of keeping the current machine + selected. +* Some OBJ files are rendered as black objects due to missing + normals. +* The developer documentation for Uranium (available at + http://software.ultimaker.com/uranium/index.html) is not yet + complete. +* Disabling plugins does not work correctly yet. +* Unicorn occasionally still requires feeding. Do not feed it + after midnight. From 3b03cfa9493f1b2bf54ab750508f4bf99db350a6 Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Wed, 3 Jun 2015 15:14:22 +0200 Subject: [PATCH 015/117] Bump version to 15.05.93 --- cura/CuraApplication.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 62318fb200..f6d41a51c8 100644 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -48,7 +48,7 @@ class CuraApplication(QtApplication): if not hasattr(sys, "frozen"): Resources.addResourcePath(os.path.join(os.path.abspath(os.path.dirname(__file__)), "..")) - super().__init__(name = "cura", version = "15.05.92") + super().__init__(name = "cura", version = "15.05.93") self.setRequiredPlugins([ "CuraEngineBackend", From 7302d979dc4358d254ae0db72a582e464caf7997 Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Wed, 3 Jun 2015 17:04:29 +0200 Subject: [PATCH 016/117] Use the right offset for object collision after Polygon collision was fixed Contributes to #35 --- cura/PlatformPhysics.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cura/PlatformPhysics.py b/cura/PlatformPhysics.py index 8ddf206aea..c52e8a9174 100644 --- a/cura/PlatformPhysics.py +++ b/cura/PlatformPhysics.py @@ -80,8 +80,8 @@ class PlatformPhysics: if overlap is None: continue - move_vector.setX(-overlap[0]) - move_vector.setZ(-overlap[1]) + move_vector.setX(overlap[0] * 1.1) + move_vector.setZ(overlap[1] * 1.1) if move_vector != Vector(): op = PlatformPhysicsOperation.PlatformPhysicsOperation(node, move_vector) From db8b0acdded697489eb507b274d7b418b4c83d97 Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Wed, 3 Jun 2015 19:10:21 +0200 Subject: [PATCH 017/117] Ignore selection when moving objects due to collision Fixes #35 --- cura/PlatformPhysics.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cura/PlatformPhysics.py b/cura/PlatformPhysics.py index c52e8a9174..5e4bd5a415 100644 --- a/cura/PlatformPhysics.py +++ b/cura/PlatformPhysics.py @@ -11,6 +11,7 @@ from UM.Math.Float import Float from UM.Math.Vector import Vector from UM.Math.AxisAlignedBox import AxisAlignedBox from UM.Application import Application +from UM.Scene.Selection import Selection from . import PlatformPhysicsOperation from . import ConvexHullJob @@ -60,6 +61,8 @@ class PlatformPhysics: job = ConvexHullJob.ConvexHullJob(node) job.start() node._convex_hull_job = job + elif Selection.isSelected(node): + pass else: # Check for collisions between convex hulls for other_node in BreadthFirstIterator(root): From 367b8791aecee16bc1d426b3ead3bee2065eff1e Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Wed, 3 Jun 2015 20:29:53 +0200 Subject: [PATCH 018/117] Render the build platform grid as a single sided plane This allows us to look at objects from beneath the object. Also fixes #14 --- cura/BuildVolume.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cura/BuildVolume.py b/cura/BuildVolume.py index 85806542f8..97a3888c6e 100644 --- a/cura/BuildVolume.py +++ b/cura/BuildVolume.py @@ -62,7 +62,7 @@ class BuildVolume(SceneNode): self._grid_material.setUniformValue("u_gridColor1", Color(205, 202, 201, 255)) renderer.queueNode(self, material = self._material, mode = Renderer.RenderLines) - renderer.queueNode(self, mesh = self._grid_mesh, material = self._grid_material) + renderer.queueNode(self, mesh = self._grid_mesh, material = self._grid_material, force_single_sided = True) if self._disallowed_area_mesh: renderer.queueNode(self, mesh = self._disallowed_area_mesh, material = self._material) return True @@ -99,10 +99,10 @@ class BuildVolume(SceneNode): mb = MeshBuilder() mb.addQuad( - Vector(minW, minH, maxD), - Vector(maxW, minH, maxD), + Vector(minW, minH, minD), Vector(maxW, minH, minD), - Vector(minW, minH, minD) + Vector(maxW, minH, maxD), + Vector(minW, minH, maxD) ) self._grid_mesh = mb.getData() for n in range(0, 6): From 24a8edd2ae955a968acf40356c5aa772b83b7dbf Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Wed, 3 Jun 2015 21:00:58 +0200 Subject: [PATCH 019/117] Make button tooltip text use the dark color from the palette This fixes a regression where tooltip text was white --- resources/themes/cura/styles.qml | 2 +- resources/themes/cura/theme.json | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/resources/themes/cura/styles.qml b/resources/themes/cura/styles.qml index 9e9cc11e42..dc2b80d2ad 100644 --- a/resources/themes/cura/styles.qml +++ b/resources/themes/cura/styles.qml @@ -73,7 +73,7 @@ QtObject { anchors.horizontalCenter: parent.horizontalCenter; text: control.text; font: UM.Theme.fonts.button_tooltip; - color: UM.Theme.colors.button_text; + color: UM.Theme.colors.button_tooltip_text; } } } diff --git a/resources/themes/cura/theme.json b/resources/themes/cura/theme.json index f73360d439..a79636aece 100644 --- a/resources/themes/cura/theme.json +++ b/resources/themes/cura/theme.json @@ -63,6 +63,7 @@ "button_active_hover": [34, 150, 190, 255], "button_text": [255, 255, 255, 255], "button_disabled": [245, 245, 245, 255], + "button_tooltip_text": [35, 35, 35, 255], "scrollbar_background": [245, 245, 245, 255], "scrollbar_handle": [205, 202, 201, 255], From 005b02ba10538703c6aa3037e17db3beabcd682c Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Wed, 3 Jun 2015 21:56:20 +0200 Subject: [PATCH 020/117] Add changes of 15.05.93 release --- CHANGES | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGES b/CHANGES index 616f906b34..e62253066b 100644 --- a/CHANGES +++ b/CHANGES @@ -21,6 +21,12 @@ Changes since 15.05.91 * Fixed: Text fields will trigger a slice on text change, not only after focus change/enter press. * Fixed: Rotate Tool snaps to incorrect value. +* Fixed: Object Collision would only moved objects to the right. +* Fixed: Object Collision would move the selected object when it should not. +* Fixed: Camera panning now works correctly instead of doing nothing. +* Fixed: Camera would flip around center point at maximum rotation. +* Fixed: Build platform grid blocked view from below objects. +* Fixed: Viewport on MacOSX with high-DPI screens was only taking 1/4th of the window Changes since 15.05.90 ---------------------- From e73d48bc675ed068db0cb8f61bf5ce0d12ae2008 Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Thu, 4 Jun 2015 10:29:45 +0200 Subject: [PATCH 021/117] Render a transparent ghost of the selection when things are selected. Fixes Asana issue about layer view --- plugins/LayerView/LayerView.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/plugins/LayerView/LayerView.py b/plugins/LayerView/LayerView.py index 576feb6cb4..96ff102a14 100644 --- a/plugins/LayerView/LayerView.py +++ b/plugins/LayerView/LayerView.py @@ -5,6 +5,8 @@ from UM.View.View import View from UM.View.Renderer import Renderer from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator from UM.Resources import Resources +from UM.Scene.Selection import Selection +from UM.Math.Color import Color ## View used to display g-code paths. class LayerView(View): @@ -23,9 +25,15 @@ class LayerView(View): self._material = renderer.createMaterial(Resources.getPath(Resources.ShadersLocation, "basic.vert"), Resources.getPath(Resources.ShadersLocation, "vertexcolor.frag")) self._material.setUniformValue("u_color", [1.0, 0.0, 0.0, 1.0]) + self._selection_material = renderer.createMaterial(Resources.getPath(Resources.ShadersLocation, "basic.vert"), Resources.getPath(Resources.ShadersLocation, "color.frag")) + self._selection_material.setUniformValue("u_color", Color(35, 35, 35, 128)) + for node in DepthFirstIterator(scene.getRoot()): if not node.render(renderer): if node.getMeshData() and node.isVisible(): + if Selection.isSelected(node): + renderer.queueNode(node, material = self._selection_material, transparent = True) + try: layer_data = node.getMeshData().layerData except AttributeError: @@ -43,9 +51,9 @@ class LayerView(View): if layer >= end_layer: break - renderer.queueNode(node, mesh = layer_data, material = self._material, mode = Renderer.RenderLines, start = start, end = end) + renderer.queueNode(node, mesh = layer_data, material = self._material, mode = Renderer.RenderLines, start = start, end = end, overlay = True) else: - renderer.queueNode(node, mesh = layer_data, material = self._material, mode = Renderer.RenderLines) + renderer.queueNode(node, mesh = layer_data, material = self._material, mode = Renderer.RenderLines, overlay = True) def setLayer(self, value): self._layer_percentage = value From 95d4b34f06a1d31a9235845f04ce70d1006fda40 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 4 Jun 2015 11:12:31 +0200 Subject: [PATCH 022/117] Added exception handling to listen thread join --- plugins/USBPrinting/PrinterConnection.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/plugins/USBPrinting/PrinterConnection.py b/plugins/USBPrinting/PrinterConnection.py index 3a953c3744..4a9a73bb7b 100644 --- a/plugins/USBPrinting/PrinterConnection.py +++ b/plugins/USBPrinting/PrinterConnection.py @@ -248,7 +248,10 @@ class PrinterConnection(SignalEmitter): if self._serial is not None: self.setIsConnected(False) - self._listen_thread.join() + try: + self._listen_thread.join() + except: + pass self._serial.close() self._serial = None From 8db6b10ad3b27ba2a872ce37fd9978c9fa8b12b4 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 4 Jun 2015 11:23:15 +0200 Subject: [PATCH 023/117] Updated name in menu for firmware update --- plugins/USBPrinting/USBPrinterManager.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/USBPrinting/USBPrinterManager.py b/plugins/USBPrinting/USBPrinterManager.py index ba515594ae..27ff31647b 100644 --- a/plugins/USBPrinting/USBPrinterManager.py +++ b/plugins/USBPrinting/USBPrinterManager.py @@ -40,7 +40,8 @@ class USBPrinterManager(QObject, SignalEmitter, Extension): self._error_message = "" ## Add menu item to top menu of the application. - self.addMenuItem(i18n_catalog.i18n("Update Firmware"), self.updateAllFirmware) + self.setMenuName("Firmware") + self.addMenuItem(i18n_catalog.i18n("Update"), self.updateAllFirmware) pyqtError = pyqtSignal(str, arguments = ["amount"]) processingProgress = pyqtSignal(float, arguments = ["amount"]) From 1f9f253292bd1af8385e937aaff13a2c6d625bfd Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 4 Jun 2015 11:12:31 +0200 Subject: [PATCH 024/117] Added exception handling to listen thread join --- plugins/USBPrinting/PrinterConnection.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/plugins/USBPrinting/PrinterConnection.py b/plugins/USBPrinting/PrinterConnection.py index ad231f5207..19c5b5c07a 100644 --- a/plugins/USBPrinting/PrinterConnection.py +++ b/plugins/USBPrinting/PrinterConnection.py @@ -244,7 +244,10 @@ class PrinterConnection(SignalEmitter): self._connect_thread.join() if self._serial is not None: self.setIsConnected(False) - self._listen_thread.join() + try: + self._listen_thread.join() + except: + pass self._serial.close() self._serial = None From 797f9ec426caf10ebed079e617abd200ad9632e2 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 4 Jun 2015 11:23:15 +0200 Subject: [PATCH 025/117] Updated name in menu for firmware update --- plugins/USBPrinting/USBPrinterManager.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/USBPrinting/USBPrinterManager.py b/plugins/USBPrinting/USBPrinterManager.py index ba515594ae..27ff31647b 100644 --- a/plugins/USBPrinting/USBPrinterManager.py +++ b/plugins/USBPrinting/USBPrinterManager.py @@ -40,7 +40,8 @@ class USBPrinterManager(QObject, SignalEmitter, Extension): self._error_message = "" ## Add menu item to top menu of the application. - self.addMenuItem(i18n_catalog.i18n("Update Firmware"), self.updateAllFirmware) + self.setMenuName("Firmware") + self.addMenuItem(i18n_catalog.i18n("Update"), self.updateAllFirmware) pyqtError = pyqtSignal(str, arguments = ["amount"]) processingProgress = pyqtSignal(float, arguments = ["amount"]) From 33c69b32f81bf63a197a34babebfc0945e194601 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 5 Jun 2015 12:10:13 +0200 Subject: [PATCH 026/117] Fix for #29 --- plugins/LayerView/LayerView.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/LayerView/LayerView.py b/plugins/LayerView/LayerView.py index 576feb6cb4..3b6edc195e 100644 --- a/plugins/LayerView/LayerView.py +++ b/plugins/LayerView/LayerView.py @@ -39,6 +39,8 @@ class LayerView(View): element_counts = layer_data.getElementCounts() for layer, counts in element_counts.items(): end += sum(counts) + ## Hack to ensure the end is correct. Not quite sure what causes this + end += 2 * len(counts) if layer >= end_layer: break From 81b41b88899077d3d21e06914280cfff4706d4a2 Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Thu, 4 Jun 2015 10:29:45 +0200 Subject: [PATCH 027/117] Render a transparent ghost of the selection when things are selected. Fixes Asana issue about layer view --- plugins/LayerView/LayerView.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/plugins/LayerView/LayerView.py b/plugins/LayerView/LayerView.py index 3b6edc195e..82a164ecd7 100644 --- a/plugins/LayerView/LayerView.py +++ b/plugins/LayerView/LayerView.py @@ -5,6 +5,8 @@ from UM.View.View import View from UM.View.Renderer import Renderer from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator from UM.Resources import Resources +from UM.Scene.Selection import Selection +from UM.Math.Color import Color ## View used to display g-code paths. class LayerView(View): @@ -23,9 +25,15 @@ class LayerView(View): self._material = renderer.createMaterial(Resources.getPath(Resources.ShadersLocation, "basic.vert"), Resources.getPath(Resources.ShadersLocation, "vertexcolor.frag")) self._material.setUniformValue("u_color", [1.0, 0.0, 0.0, 1.0]) + self._selection_material = renderer.createMaterial(Resources.getPath(Resources.ShadersLocation, "basic.vert"), Resources.getPath(Resources.ShadersLocation, "color.frag")) + self._selection_material.setUniformValue("u_color", Color(35, 35, 35, 128)) + for node in DepthFirstIterator(scene.getRoot()): if not node.render(renderer): if node.getMeshData() and node.isVisible(): + if Selection.isSelected(node): + renderer.queueNode(node, material = self._selection_material, transparent = True) + try: layer_data = node.getMeshData().layerData except AttributeError: @@ -45,9 +53,9 @@ class LayerView(View): if layer >= end_layer: break - renderer.queueNode(node, mesh = layer_data, material = self._material, mode = Renderer.RenderLines, start = start, end = end) + renderer.queueNode(node, mesh = layer_data, material = self._material, mode = Renderer.RenderLines, start = start, end = end, overlay = True) else: - renderer.queueNode(node, mesh = layer_data, material = self._material, mode = Renderer.RenderLines) + renderer.queueNode(node, mesh = layer_data, material = self._material, mode = Renderer.RenderLines, overlay = True) def setLayer(self, value): self._layer_percentage = value From b5fda5b835142f0c6cdd09193e9182b21e4631d9 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 4 Jun 2015 11:12:31 +0200 Subject: [PATCH 028/117] Added exception handling to listen thread join --- plugins/USBPrinting/PrinterConnection.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/plugins/USBPrinting/PrinterConnection.py b/plugins/USBPrinting/PrinterConnection.py index ad231f5207..19c5b5c07a 100644 --- a/plugins/USBPrinting/PrinterConnection.py +++ b/plugins/USBPrinting/PrinterConnection.py @@ -244,7 +244,10 @@ class PrinterConnection(SignalEmitter): self._connect_thread.join() if self._serial is not None: self.setIsConnected(False) - self._listen_thread.join() + try: + self._listen_thread.join() + except: + pass self._serial.close() self._serial = None From f57d2693fedf7e227e77b2d07840a94242863f99 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 4 Jun 2015 11:23:15 +0200 Subject: [PATCH 029/117] Updated name in menu for firmware update --- plugins/USBPrinting/USBPrinterManager.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/USBPrinting/USBPrinterManager.py b/plugins/USBPrinting/USBPrinterManager.py index ba515594ae..27ff31647b 100644 --- a/plugins/USBPrinting/USBPrinterManager.py +++ b/plugins/USBPrinting/USBPrinterManager.py @@ -40,7 +40,8 @@ class USBPrinterManager(QObject, SignalEmitter, Extension): self._error_message = "" ## Add menu item to top menu of the application. - self.addMenuItem(i18n_catalog.i18n("Update Firmware"), self.updateAllFirmware) + self.setMenuName("Firmware") + self.addMenuItem(i18n_catalog.i18n("Update"), self.updateAllFirmware) pyqtError = pyqtSignal(str, arguments = ["amount"]) processingProgress = pyqtSignal(float, arguments = ["amount"]) From a53e56e282df3359f0fc2c4ad5f71f916d1e7f74 Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Fri, 5 Jun 2015 13:25:04 +0200 Subject: [PATCH 030/117] Handle finished signal for the ReadMeshJob started from command line args This puts the actual mesh into the scene rather than just loading it and discarding it afterwards. Now file arguments supplied to the application actually work. Fixes "Open With" on Windows and the related Asana issue. --- cura/CuraApplication.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index f6d41a51c8..55e1e466a4 100644 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -162,6 +162,7 @@ class CuraApplication(QtApplication): for file in self.getCommandLineOption("file", []): job = ReadMeshJob(os.path.abspath(file)) + job.finished.connect(self._onFileLoaded) job.start() self.exec_() @@ -447,3 +448,15 @@ class CuraApplication(QtApplication): def _onMessageActionTriggered(self, message, action): if action == "eject": self.getStorageDevice("LocalFileStorage").ejectRemovableDrive(message._sdcard) + + def _onFileLoaded(self, job): + mesh = job.getResult() + if mesh != None: + node = SceneNode() + + node.setSelectable(True) + node.setMeshData(mesh) + node.setName(os.path.basename(job.getFileName())) + + op = AddSceneNodeOperation(node, self.getController().getScene().getRoot()) + op.push() From a2c099a7d81526d03d0ca83f2b857a3ac9c33e62 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 5 Jun 2015 17:40:57 +0200 Subject: [PATCH 031/117] Implemented feature described by #30 --- plugins/LayerView/LayerView.py | 92 +++++++++++++++++++++++------ plugins/LayerView/LayerView.qml | 6 +- plugins/LayerView/LayerViewProxy.py | 44 ++++++++++++++ plugins/LayerView/__init__.py | 7 ++- 4 files changed, 128 insertions(+), 21 deletions(-) create mode 100644 plugins/LayerView/LayerViewProxy.py diff --git a/plugins/LayerView/LayerView.py b/plugins/LayerView/LayerView.py index 3b6edc195e..50ad534b09 100644 --- a/plugins/LayerView/LayerView.py +++ b/plugins/LayerView/LayerView.py @@ -5,6 +5,9 @@ from UM.View.View import View from UM.View.Renderer import Renderer from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator from UM.Resources import Resources +from UM.Event import Event, KeyEvent +from UM.Signal import Signal +from . import LayerViewProxy ## View used to display g-code paths. class LayerView(View): @@ -13,11 +16,27 @@ class LayerView(View): self._material = None self._num_layers = 0 self._layer_percentage = 0 # what percentage of layers need to be shown (SLider gives value between 0 - 100) + self._current_layer_num = 0 + self._proxy = LayerViewProxy.LayerViewProxy() + self._controller.getScene().sceneChanged.connect(self._onSceneChanged) + self._max_layers = 10 + + def getCurrentLayer(self): + return self._current_layer_num + + def _onSceneChanged(self, node): + self.calculateMaxLayers() + + def getMaxLayers(self): + return self._max_layers def beginRendering(self): scene = self.getController().getScene() renderer = self.getRenderer() renderer.setRenderSelection(False) + + ## Recalculate num max layers + #self.calculateMaxLayers() if not self._material: self._material = renderer.createMaterial(Resources.getPath(Resources.ShadersLocation, "basic.vert"), Resources.getPath(Resources.ShadersLocation, "vertexcolor.frag")) @@ -31,26 +50,65 @@ class LayerView(View): except AttributeError: continue - if self._layer_percentage < 100: - start = 0 - end_layer = round(len(layer_data.getLayers()) * (self._layer_percentage / 100)) - end = 0 + start = 0 + end = 0 - element_counts = layer_data.getElementCounts() - for layer, counts in element_counts.items(): - end += sum(counts) - ## Hack to ensure the end is correct. Not quite sure what causes this - end += 2 * len(counts) + element_counts = layer_data.getElementCounts() + for layer, counts in element_counts.items(): + end += sum(counts) + ## Hack to ensure the end is correct. Not quite sure what causes this + end += 2 * len(counts) - if layer >= end_layer: - break + if layer >= self._current_layer_num: + break + + renderer.queueNode(node, mesh = layer_data, material = self._material, mode = Renderer.RenderLines, start = start, end = end) - renderer.queueNode(node, mesh = layer_data, material = self._material, mode = Renderer.RenderLines, start = start, end = end) - else: - renderer.queueNode(node, mesh = layer_data, material = self._material, mode = Renderer.RenderLines) - def setLayer(self, value): - self._layer_percentage = value - + if self._current_layer_num != value: + self._current_layer_num = value + if self._current_layer_num < 0: + self._current_layer_num = 0 + if self._current_layer_num > self._max_layers: + self._current_layer_num = self._max_layers + self.currentLayerNumChanged.emit() + + currentLayerNumChanged = Signal() + + def calculateMaxLayers(self): + scene = self.getController().getScene() + renderer = self.getRenderer() + if renderer and self._material: + renderer.setRenderSelection(False) + self._old_max_layers = self._max_layers + ## Recalculate num max layers + self._max_layers = 0 + for node in DepthFirstIterator(scene.getRoot()): + if not node.render(renderer): + if node.getMeshData() and node.isVisible(): + try: + layer_data = node.getMeshData().layerData + except AttributeError: + continue + if self._max_layers < len(layer_data.getLayers()): + self._max_layers = len(layer_data.getLayers()) + + if self._max_layers != self._old_max_layers: + self.maxLayersChanged.emit() + + maxLayersChanged = Signal() + + ## Hackish way to ensure the proxy is already created, which ensures that the layerview.qml is already created + # as this caused some issues. + def getProxy(self, engine, script_engine): + return self._proxy + def endRendering(self): pass + + def event(self, event): + if event.type == Event.KeyPressEvent: + if event.key == KeyEvent.UpKey: + self.setLayer(self._current_layer_num + 1) + if event.key == KeyEvent.DownKey: + self.setLayer(self._current_layer_num - 1) diff --git a/plugins/LayerView/LayerView.qml b/plugins/LayerView/LayerView.qml index fc598bfb75..d8a95dff47 100644 --- a/plugins/LayerView/LayerView.qml +++ b/plugins/LayerView/LayerView.qml @@ -20,10 +20,10 @@ Item anchors.right : parent.right orientation: Qt.Vertical minimumValue: 0; - maximumValue: 100; + maximumValue: UM.LayerView.numLayers; - value: 100; - onValueChanged: UM.ActiveView.triggerAction("setLayer", value) + value: UM.LayerView.currentLayer + onValueChanged: UM.LayerView.setCurrentLayer(value) style: UM.Theme.styles.slider; } diff --git a/plugins/LayerView/LayerViewProxy.py b/plugins/LayerView/LayerViewProxy.py new file mode 100644 index 0000000000..b6a266233c --- /dev/null +++ b/plugins/LayerView/LayerViewProxy.py @@ -0,0 +1,44 @@ +from PyQt5.QtCore import QObject, pyqtSignal, pyqtSlot, pyqtProperty +from UM.Application import Application +import LayerView +class LayerViewProxy(QObject): + def __init__(self, parent = None): + super().__init__(parent) + self._current_layer = 0 + self._controller = Application.getInstance().getController() + self._controller.activeViewChanged.connect(self._onActiveViewChanged) + self._onActiveViewChanged() + + currentLayerChanged = pyqtSignal() + maxLayersChanged = pyqtSignal() + + @pyqtProperty(int, notify = maxLayersChanged) + def numLayers(self): + active_view = self._controller.getActiveView() + #print("num max layers " , active_view.getMaxLayers()) + return active_view.getMaxLayers() + #return 100 + + @pyqtProperty(int, notify = currentLayerChanged) + def currentLayer(self): + active_view = self._controller.getActiveView() + if type(active_view) == LayerView.LayerView.LayerView: + return active_view.getCurrentLayer() + + @pyqtSlot(int) + def setCurrentLayer(self, layer_num): + active_view = self._controller.getActiveView() + if type(active_view) == LayerView.LayerView.LayerView: + active_view.setLayer(layer_num) + + def _onLayerChanged(self): + self.currentLayerChanged.emit() + + def _onMaxLayersChanged(self): + self.maxLayersChanged.emit() + + def _onActiveViewChanged(self): + active_view = self._controller.getActiveView() + if type(active_view) == LayerView.LayerView.LayerView: + active_view.currentLayerNumChanged.connect(self._onLayerChanged) + active_view.maxLayersChanged.connect(self._onMaxLayersChanged) \ No newline at end of file diff --git a/plugins/LayerView/__init__.py b/plugins/LayerView/__init__.py index 93aa856175..eb3ba4cdbe 100644 --- a/plugins/LayerView/__init__.py +++ b/plugins/LayerView/__init__.py @@ -1,7 +1,8 @@ # Copyright (c) 2015 Ultimaker B.V. # Cura is released under the terms of the AGPLv3 or higher. -from . import LayerView +from . import LayerView, LayerViewProxy +from PyQt5.QtQml import qmlRegisterType, qmlRegisterSingletonType from UM.i18n import i18nCatalog catalog = i18nCatalog("cura") @@ -21,6 +22,10 @@ def getMetaData(): } } +def createLayerViewProxy(engine, script_engine): + return LayerViewProxy.LayerViewProxy() def register(app): + layer_view = LayerView.LayerView() + qmlRegisterSingletonType(LayerViewProxy.LayerViewProxy, "UM", 1, 0, "LayerView", layer_view.getProxy) return { "view": LayerView.LayerView() } From 8db06b8305984c614e8a54b9f2e7fe5f49b89dc4 Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Fri, 5 Jun 2015 18:55:10 +0200 Subject: [PATCH 032/117] Add a text field style that can be used for generic text fields Based on the setting text field style. Contributes to Asana issue 33752130551782 --- resources/themes/cura/styles.qml | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/resources/themes/cura/styles.qml b/resources/themes/cura/styles.qml index dc2b80d2ad..b7db374da1 100644 --- a/resources/themes/cura/styles.qml +++ b/resources/themes/cura/styles.qml @@ -246,4 +246,32 @@ QtObject { } } } + + property Component text_field: Component { + TextFieldStyle { + textColor: UM.Theme.colors.setting_control_text; + font: UM.Theme.fonts.default; + + background: Rectangle + { + implicitHeight: control.height; + implicitWidth: control.width; + + border.width: 1; + border.color: UM.Theme.colors.setting_control_border; + + color: UM.Theme.colors.setting_validation_ok; + + Label { + anchors.right: parent.right; + anchors.rightMargin: UM.Theme.sizes.setting_unit_margin.width; + anchors.verticalCenter: parent.verticalCenter; + + text: control.unit ? control.unit : "" + color: UM.Theme.colors.setting_unit; + font: UM.Theme.fonts.default; + } + } + } + } } From 5235278a311426ad03cf418153af267f37cf445c Mon Sep 17 00:00:00 2001 From: Tim Kuipers Date: Mon, 8 Jun 2015 10:51:51 +0200 Subject: [PATCH 033/117] bugfix for 0% infill --- resources/settings/fdmprinter.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/settings/fdmprinter.json b/resources/settings/fdmprinter.json index acab4b84bd..799d685b8f 100644 --- a/resources/settings/fdmprinter.json +++ b/resources/settings/fdmprinter.json @@ -307,7 +307,7 @@ "type": "float", "default": 2.0, "visible": false, - "inherit_function": "(infill_line_width * 100) / parent_value" + "inherit_function": "0 if parent_value == 0 else (infill_line_width * 100) / parent_value" } } }, From c25a479fb78413bc52de7b43b2e587681ea41dbd Mon Sep 17 00:00:00 2001 From: Tim Kuipers Date: Mon, 8 Jun 2015 13:24:35 +0200 Subject: [PATCH 034/117] coasting settings and some more setting fixes --- resources/settings/fdmprinter.json | 348 ++++++++++++++++++++++------- 1 file changed, 264 insertions(+), 84 deletions(-) diff --git a/resources/settings/fdmprinter.json b/resources/settings/fdmprinter.json index 799d685b8f..c9eb2c0a49 100644 --- a/resources/settings/fdmprinter.json +++ b/resources/settings/fdmprinter.json @@ -494,109 +494,273 @@ "label": "Enable Retraction", "description": "Retract the filament when the nozzle is moving over a non-printed area. Details about the retraction can be configured in the advanced tab.", "type": "boolean", - "default": true, - + "default": true + }, + "retraction_speed": { + "label": "Retraction Speed", + "description": "The speed at which the filament is retracted. A higher retraction speed works better, but a very high retraction speed can lead to filament grinding.", + "unit": "mm/s", + "type": "float", + "default": 25.0, + "visible": false, + "inherit": false, + "active_if": { + "setting": "retraction_enable", + "value": true + }, "children": { - "retraction_speed": { - "label": "Retraction Speed", + "retraction_retract_speed": { + "label": "Retraction Retract Speed", "description": "The speed at which the filament is retracted. A higher retraction speed works better, but a very high retraction speed can lead to filament grinding.", "unit": "mm/s", "type": "float", "default": 25.0, "visible": false, - "inherit": false, - - "children": { - "retraction_retract_speed": { - "label": "Retraction Retract Speed", - "description": "The speed at which the filament is retracted. A higher retraction speed works better, but a very high retraction speed can lead to filament grinding.", - "unit": "mm/s", - "type": "float", - "default": 25.0, - "visible": false - }, - "retraction_prime_speed": { - "label": "Retraction Prime Speed", - "description": "The speed at which the filament is pushed back after retraction.", - "unit": "mm/s", - "type": "float", - "default": 25.0, - "visible": false - } + "active_if": { + "setting": "retraction_enable", + "value": true } }, - "retraction_amount": { - "label": "Retraction Distance", - "description": "The amount of retraction: Set at 0 for no retraction at all. A value of 4.5mm seems to generate good results for 3mm filament in Bowden-tube fed printers.", - "unit": "mm", + "retraction_prime_speed": { + "label": "Retraction Prime Speed", + "description": "The speed at which the filament is pushed back after retraction.", + "unit": "mm/s", "type": "float", - "default": 4.5, + "default": 25.0, "visible": false, - "inherit": false - }, - "retraction_min_travel": { - "label": "Retraction Minimum Travel", - "description": "The minimum distance of travel needed for a retraction to happen at all. This helps ensure you do not get a lot of retractions in a small area.", - "unit": "mm", - "type": "float", - "default": 4.5, - "visible": false, - "inherit": false - }, - "retraction_count_max": { - "label": "Maximal Retraction Count", - "description": "This settings limits the number of retractions occuring within the Minimal Extrusion Distance Window. Further retractions within this window will be ignored. This avoids retracting repeatedly on the same piece of filament as that can flatten the filament and cause grinding issues.", - "default": 6, - "type": "int", - "visible": false, - "inherit": false - }, - "retraction_extrusion_window": { - "label": "Minimal Extrusion Distance Window", - "description": "The window in which the Maximal Retraction Count is enforced. This window should be approximately the size of the Retraction distance, so that effectively the number of times a retraction passes the same patch of material is limited.", - "unit": "mm", - "type": "float", - "default": 4.5, - "visible": false, - "inherit_function": "retraction_amount" - }, - "retraction_hop": { - "label": "Z Hop when Retracting", - "description": "Whenever a retraction is done, the head is lifted by this amount to travel over the print. A value of 0.075 works well. This feature has a lot of positive effect on delta towers.", - "unit": "mm", - "type": "float", - "default": 0.0, - "visible": false, - "inherit": false + "active_if": { + "setting": "retraction_enable", + "value": true + } } } }, + "retraction_amount": { + "label": "Retraction Distance", + "description": "The amount of retraction: Set at 0 for no retraction at all. A value of 4.5mm seems to generate good results for 3mm filament in Bowden-tube fed printers.", + "unit": "mm", + "type": "float", + "default": 4.5, + "visible": false, + "inherit": false, + "active_if": { + "setting": "retraction_enable", + "value": true + } + }, + "retraction_min_travel": { + "label": "Retraction Minimum Travel", + "description": "The minimum distance of travel needed for a retraction to happen at all. This helps ensure you do not get a lot of retractions in a small area.", + "unit": "mm", + "type": "float", + "default": 4.5, + "visible": false, + "inherit": false, + "active_if": { + "setting": "retraction_enable", + "value": true + } + }, + "retraction_count_max": { + "label": "Maximal Retraction Count", + "description": "This settings limits the number of retractions occuring within the Minimal Extrusion Distance Window. Further retractions within this window will be ignored. This avoids retracting repeatedly on the same piece of filament as that can flatten the filament and cause grinding issues.", + "default": 6, + "type": "int", + "visible": false, + "inherit": false, + "active_if": { + "setting": "retraction_enable", + "value": true + } + }, + "retraction_extrusion_window": { + "label": "Minimal Extrusion Distance Window", + "description": "The window in which the Maximal Retraction Count is enforced. This window should be approximately the size of the Retraction distance, so that effectively the number of times a retraction passes the same patch of material is limited.", + "unit": "mm", + "type": "float", + "default": 4.5, + "visible": false, + "inherit_function": "retraction_amount", + "active_if": { + "setting": "retraction_enable", + "value": true + } + }, + "retraction_hop": { + "label": "Z Hop when Retracting", + "description": "Whenever a retraction is done, the head is lifted by this amount to travel over the print. A value of 0.075 works well. This feature has a lot of positive effect on delta towers.", + "unit": "mm", + "type": "float", + "default": 0.0, + "visible": false, + "inherit": false, + "active_if": { + "setting": "retraction_enable", + "value": true + } + }, "retraction_combing": { "label": "Enable Combing", "description": "Combing keeps the head within the interior of the print whenever possible when traveling from one part of the print to another, and does not use retraction. If combing is disabled the printer head moves straight from the start point to the end point and it will always retract.", "type": "boolean", "default": true, + "visible": false + }, + "travel_avoid_other_parts": { + "label": "Avoid Printed Parts", + "description": "Avoid other parts when traveling between parts.", + "type": "boolean", + "default": true, "visible": false, + "active_if": { + "setting": "retraction_combing", + "value": true + }, "children": { - "travel_avoid_other_parts": { - "label": "Avoid Printed Parts", - "description": "Avoid other parts when traveling between parts.", - "type": "boolean", - "default": true, + "travel_avoid_distance": { + "label": "Avoid Distance", + "description": "The distance to stay clear of parts which are avoided during travel.", + "unit": "mm", + "type": "float", + "default": 1.5, "visible": false, - "children": { - "travel_avoid_distance": { - "label": "Avoid Distance", - "description": "The distance to stay clear of parts which are avoided during travel.", - "unit": "mm", - "type": "float", - "default": 1.5, - "visible": false, - "inherit": false - } + "inherit": false, + "active_if": { + "setting": "retraction_combing", + "value": true } } } + }, + "coasting_enable": { + "label": "Enable Coasting", + "description": "Coasting replaces the last part of an extrusion path with a travel path. The oozed material is used to lay down the last piece of the extrusion path in order to reduce stringing.", + "type": "boolean", + "default": true, + "visible": true + }, + "coasting_volume": { + "label": "Coasting Volume", + "description": "The volume otherwise oozed. This value should generally be close to the nozzle diameter cubed.", + "unit": "mm³", + "type": "float", + "default": 0.064, + "visible": false, + "inherit": false, + "active_if": { + "setting": "coasting_enable", + "value": true + }, + "children": { + "coasting_volume_retract": { + "label": "Retract-Coasting Volume", + "description": "The volume otherwise oozed in a travel move with retraction.", + "unit": "mm³", + "type": "float", + "default": 0.096, + "visible": false, + "inherit": true, + "active_if": { + "setting": "coasting_enable", + "value": true + } + }, + "coasting_volume_move": { + "label": "Move-Coasting Volume", + "description": "The volume otherwise oozed in a travel move without retraction.", + "unit": "mm³", + "type": "float", + "default": 0.064, + "visible": false, + "inherit": true, + "active_if": { + "setting": "coasting_enable", + "value": true + } + } + } + }, + "coasting_min_volume": { + "label": "Minimal Volume Before Coasting", + "description": "The least volume an extrusion path should have to coast the full amount. For smaller extrusion paths, less pressure has been built up in the bowden tube and so the coasted volume is scaled linearly.", + "unit": "mm³", + "type": "float", + "default": 0.8, + "visible": false, + "inherit": false, + "active_if": { + "setting": "coasting_enable", + "value": true + }, + "children": { + "coasting_min_volume_retract": { + "label": "Min Volume Retract-Coasting", + "description": "The minimal volume an extrusion path must have in order to coast the full amount before doing a retraction.", + "unit": "mm³", + "type": "float", + "default": 0.6, + "visible": false, + "inherit": true, + "active_if": { + "setting": "coasting_enable", + "value": true + } + }, + "coasting_min_volume_move": { + "label": "Min Volume Move-Coasting", + "description": "The minimal volume an extrusion path must have in order to coast the full amount before doing a travel move without retraction.", + "unit": "mm³", + "type": "float", + "default": 0.8, + "visible": false, + "inherit": true, + "active_if": { + "setting": "coasting_enable", + "value": true + } + } + } + }, + "coasting_speed": { + "label": "Coasting Speed", + "description": "The speed by which to move during coasting, relative to the speed of the extrusion path. A value slightly under 100% is advised, since during the coasting move, the pressure in the bowden tube drops.", + "unit": "%", + "type": "float", + "default": 90.0, + "visible": false, + "inherit": false, + "active_if": { + "setting": "coasting_enable", + "value": true + }, + "children": { + "coasting_speed_retract": { + "label": "Retract-Coasting Speed", + "description": "The speed by which to move during coasting before a retraction, relative to the speed of the extrusion path.", + "unit": "%", + "type": "float", + "default": 90.0, + "visible": false, + "inherit": true, + "active_if": { + "setting": "coasting_enable", + "value": true + } + }, + "coasting_speed_move": { + "label": "Move-Coasting Speed", + "description": "The speed by which to move during coasting before a travel move without retraction, relative to the speed of the extrusion path.", + "unit": "%", + "type": "float", + "default": 90.0, + "visible": false, + "inherit": true, + "active_if": { + "setting": "coasting_enable", + "value": true + } + } + } } } }, @@ -1101,7 +1265,7 @@ "icon": "category_blackmagic", "settings": { "magic_spiralize": { - "label": "Spiralize the Outer Contour", + "label": "Spiralize Outer Contour", "description": "Spiralize smooths out the Z move of the outer edge. This will create a steady Z increase over the whole print. This feature turns a solid object into a single walled print with a solid bottom. This feature used to be called ‘Joris’ in older versions.", "type": "boolean", "default": false, @@ -1133,7 +1297,11 @@ "type": "float", "default": 5.0, "visible": false, - "inherit":true + "inherit":true, + "active_if": { + "setting": "wireframe_enabled", + "value": true + } }, "wireframe_printspeed_up": { "label": "Wire Upward Printing Speed", @@ -1142,7 +1310,11 @@ "type": "float", "default": 5.0, "visible": false, - "inherit":true + "inherit":true, + "active_if": { + "setting": "wireframe_enabled", + "value": true + } }, "wireframe_printspeed_down": { "label": "Wire Downward Printing Speed", @@ -1151,7 +1323,11 @@ "type": "float", "default": 5.0, "visible": false, - "inherit":true + "inherit":true, + "active_if": { + "setting": "wireframe_enabled", + "value": true + } }, "wireframe_printspeed_flat": { "label": "Wire Horizontal Printing Speed", @@ -1160,7 +1336,11 @@ "type": "float", "default": 5.0, "visible": false, - "inherit":true + "inherit":true, + "active_if": { + "setting": "wireframe_enabled", + "value": true + } } } }, From d9ad54cd0d5a63545a97b29b332131e37b19b85e Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Mon, 8 Jun 2015 16:31:38 +0200 Subject: [PATCH 035/117] Rescale the current layer number based on the maximum layer This makes sure we still have the top layer selected when we receive new data. Contributes to Asana issue 34806173176751 --- plugins/LayerView/LayerView.py | 16 ++++++++++------ plugins/LayerView/LayerView.qml | 1 + 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/plugins/LayerView/LayerView.py b/plugins/LayerView/LayerView.py index a7caca6a4f..7f8c04986d 100644 --- a/plugins/LayerView/LayerView.py +++ b/plugins/LayerView/LayerView.py @@ -18,10 +18,10 @@ class LayerView(View): self._material = None self._num_layers = 0 self._layer_percentage = 0 # what percentage of layers need to be shown (SLider gives value between 0 - 100) - self._current_layer_num = 0 self._proxy = LayerViewProxy.LayerViewProxy() self._controller.getScene().sceneChanged.connect(self._onSceneChanged) self._max_layers = 10 + self._current_layer_num = 10 def getCurrentLayer(self): return self._current_layer_num @@ -87,7 +87,7 @@ class LayerView(View): renderer.setRenderSelection(False) self._old_max_layers = self._max_layers ## Recalculate num max layers - self._max_layers = 0 + new_max_layers = 0 for node in DepthFirstIterator(scene.getRoot()): if not node.render(renderer): if node.getMeshData() and node.isVisible(): @@ -95,11 +95,15 @@ class LayerView(View): layer_data = node.getMeshData().layerData except AttributeError: continue - if self._max_layers < len(layer_data.getLayers()): - self._max_layers = len(layer_data.getLayers()) - - if self._max_layers != self._old_max_layers: + if new_max_layers < len(layer_data.getLayers()): + new_max_layers = len(layer_data.getLayers()) + + if new_max_layers > 0 and new_max_layers != self._old_max_layers: + self._max_layers = new_max_layers self.maxLayersChanged.emit() + + # This makes sure we update the current layer + self.setLayer(int(self._max_layers * (self._current_layer_num / self._old_max_layers))) maxLayersChanged = Signal() diff --git a/plugins/LayerView/LayerView.qml b/plugins/LayerView/LayerView.qml index d8a95dff47..f1d78d78f9 100644 --- a/plugins/LayerView/LayerView.qml +++ b/plugins/LayerView/LayerView.qml @@ -21,6 +21,7 @@ Item orientation: Qt.Vertical minimumValue: 0; maximumValue: UM.LayerView.numLayers; + stepSize: 1 value: UM.LayerView.currentLayer onValueChanged: UM.LayerView.setCurrentLayer(value) From 478babff9a99ab647b2d85f0049b87b23d366468 Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Mon, 8 Jun 2015 16:40:09 +0200 Subject: [PATCH 036/117] Add a background to the tool additional controls This makes the actual text more visible and also makes the link between active tool and active tool controls more apparent. Fixes #21 --- resources/qml/Toolbar.qml | 44 +++++++++++++++++++++++++++----- resources/themes/cura/theme.json | 4 ++- 2 files changed, 41 insertions(+), 7 deletions(-) diff --git a/resources/qml/Toolbar.qml b/resources/qml/Toolbar.qml index e24d929ed7..e7cb0788c5 100644 --- a/resources/qml/Toolbar.qml +++ b/resources/qml/Toolbar.qml @@ -14,6 +14,23 @@ Item { width: buttons.width; height: buttons.height + panel.height; + Rectangle { + id: activeItemBackground; + + anchors.bottom: parent.bottom; + + width: UM.Theme.sizes.button.width; + height: UM.Theme.sizes.button.height * 2; + + opacity: panelBackground.opacity; + + color: UM.Theme.colors.tool_panel_background + + function setActive(new_x) { + x = new_x; + } + } + RowLayout { id: buttons; @@ -34,6 +51,7 @@ Item { checkable: true; checked: model.active; + onCheckedChanged: if (checked) activeItemBackground.setActive(x); style: UM.Theme.styles.tool_button; @@ -42,21 +60,35 @@ Item { MouseArea { anchors.fill: parent; onClicked: parent.checked ? UM.Controller.setActiveTool(null) : UM.Controller.setActiveTool(model.id); + } - } + } } } - Loader { - id: panel + UM.AngledCornerRectangle { + id: panelBackground; anchors.left: parent.left; - anchors.right: parent.right; anchors.bottom: buttons.top; anchors.bottomMargin: UM.Theme.sizes.default_margin.height; - height: childrenRect.height; + width: panel.item ? panel.width + 2 * UM.Theme.sizes.default_margin.width : 0; + height: panel.item ? panel.height + 2 * UM.Theme.sizes.default_margin.height : 0; - source: UM.ActiveTool.valid ? UM.ActiveTool.activeToolPanel : ""; + opacity: panel.item ? 1 : 0 + Behavior on opacity { NumberAnimation { duration: 100 } } + + color: UM.Theme.colors.tool_panel_background; + cornerSize: width > 0 ? UM.Theme.sizes.default_margin.width : 0; + + Loader { + id: panel + + x: UM.Theme.sizes.default_margin.width; + y: UM.Theme.sizes.default_margin.height; + + source: UM.ActiveTool.valid ? UM.ActiveTool.activeToolPanel : ""; + } } } diff --git a/resources/themes/cura/theme.json b/resources/themes/cura/theme.json index a79636aece..eda2badd71 100644 --- a/resources/themes/cura/theme.json +++ b/resources/themes/cura/theme.json @@ -110,7 +110,9 @@ "save_button_text": [35, 35, 35, 255], "message": [205, 202, 201, 255], - "message_text": [35, 35, 35, 255] + "message_text": [35, 35, 35, 255], + + "tool_panel_background": [255, 255, 255, 255] }, "sizes": { From 56454a9c7a894f3c2c7acc08e5347f0bdad008b4 Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Mon, 8 Jun 2015 17:50:29 +0200 Subject: [PATCH 037/117] Add support for listing recent files Fixes Asana issue 33694049548880 --- cura/CuraApplication.py | 32 ++++++++++++++++++++++++++++++++ resources/qml/Cura.qml | 14 ++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 55e1e466a4..7668a62dae 100644 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -17,6 +17,7 @@ from UM.Logger import Logger from UM.Preferences import Preferences from UM.Message import Message from UM.PluginRegistry import PluginRegistry +from UM.JobQueue import JobQueue from UM.Scene.BoxRenderer import BoxRenderer from UM.Scene.Selection import Selection @@ -71,6 +72,17 @@ class CuraApplication(QtApplication): Preferences.getInstance().addPreference("cura/active_machine", "") Preferences.getInstance().addPreference("cura/active_mode", "simple") + Preferences.getInstance().addPreference("cura/recent_files", "") + + JobQueue.getInstance().jobFinished.connect(self._onJobFinished) + + self._recent_files = [] + files = Preferences.getInstance().getValue("cura/recent_files").split(";") + for f in files: + if not os.path.isfile(f): + continue + + self._recent_files.append(f) ## Handle loading of all plugin types (and the backend explicitly) # \sa PluginRegistery @@ -305,6 +317,11 @@ class CuraApplication(QtApplication): return log + recentFilesChanged = pyqtSignal() + @pyqtProperty("QStringList", notify = recentFilesChanged) + def recentFiles(self): + return self._recent_files + outputDevicesChanged = pyqtSignal() @pyqtProperty("QVariantMap", notify = outputDevicesChanged) @@ -460,3 +477,18 @@ class CuraApplication(QtApplication): op = AddSceneNodeOperation(node, self.getController().getScene().getRoot()) op.push() + + def _onJobFinished(self, job): + if type(job) is not ReadMeshJob: + return + + f = job.getFileName() + if f in self._recent_files: + self._recent_files.remove(f) + + self._recent_files.insert(0, f) + if len(self._recent_files) > 10: + del self._recent_files[10] + + Preferences.getInstance().setValue("cura/recent_files", ";".join(self._recent_files)) + self.recentFilesChanged.emit() diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index a2eeba59cd..e0caf4d83d 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -25,6 +25,7 @@ UM.MainWindow { window: base Menu { + id: fileMenu //: File menu title: qsTr("&File"); @@ -33,6 +34,19 @@ UM.MainWindow { MenuSeparator { } + Instantiator { + model: Printer.recentFiles + MenuItem { + property url filePath: modelData; + text: (index + 1) + ". " + modelData.slice(modelData.lastIndexOf("/") + 1); + onTriggered: UM.MeshFileHandler.readLocalFile(filePath); + } + onObjectAdded: fileMenu.insertItem(index, object) + onObjectRemoved: fileMenu.removeItem(object) + } + + MenuSeparator { } + MenuItem { action: actions.quit; } } From fc33b340607fd0cf99db82ba722bc5531fd30b6e Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Tue, 9 Jun 2015 13:38:23 +0200 Subject: [PATCH 038/117] Save the state of collapsed/expanded categories Fixes Asana issue 36436828173802 --- cura/CuraApplication.py | 15 +++++++++++++++ resources/qml/SidebarAdvanced.qml | 9 ++++++++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 7668a62dae..43ce586e74 100644 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -73,6 +73,7 @@ class CuraApplication(QtApplication): Preferences.getInstance().addPreference("cura/active_machine", "") Preferences.getInstance().addPreference("cura/active_mode", "simple") Preferences.getInstance().addPreference("cura/recent_files", "") + Preferences.getInstance().addPreference("cura/categories_expanded", "") JobQueue.getInstance().jobFinished.connect(self._onJobFinished) @@ -322,6 +323,20 @@ class CuraApplication(QtApplication): def recentFiles(self): return self._recent_files + @pyqtSlot("QStringList") + def setExpandedCategories(self, categories): + categories = list(set(categories)) + categories.sort() + joined = ";".join(categories) + if joined != Preferences.getInstance().getValue("cura/categories_expanded"): + Preferences.getInstance().setValue("cura/categories_expanded", joined) + self.expandedCategoriesChanged.emit() + + expandedCategoriesChanged = pyqtSignal() + @pyqtProperty("QStringList", notify = expandedCategoriesChanged) + def expandedCategories(self): + return Preferences.getInstance().getValue("cura/categories_expanded").split(";") + outputDevicesChanged = pyqtSignal() @pyqtProperty("QVariantMap", notify = outputDevicesChanged) diff --git a/resources/qml/SidebarAdvanced.qml b/resources/qml/SidebarAdvanced.qml index 7d0e391768..8a231aa53d 100644 --- a/resources/qml/SidebarAdvanced.qml +++ b/resources/qml/SidebarAdvanced.qml @@ -1,6 +1,13 @@ // Copyright (c) 2015 Ultimaker B.V. // Cura is released under the terms of the AGPLv3 or higher. +import QtQuick 2.0 + +import QtQuick.Controls 1.2 + import UM 1.0 as UM -UM.SettingView { } +UM.SettingView { + expandedCategories: Printer.expandedCategories; + onExpandedCategoriesChanged: Printer.setExpandedCategories(expandedCategories); +} From 5019a157a1a31b2c999cfd75f510785b74e47dd0 Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Tue, 9 Jun 2015 05:22:28 -0700 Subject: [PATCH 039/117] Bump version to .95 --- cura/CuraApplication.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 43ce586e74..5135a84471 100644 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -49,7 +49,7 @@ class CuraApplication(QtApplication): if not hasattr(sys, "frozen"): Resources.addResourcePath(os.path.join(os.path.abspath(os.path.dirname(__file__)), "..")) - super().__init__(name = "cura", version = "15.05.93") + super().__init__(name = "cura", version = "15.05.94") self.setRequiredPlugins([ "CuraEngineBackend", From da6b7c272cebe17a2b2045106eef664416b116ed Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Tue, 9 Jun 2015 05:23:37 -0700 Subject: [PATCH 040/117] Include VCRedist and add an option to run it during install Fixes Asana issue 34145139570660 --- installer.nsi | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/installer.nsi b/installer.nsi index 5bd18a929b..1259aeba7a 100644 --- a/installer.nsi +++ b/installer.nsi @@ -99,6 +99,15 @@ Function LaunchLink Exec '"$WINDIR\explorer.exe" "$SMPROGRAMS\Cura ${VERSION}\Cura ${VERSION}.lnk"' FunctionEnd +Section "Install Visual Studio 2010 Redistributable" + SetOutPath "$INSTDIR" + File "vcredist_2010_x86.exe" + + IfSilent +2 + ExecWait '"$INSTDIR\vcredist_2010_x86.exe"' + +SectionEnd + ;Section "Install Arduino Drivers" ; ; Set output path to the driver directory. ; SetOutPath "$INSTDIR\drivers\" From aef2821464ba50958a323663cbfdd02e7f463cc2 Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Tue, 9 Jun 2015 14:37:04 +0200 Subject: [PATCH 041/117] Update CHANGES file with changes since .93 --- CHANGES | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index e62253066b..f33001afd5 100644 --- a/CHANGES +++ b/CHANGES @@ -7,6 +7,24 @@ Cura 15.06 is a new release built from the ground up on a completely new framework called Uranium. This framework has been designed to make it easier to extend Cura with additional functionality as well as provide a cleaner UI. +Changes since 15.05.93 +---------------------- + +* Fixed: No shortcuts for moving up/down layers in layer view. +* Fixed: Last view layers could not be scrolled through in layer view. +* Fixed: Files provided on command line would not actually show up on the build + platform. +* Fixed: Render a ghost of the selection in Layer view to make the actual object + position clear. +* Fixed: Showing a menu would clear the selection. +* Fixed: Size and scaling factor display for scale tool. +* Fixed: Missing background for additional tool controls. +* Fixed: Loading message times out when loading large files. +* Fixed: Show recent files in the file menu. +* Fixed: Windows installer will now install MSVC 2010 redistributable, to + prevent issues with missing DLL's. +* Fixed: Collapsed/expanded state of setting categories not stored. + Changes since 15.05.91 ---------------------- @@ -26,7 +44,8 @@ Changes since 15.05.91 * Fixed: Camera panning now works correctly instead of doing nothing. * Fixed: Camera would flip around center point at maximum rotation. * Fixed: Build platform grid blocked view from below objects. -* Fixed: Viewport on MacOSX with high-DPI screens was only taking 1/4th of the window +* Fixed: Viewport on MacOSX with high-DPI screens was only taking 1/4th of the +window Changes since 15.05.90 ---------------------- From 74cf0274214797bdfe5e1a1ddfeb25c37353249f Mon Sep 17 00:00:00 2001 From: Tamara Hogenhout Date: Wed, 10 Jun 2015 14:40:46 +0200 Subject: [PATCH 042/117] Fix text wrapping and several other style issues of the About dialog Contributes to Ultimaker/Uranium#43 --- resources/qml/AboutDialog.qml | 72 ++++++++++++++++++----------------- 1 file changed, 38 insertions(+), 34 deletions(-) diff --git a/resources/qml/AboutDialog.qml b/resources/qml/AboutDialog.qml index df2ecf4172..1ed9f5dd32 100644 --- a/resources/qml/AboutDialog.qml +++ b/resources/qml/AboutDialog.qml @@ -3,7 +3,6 @@ import QtQuick 2.2 import QtQuick.Controls 1.1 -import QtQuick.Layouts 1.1 import QtQuick.Window 2.1 import UM 1.0 as UM @@ -12,48 +11,53 @@ UM.Dialog { id: base //: About dialog title - title: qsTr("About Cura"); + title: qsTr("About Cura") + minimumWidth: 400 + minimumHeight: 300 - ColumnLayout { - anchors.fill: parent; + Image { + id: logo + width: parent.width * 0.75 + height: width * (1/4.25) - Item { - Layout.fillWidth: true; - Layout.fillHeight: true; - } + source: UM.Theme.images.logo - Image { - Layout.alignment: Qt.AlignHCenter; - Layout.preferredWidth: parent.width * 0.75; - Layout.preferredHeight: width * (1/4.25); + sourceSize.width: width + sourceSize.height: height + anchors.centerIn: parent + anchors.verticalCenterOffset : -(height * 0.5) + } - source: UM.Theme.images.logo; + Label { + id: version - sourceSize.width: width; - sourceSize.height: height; - } + text: "Cura 15.06 Beta" + font: UM.Theme.fonts.large + anchors.horizontalCenter : logo.horizontalCenter + anchors.horizontalCenterOffset : (logo.width * 0.25) + anchors.top: logo.bottom + anchors.topMargin : 5 + } - Label { - Layout.alignment: Qt.AlignHCenter; + Label { + id: description + width: parent.width - text: "Cura 15.06 Beta"; - font: UM.Theme.fonts.large; - } + //: About dialog application description + text: qsTr("End-to-end solution for fused filament 3D printing.") + wrapMode: Text.WordWrap + anchors.top: version.bottom + anchors.topMargin : 10 + } - Label { - //: About dialog application description - text: qsTr("End-to-end solution for fused filament 3D printing.") - } + Label { + id: author_note + width: parent.width - Label { - //: About dialog application author note - text: qsTr("Cura has been developed by Ultimaker B.V. in cooperation with the community.") - } - - Item { - Layout.fillWidth: true; - Layout.fillHeight: true; - } + //: About dialog application author note + text: qsTr("Cura has been developed by Ultimaker B.V. in cooperation with the community.") + wrapMode: Text.WordWrap + anchors.top: description.bottom } rightButtons: Button { From 5f24b70453d11fa17f6b746afe425d6841c37dd8 Mon Sep 17 00:00:00 2001 From: Tamara Hogenhout Date: Wed, 10 Jun 2015 14:57:37 +0200 Subject: [PATCH 043/117] Highlight Open button when no files are loaded This helps with first-run to make it clear where to start. Fixes #37 --- resources/qml/Cura.qml | 3 +- resources/themes/cura/styles.qml | 49 ++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index e0caf4d83d..8c5c43d83c 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -192,7 +192,7 @@ UM.MainWindow { id: openFileButton; iconSource: UM.Theme.icons.open; - style: UM.Theme.styles.tool_button; + style: UM.Backend.progress < 0 ? UM.Theme.styles.open_file_button : UM.Theme.styles.tool_button; anchors { top: parent.top; @@ -433,3 +433,4 @@ UM.MainWindow { Component.onCompleted: UM.Theme.load(UM.Resources.getPath(UM.Resources.ThemesLocation, "cura")) } + diff --git a/resources/themes/cura/styles.qml b/resources/themes/cura/styles.qml index b7db374da1..e6c08b381c 100644 --- a/resources/themes/cura/styles.qml +++ b/resources/themes/cura/styles.qml @@ -35,6 +35,55 @@ QtObject { } } + property Component open_file_button: Component { + ButtonStyle { + background: UM.AngledCornerRectangle { + implicitWidth: UM.Theme.sizes.button.width; + implicitHeight: UM.Theme.sizes.button.height; + color: { + if(control.hovered) { + return UM.Theme.colors.button_active_hover; + } else { + return UM.Theme.colors.button_active; + } + } + Behavior on color { ColorAnimation { duration: 50; } } + cornerSize: UM.Theme.sizes.default_margin.width; + + Rectangle { + anchors.bottom: parent.top; + + width: parent.width; + height: control.hovered ? label.height : 0; + Behavior on height { NumberAnimation { duration: 75; } } + + opacity: control.hovered ? 1.0 : 0.0; + Behavior on opacity { NumberAnimation { duration: 75; } } + + Label { + id: label + anchors.horizontalCenter: parent.horizontalCenter; + text: control.text; + font: UM.Theme.fonts.button_tooltip; + color: UM.Theme.colors.button_tooltip_text; + } + } + } + + label: Item { + Image { + anchors.centerIn: parent; + + source: control.iconSource; + width: UM.Theme.sizes.button_icon.width; + height: UM.Theme.sizes.button_icon.height; + + sourceSize: UM.Theme.sizes.button_icon; + } + } + } + } + property Component tool_button: Component { ButtonStyle { background: UM.AngledCornerRectangle { From 9d50b1fb26090237f5f38b30f7f028dd3c4c730b Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Wed, 10 Jun 2015 15:32:57 +0200 Subject: [PATCH 044/117] Correct name of UM Original+ --- resources/settings/ultimaker_original_plus.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/settings/ultimaker_original_plus.json b/resources/settings/ultimaker_original_plus.json index d0b04879db..9e5cf6370d 100644 --- a/resources/settings/ultimaker_original_plus.json +++ b/resources/settings/ultimaker_original_plus.json @@ -1,6 +1,6 @@ { "id": "ultimaker_original_plus", - "name": "Ultimaker Original Plus", + "name": "Ultimaker Original+", "icon": "icon_ultimaker.png", "platform": "ultimaker2_platform.obj", "platform_texture": "UltimakerPlusbackplate.png", From fa671225c829f7acc1efeac74d08535a4e7ccb06 Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Wed, 10 Jun 2015 15:36:15 +0200 Subject: [PATCH 045/117] Update tool button styling for improved clarity Fixes #35 --- resources/themes/cura/styles.qml | 53 ++++++++++++++++++-------------- resources/themes/cura/theme.json | 6 ++-- 2 files changed, 33 insertions(+), 26 deletions(-) diff --git a/resources/themes/cura/styles.qml b/resources/themes/cura/styles.qml index e6c08b381c..3ca9ea6ea2 100644 --- a/resources/themes/cura/styles.qml +++ b/resources/themes/cura/styles.qml @@ -86,45 +86,52 @@ QtObject { property Component tool_button: Component { ButtonStyle { - background: UM.AngledCornerRectangle { - property bool down: control.pressed || (control.checkable && control.checked); - + background: Item { implicitWidth: UM.Theme.sizes.button.width; implicitHeight: UM.Theme.sizes.button.height; - color: { - if(!control.enabled) { - return UM.Theme.colors.button_disabled; - } else if(control.checkable && control.checked && control.hovered) { - return UM.Theme.colors.button_active_hover; - } else if(control.pressed || (control.checkable && control.checked)) { - return UM.Theme.colors.button_active; - } else if(control.hovered) { - return UM.Theme.colors.button_hover; - } else { - return UM.Theme.colors.button; - } - } - Behavior on color { ColorAnimation { duration: 50; } } - cornerSize: UM.Theme.sizes.default_margin.width; Rectangle { - anchors.bottom: parent.top; + anchors.bottom: parent.verticalCenter; width: parent.width; - height: control.hovered ? label.height : 0; - Behavior on height { NumberAnimation { duration: 75; } } + height: control.hovered ? parent.height / 2 + label.height : 0; + Behavior on height { NumberAnimation { duration: 100; } } opacity: control.hovered ? 1.0 : 0.0; - Behavior on opacity { NumberAnimation { duration: 75; } } + Behavior on opacity { NumberAnimation { duration: 100; } } Label { id: label anchors.horizontalCenter: parent.horizontalCenter; - text: control.text; + text: control.text.replace("&", ""); font: UM.Theme.fonts.button_tooltip; color: UM.Theme.colors.button_tooltip_text; } } + + UM.AngledCornerRectangle { + id: buttonFace; + + anchors.fill: parent; + + property bool down: control.pressed || (control.checkable && control.checked); + + color: { + if(!control.enabled) { + return UM.Theme.colors.button_disabled; + } else if(control.checkable && control.checked && control.hovered) { + return UM.Theme.colors.button_active_hover; + } else if(control.pressed || (control.checkable && control.checked)) { + return UM.Theme.colors.button_active; + } else if(control.hovered) { + return UM.Theme.colors.button_hover; + } else { + return UM.Theme.colors.button; + } + } + Behavior on color { ColorAnimation { duration: 50; } } + cornerSize: UM.Theme.sizes.default_margin.width; + } } label: Item { diff --git a/resources/themes/cura/theme.json b/resources/themes/cura/theme.json index eda2badd71..a99fcc7f55 100644 --- a/resources/themes/cura/theme.json +++ b/resources/themes/cura/theme.json @@ -57,10 +57,10 @@ "text_hover": [35, 35, 35, 255], "text_pressed": [12, 169, 227, 255], - "button": [205, 202, 201, 255], - "button_hover": [174, 174, 174, 255], + "button": [160, 163, 171, 255], + "button_hover": [140, 144, 154, 255], "button_active": [12, 169, 227, 255], - "button_active_hover": [34, 150, 190, 255], + "button_active_hover": [34, 150, 199, 255], "button_text": [255, 255, 255, 255], "button_disabled": [245, 245, 245, 255], "button_tooltip_text": [35, 35, 35, 255], From 9f1b6fc496118d208b59c2b959623cf2af6fd08c Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Wed, 10 Jun 2015 15:41:21 +0200 Subject: [PATCH 046/117] Use a darker text color for improved clarity Contributes to Asana Readability issue --- resources/themes/cura/theme.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/resources/themes/cura/theme.json b/resources/themes/cura/theme.json index a99fcc7f55..caf6dc5358 100644 --- a/resources/themes/cura/theme.json +++ b/resources/themes/cura/theme.json @@ -52,8 +52,8 @@ "border": [205, 202, 201, 255], "secondary": [205, 202, 201, 255], - "text": [174, 174, 174, 255], - "text_inactive": [205, 202, 201, 255], + "text": [140, 144, 154, 255], + "text_inactive": [174, 174, 174, 255], "text_hover": [35, 35, 35, 255], "text_pressed": [12, 169, 227, 255], @@ -77,7 +77,7 @@ "setting_category_active_hover": [34, 150, 190, 255], "setting_category_text": [255, 255, 255, 255], - "setting_label": [174, 174, 174, 255], + "setting_label": [140, 144, 154, 255], "setting_control": [255, 255, 255, 255], "setting_control_highlight": [245, 245, 245, 255], "setting_control_border": [174, 174, 174, 255], @@ -98,7 +98,7 @@ "checkbox_hover": [245, 245, 245, 255], "checkbox_border": [174, 174, 174, 255], "checkbox_mark": [35, 35, 35, 255], - "checkbox_text": [174, 174, 174, 255], + "checkbox_text": [140, 144, 154, 255], "tooltip": [255, 225, 146, 255], From ff058bbfae0f11f0fb453e08b56984b9773c5db2 Mon Sep 17 00:00:00 2001 From: Tamara Hogenhout Date: Thu, 11 Jun 2015 13:42:49 +0200 Subject: [PATCH 047/117] Changes icon size (within the buttons) to improve clarity Contributes to #43 --- resources/themes/cura/theme.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/themes/cura/theme.json b/resources/themes/cura/theme.json index eda2badd71..bfe36b5d97 100644 --- a/resources/themes/cura/theme.json +++ b/resources/themes/cura/theme.json @@ -133,7 +133,7 @@ "setting_unit_margin": [0.5, 0.5], "button": [4.25, 4.25], - "button_icon": [3.57, 3.57], + "button_icon": [2.9, 2.9], "scrollbar": [0.5, 0.5], From 45143107ca3dd663f385ed5084691dd66193f568 Mon Sep 17 00:00:00 2001 From: Tim Kuipers Date: Thu, 11 Jun 2015 13:57:56 +0200 Subject: [PATCH 048/117] skin alternate rotation setting --- resources/settings/fdmprinter.json | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/resources/settings/fdmprinter.json b/resources/settings/fdmprinter.json index ea4c59ed7c..1565a1cc07 100644 --- a/resources/settings/fdmprinter.json +++ b/resources/settings/fdmprinter.json @@ -254,6 +254,13 @@ "default": "Lines", "visible": false }, + "skin_alternate_rotation": { + "label": "Alternate Skin Rotation", + "description": "Alternate between diagonal skin fill and horizontal + vertical skin fill. Although the diagonal directions can print quicker, this option can improve on the printing quality by reducing the pillowing effect.", + "type": "boolean", + "default": false, + "visible": false + }, "skin_outline_count": { "label": "Skin Perimeter Line Count", "description": "Number of lines around skin regions. Using one or two skin perimeter lines can greatly improve on roofs which would start in the middle of infill cells.", From 7e68a71ca32e5baf754027ab57e73c6fe84899a1 Mon Sep 17 00:00:00 2001 From: Tim Kuipers Date: Thu, 11 Jun 2015 14:33:10 +0200 Subject: [PATCH 049/117] magic mesh surface mode setting --- resources/settings/fdmprinter.json | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/resources/settings/fdmprinter.json b/resources/settings/fdmprinter.json index 1565a1cc07..e088dd8eb9 100644 --- a/resources/settings/fdmprinter.json +++ b/resources/settings/fdmprinter.json @@ -1273,6 +1273,13 @@ "visible": true, "icon": "category_blackmagic", "settings": { + "magic_mesh_surface_mode": { + "label": "Surface Mode", + "description": "Print the surface instead of the volume. No infill, no top/bottom skin, just a single wall of which the middle coincides with the surface of the mesh.", + "type": "boolean", + "default": false, + "visible": false + }, "magic_spiralize": { "label": "Spiralize Outer Contour", "description": "Spiralize smooths out the Z move of the outer edge. This will create a steady Z increase over the whole print. This feature turns a solid object into a single walled print with a solid bottom. This feature used to be called ‘Joris’ in older versions.", From 4d1fdc92b31306ff659851b25396e29ae459057e Mon Sep 17 00:00:00 2001 From: Tamara Hogenhout Date: Thu, 11 Jun 2015 14:41:52 +0200 Subject: [PATCH 050/117] Removes the default tooltips of the buttons to improve clarity Put the automatically added tooltips back to the default value (empty qSt) so the user doensn't sees two 'tooltips' Contributes to #43 --- resources/qml/Cura.qml | 4 ++-- resources/qml/Toolbar.qml | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index 8c5c43d83c..7067c88c12 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -193,7 +193,7 @@ UM.MainWindow { iconSource: UM.Theme.icons.open; style: UM.Backend.progress < 0 ? UM.Theme.styles.open_file_button : UM.Theme.styles.tool_button; - + tooltip: ''; anchors { top: parent.top; topMargin: UM.Theme.sizes.window_margin.height; @@ -232,7 +232,7 @@ UM.MainWindow { iconSource: UM.Theme.icons.viewmode; style: UM.Theme.styles.tool_button; - + tooltip: ''; menu: Menu { id: viewMenu; Instantiator { diff --git a/resources/qml/Toolbar.qml b/resources/qml/Toolbar.qml index e7cb0788c5..d5126db481 100644 --- a/resources/qml/Toolbar.qml +++ b/resources/qml/Toolbar.qml @@ -47,7 +47,6 @@ Item { Button { text: model.name; iconSource: UM.Theme.icons[model.icon]; - tooltip: model.description; checkable: true; checked: model.active; From 62558f47fafb9d783388238011f8fca938ffe145 Mon Sep 17 00:00:00 2001 From: Tamara Hogenhout Date: Thu, 11 Jun 2015 15:56:09 +0200 Subject: [PATCH 051/117] makes the tooltip/label appear behind the button. Contributes to #43 --- resources/themes/cura/styles.qml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/resources/themes/cura/styles.qml b/resources/themes/cura/styles.qml index e6c08b381c..cd66a79257 100644 --- a/resources/themes/cura/styles.qml +++ b/resources/themes/cura/styles.qml @@ -51,14 +51,15 @@ QtObject { cornerSize: UM.Theme.sizes.default_margin.width; Rectangle { - anchors.bottom: parent.top; + anchors.verticalCenter: parent.top; width: parent.width; - height: control.hovered ? label.height : 0; + height: control.hovered ? (label.height + 20) : 0; Behavior on height { NumberAnimation { duration: 75; } } opacity: control.hovered ? 1.0 : 0.0; Behavior on opacity { NumberAnimation { duration: 75; } } + z: -1; Label { id: label @@ -106,16 +107,16 @@ QtObject { } Behavior on color { ColorAnimation { duration: 50; } } cornerSize: UM.Theme.sizes.default_margin.width; - Rectangle { - anchors.bottom: parent.top; + anchors.verticalCenter: parent.top; width: parent.width; - height: control.hovered ? label.height : 0; + height: control.hovered ? (label.height + 20) : 0; Behavior on height { NumberAnimation { duration: 75; } } opacity: control.hovered ? 1.0 : 0.0; Behavior on opacity { NumberAnimation { duration: 75; } } + z: -1; Label { id: label From 0b9986d34b0c6257e8a5db86c3407c4dd94ae871 Mon Sep 17 00:00:00 2001 From: Tamara Hogenhout Date: Thu, 11 Jun 2015 13:42:49 +0200 Subject: [PATCH 052/117] Changes icon size (within the buttons) to improve clarity Contributes to #43 --- resources/themes/cura/theme.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/themes/cura/theme.json b/resources/themes/cura/theme.json index caf6dc5358..9e5911e4e0 100644 --- a/resources/themes/cura/theme.json +++ b/resources/themes/cura/theme.json @@ -133,7 +133,7 @@ "setting_unit_margin": [0.5, 0.5], "button": [4.25, 4.25], - "button_icon": [3.57, 3.57], + "button_icon": [2.9, 2.9], "scrollbar": [0.5, 0.5], From 4bd5aeda6a2acc2f74288c2321d3a0f87d264650 Mon Sep 17 00:00:00 2001 From: Tamara Hogenhout Date: Thu, 11 Jun 2015 14:41:52 +0200 Subject: [PATCH 053/117] Removes the default tooltips of the buttons to improve clarity Put the automatically added tooltips back to the default value (empty qSt) so the user doensn't sees two 'tooltips' Contributes to #43 --- resources/qml/Cura.qml | 4 ++-- resources/qml/Toolbar.qml | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index 8c5c43d83c..7067c88c12 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -193,7 +193,7 @@ UM.MainWindow { iconSource: UM.Theme.icons.open; style: UM.Backend.progress < 0 ? UM.Theme.styles.open_file_button : UM.Theme.styles.tool_button; - + tooltip: ''; anchors { top: parent.top; topMargin: UM.Theme.sizes.window_margin.height; @@ -232,7 +232,7 @@ UM.MainWindow { iconSource: UM.Theme.icons.viewmode; style: UM.Theme.styles.tool_button; - + tooltip: ''; menu: Menu { id: viewMenu; Instantiator { diff --git a/resources/qml/Toolbar.qml b/resources/qml/Toolbar.qml index e7cb0788c5..d5126db481 100644 --- a/resources/qml/Toolbar.qml +++ b/resources/qml/Toolbar.qml @@ -47,7 +47,6 @@ Item { Button { text: model.name; iconSource: UM.Theme.icons[model.icon]; - tooltip: model.description; checkable: true; checked: model.active; From aa0c18118b0d258b43cb617a206ce57101aa88ea Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Fri, 12 Jun 2015 12:19:23 +0200 Subject: [PATCH 054/117] Revert "makes the tooltip/label appear behind the button." This reverts commit 62558f47fafb9d783388238011f8fca938ffe145. --- resources/themes/cura/styles.qml | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/resources/themes/cura/styles.qml b/resources/themes/cura/styles.qml index cd66a79257..e6c08b381c 100644 --- a/resources/themes/cura/styles.qml +++ b/resources/themes/cura/styles.qml @@ -51,15 +51,14 @@ QtObject { cornerSize: UM.Theme.sizes.default_margin.width; Rectangle { - anchors.verticalCenter: parent.top; + anchors.bottom: parent.top; width: parent.width; - height: control.hovered ? (label.height + 20) : 0; + height: control.hovered ? label.height : 0; Behavior on height { NumberAnimation { duration: 75; } } opacity: control.hovered ? 1.0 : 0.0; Behavior on opacity { NumberAnimation { duration: 75; } } - z: -1; Label { id: label @@ -107,16 +106,16 @@ QtObject { } Behavior on color { ColorAnimation { duration: 50; } } cornerSize: UM.Theme.sizes.default_margin.width; + Rectangle { - anchors.verticalCenter: parent.top; + anchors.bottom: parent.top; width: parent.width; - height: control.hovered ? (label.height + 20) : 0; + height: control.hovered ? label.height : 0; Behavior on height { NumberAnimation { duration: 75; } } opacity: control.hovered ? 1.0 : 0.0; Behavior on opacity { NumberAnimation { duration: 75; } } - z: -1; Label { id: label From 8e6ab3a10b526b6b6767af712448eb95cd5ac0f7 Mon Sep 17 00:00:00 2001 From: derekhe Date: Sat, 13 Jun 2015 14:31:37 +0800 Subject: [PATCH 055/117] Add new cura description. Add build scripts url --- README.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index f311976ca1..acd87f1b0e 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,9 @@ Cura ==== -This is the source code of Cura. +This is the new, shiny, unreleased frontend for Cura. [daid/Cura](https://github.com/daid/Cura.git) is the old legacy Cura that everyone knows and loves/hates. + +We re-worked the whole GUI code at Ultimaker, because my old code started to become an unmaintainable ball of poo. Dependencies ------------ @@ -12,3 +14,8 @@ Dependencies This will be needed at runtime to perform the actual slicing. * PySerial Only required for USB printing support. + +Build scripts +------------- + +Please checkout [cura-build](https://github.com/Ultimaker/cura-build) From 6add467477dbc50d6bd9acb62195316f36d68e7f Mon Sep 17 00:00:00 2001 From: Tamara Hogenhout Date: Mon, 15 Jun 2015 13:59:17 +0200 Subject: [PATCH 056/117] Makes the openfile button blue/active as long as there is no file loaded. This is to make the UI more intuitive. Contributes to #43 --- resources/themes/cura/styles.qml | 39 +++++++++++++++++--------------- 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/resources/themes/cura/styles.qml b/resources/themes/cura/styles.qml index 3ca9ea6ea2..3c2a392b8d 100644 --- a/resources/themes/cura/styles.qml +++ b/resources/themes/cura/styles.qml @@ -35,39 +35,42 @@ QtObject { } } - property Component open_file_button: Component { + property Component open_file_button: Component { ButtonStyle { - background: UM.AngledCornerRectangle { + background: Item { implicitWidth: UM.Theme.sizes.button.width; implicitHeight: UM.Theme.sizes.button.height; - color: { - if(control.hovered) { - return UM.Theme.colors.button_active_hover; - } else { - return UM.Theme.colors.button_active; - } - } - Behavior on color { ColorAnimation { duration: 50; } } - cornerSize: UM.Theme.sizes.default_margin.width; Rectangle { - anchors.bottom: parent.top; - + anchors.bottom: parent.verticalCenter; width: parent.width; - height: control.hovered ? label.height : 0; - Behavior on height { NumberAnimation { duration: 75; } } + height: control.hovered ? parent.height / 2 + label.height : 0; + Behavior on height { NumberAnimation { duration: 100; } } opacity: control.hovered ? 1.0 : 0.0; - Behavior on opacity { NumberAnimation { duration: 75; } } + Behavior on opacity { NumberAnimation { duration: 100; } } Label { - id: label + id: label; anchors.horizontalCenter: parent.horizontalCenter; - text: control.text; + text: control.text.replace("&", ""); font: UM.Theme.fonts.button_tooltip; color: UM.Theme.colors.button_tooltip_text; } } + + UM.AngledCornerRectangle { + anchors.fill: parent; + color: { + if(control.hovered) { + return UM.Theme.colors.button_active_hover; + } else { + return UM.Theme.colors.button_active; + } + } + Behavior on color { ColorAnimation { duration: 50; } } + cornerSize: UM.Theme.sizes.default_margin.width; + } } label: Item { From c11c80fa2461ea07925770cd1e437cf6fd043473 Mon Sep 17 00:00:00 2001 From: Tamara Hogenhout Date: Mon, 15 Jun 2015 16:44:43 +0200 Subject: [PATCH 057/117] Defines the style of the progressbar Commits to #44 --- resources/themes/cura/styles.qml | 43 ++++++++++++++++++++++++++++++++ resources/themes/cura/theme.json | 7 ++++++ 2 files changed, 50 insertions(+) diff --git a/resources/themes/cura/styles.qml b/resources/themes/cura/styles.qml index 3c2a392b8d..43bbb8c400 100644 --- a/resources/themes/cura/styles.qml +++ b/resources/themes/cura/styles.qml @@ -151,6 +151,49 @@ QtObject { } } + + property Component progressbar: Component{ + ProgressBarStyle { + background: UM.AngledCornerRectangle { + anchors.fill: parent + anchors.left: parent.left + implicitWidth: UM.Theme.sizes.progressbar.width + implicitHeight: UM.Theme.sizes.progressbar.height + color: "transparent" + } + progress: UM.AngledCornerRectangle { + anchors.left: parent.left + anchors.fill: parent + cornerSize: UM.Theme.sizes.progressbar_control.height + color: UM.Theme.colors.progressbar_background + Item { + anchors.fill: parent + anchors.margins: UM.Theme.sizes.progressbar_margin.width + visible: control.indeterminate + Row { + Repeater { + UM.AngledCornerRectangle { + cornerSize: UM.Theme.sizes.progressbar_control.height + color: UM.Theme.colors.progressbar_control + width: UM.Theme.sizes.progressbar_control.width + height: UM.Theme.sizes.progressbar_control.height + } + model: 1 + } + SequentialAnimation on x { + id: xAnim + property int animEndPoint: UM.Theme.sizes.progressbar.width - UM.Theme.sizes.progressbar_control.width + running: control.indeterminate + loops: Animation.Infinite + NumberAnimation { from: 0; to: xAnim.animEndPoint; duration: 2000;} + NumberAnimation { from: xAnim.animEndPoint; to: 0; duration: 2000;} + } + } + } + } + } + } + property Component sidebar_category: Component { ButtonStyle { background: UM.AngledCornerRectangle { diff --git a/resources/themes/cura/theme.json b/resources/themes/cura/theme.json index 9e5911e4e0..222b3008fe 100644 --- a/resources/themes/cura/theme.json +++ b/resources/themes/cura/theme.json @@ -88,6 +88,9 @@ "setting_validation_warning": [255, 186, 15, 255], "setting_validation_ok": [255, 255, 255, 255], + "progressbar_background": [245, 245, 245, 255], + "progressbar_control": [12, 169, 227, 255], + "slider_groove": [245, 245, 245, 255], "slider_groove_border": [205, 202, 201, 255], "slider_groove_fill": [205, 202, 201, 255], @@ -135,6 +138,10 @@ "button": [4.25, 4.25], "button_icon": [2.9, 2.9], + "progressbar": [26.0, 0.5], + "progressbar_control": [8.0, 0.5], + "progressbar_margin": [0.0, 0.0], + "scrollbar": [0.5, 0.5], "slider_groove": [0.5, 0.5], From cff6ae32a76ab710186d9925cb05dead39001ede Mon Sep 17 00:00:00 2001 From: Tamara Hogenhout Date: Mon, 15 Jun 2015 17:28:41 +0200 Subject: [PATCH 058/117] helps with making the messagestack adapt to the total size of messages + pogressbar Commits to #44 --- resources/themes/cura/theme.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/themes/cura/theme.json b/resources/themes/cura/theme.json index 222b3008fe..1e34028046 100644 --- a/resources/themes/cura/theme.json +++ b/resources/themes/cura/theme.json @@ -140,7 +140,7 @@ "progressbar": [26.0, 0.5], "progressbar_control": [8.0, 0.5], - "progressbar_margin": [0.0, 0.0], + "progressbar_padding": [0.0, 1.0], "scrollbar": [0.5, 0.5], From 2aa2d4d1a8b85a475602adfbc827fa8fba55f356 Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Tue, 16 Jun 2015 12:36:39 +0200 Subject: [PATCH 059/117] Update generated protobuf protocol file as per CuraEngine changes Contributes to Ultimaker/Cura#52 --- plugins/CuraEngineBackend/Cura_pb2.py | 68 ++++++++++++++++++--------- 1 file changed, 47 insertions(+), 21 deletions(-) diff --git a/plugins/CuraEngineBackend/Cura_pb2.py b/plugins/CuraEngineBackend/Cura_pb2.py index cf95bd2832..f97aaafc4d 100644 --- a/plugins/CuraEngineBackend/Cura_pb2.py +++ b/plugins/CuraEngineBackend/Cura_pb2.py @@ -1,6 +1,3 @@ -# Copyright (c) 2015 Ultimaker B.V. -# Cura is released under the terms of the AGPLv3 or higher. - # Generated by the protocol buffer compiler. DO NOT EDIT! # source: Cura.proto @@ -21,7 +18,7 @@ _sym_db = _symbol_database.Default() DESCRIPTOR = _descriptor.FileDescriptor( name='Cura.proto', package='Cura', - serialized_pb=_b('\n\nCura.proto\x12\x04\x43ura\"+\n\nObjectList\x12\x1d\n\x07objects\x18\x01 \x03(\x0b\x32\x0c.Cura.Object\"i\n\x06Object\x12\n\n\x02id\x18\x01 \x01(\x03\x12\x10\n\x08vertices\x18\x02 \x01(\x0c\x12\x0f\n\x07normals\x18\x03 \x01(\x0c\x12\x0f\n\x07indices\x18\x04 \x01(\x0c\x12\x1f\n\x08settings\x18\x05 \x03(\x0b\x32\r.Cura.Setting\"\x1a\n\x08Progress\x12\x0e\n\x06\x61mount\x18\x01 \x01(\x02\"7\n\x10SlicedObjectList\x12#\n\x07objects\x18\x01 \x03(\x0b\x32\x12.Cura.SlicedObject\"7\n\x0cSlicedObject\x12\n\n\x02id\x18\x01 \x01(\x03\x12\x1b\n\x06layers\x18\x02 \x03(\x0b\x32\x0b.Cura.Layer\"4\n\x05Layer\x12\n\n\x02id\x18\x01 \x01(\x05\x12\x1f\n\x08polygons\x18\x02 \x03(\x0b\x32\r.Cura.Polygon\"\x9f\x01\n\x07Polygon\x12 \n\x04type\x18\x01 \x01(\x0e\x32\x12.Cura.Polygon.Type\x12\x0e\n\x06points\x18\x02 \x01(\x0c\"b\n\x04Type\x12\x0c\n\x08NoneType\x10\x00\x12\x0e\n\nInset0Type\x10\x01\x12\x0e\n\nInsetXType\x10\x02\x12\x0c\n\x08SkinType\x10\x03\x12\x0f\n\x0bSupportType\x10\x04\x12\r\n\tSkirtType\x10\x05\"&\n\nGCodeLayer\x12\n\n\x02id\x18\x01 \x01(\x03\x12\x0c\n\x04\x64\x61ta\x18\x02 \x01(\x0c\"D\n\x0fObjectPrintTime\x12\n\n\x02id\x18\x01 \x01(\x03\x12\x0c\n\x04time\x18\x02 \x01(\x02\x12\x17\n\x0fmaterial_amount\x18\x03 \x01(\x02\".\n\x0bSettingList\x12\x1f\n\x08settings\x18\x01 \x03(\x0b\x32\r.Cura.Setting\"&\n\x07Setting\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x0c\"\x1b\n\x0bGCodePrefix\x12\x0c\n\x04\x64\x61ta\x18\x02 \x01(\x0c\x62\x06proto3') + serialized_pb=_b('\n\nCura.proto\x12\x04\x43ura\"+\n\nObjectList\x12\x1d\n\x07objects\x18\x01 \x03(\x0b\x32\x0c.Cura.Object\"i\n\x06Object\x12\n\n\x02id\x18\x01 \x01(\x03\x12\x10\n\x08vertices\x18\x02 \x01(\x0c\x12\x0f\n\x07normals\x18\x03 \x01(\x0c\x12\x0f\n\x07indices\x18\x04 \x01(\x0c\x12\x1f\n\x08settings\x18\x05 \x03(\x0b\x32\r.Cura.Setting\"\x1a\n\x08Progress\x12\x0e\n\x06\x61mount\x18\x01 \x01(\x02\"7\n\x10SlicedObjectList\x12#\n\x07objects\x18\x01 \x03(\x0b\x32\x12.Cura.SlicedObject\"7\n\x0cSlicedObject\x12\n\n\x02id\x18\x01 \x01(\x03\x12\x1b\n\x06layers\x18\x02 \x03(\x0b\x32\x0b.Cura.Layer\"W\n\x05Layer\x12\n\n\x02id\x18\x01 \x01(\x05\x12\x0e\n\x06height\x18\x02 \x01(\x02\x12\x11\n\tthickness\x18\x03 \x01(\x02\x12\x1f\n\x08polygons\x18\x04 \x03(\x0b\x32\r.Cura.Polygon\"\xdb\x01\n\x07Polygon\x12 \n\x04type\x18\x01 \x01(\x0e\x32\x12.Cura.Polygon.Type\x12\x0e\n\x06points\x18\x02 \x01(\x0c\x12\x12\n\nline_width\x18\x03 \x01(\x02\"\x89\x01\n\x04Type\x12\x0c\n\x08NoneType\x10\x00\x12\x0e\n\nInset0Type\x10\x01\x12\x0e\n\nInsetXType\x10\x02\x12\x0c\n\x08SkinType\x10\x03\x12\x0f\n\x0bSupportType\x10\x04\x12\r\n\tSkirtType\x10\x05\x12\x0e\n\nInfillType\x10\x06\x12\x15\n\x11SupportInfillType\x10\x07\"&\n\nGCodeLayer\x12\n\n\x02id\x18\x01 \x01(\x03\x12\x0c\n\x04\x64\x61ta\x18\x02 \x01(\x0c\"D\n\x0fObjectPrintTime\x12\n\n\x02id\x18\x01 \x01(\x03\x12\x0c\n\x04time\x18\x02 \x01(\x02\x12\x17\n\x0fmaterial_amount\x18\x03 \x01(\x02\".\n\x0bSettingList\x12\x1f\n\x08settings\x18\x01 \x03(\x0b\x32\r.Cura.Setting\"&\n\x07Setting\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x0c\"\x1b\n\x0bGCodePrefix\x12\x0c\n\x04\x64\x61ta\x18\x02 \x01(\x0c\x62\x06proto3') ) _sym_db.RegisterFileDescriptor(DESCRIPTOR) @@ -57,11 +54,19 @@ _POLYGON_TYPE = _descriptor.EnumDescriptor( name='SkirtType', index=5, number=5, options=None, type=None), + _descriptor.EnumValueDescriptor( + name='InfillType', index=6, number=6, + options=None, + type=None), + _descriptor.EnumValueDescriptor( + name='SupportInfillType', index=7, number=7, + options=None, + type=None), ], containing_type=None, options=None, - serialized_start=430, - serialized_end=528, + serialized_start=486, + serialized_end=623, ) _sym_db.RegisterEnumDescriptor(_POLYGON_TYPE) @@ -266,8 +271,22 @@ _LAYER = _descriptor.Descriptor( is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( - name='polygons', full_name='Cura.Layer.polygons', index=1, - number=2, type=11, cpp_type=10, label=3, + name='height', full_name='Cura.Layer.height', index=1, + number=2, type=2, cpp_type=6, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='thickness', full_name='Cura.Layer.thickness', index=2, + number=3, type=2, cpp_type=6, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='polygons', full_name='Cura.Layer.polygons', index=3, + number=4, type=11, cpp_type=10, label=3, has_default_value=False, default_value=[], message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, @@ -284,7 +303,7 @@ _LAYER = _descriptor.Descriptor( oneofs=[ ], serialized_start=314, - serialized_end=366, + serialized_end=401, ) @@ -309,6 +328,13 @@ _POLYGON = _descriptor.Descriptor( message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), + _descriptor.FieldDescriptor( + name='line_width', full_name='Cura.Polygon.line_width', index=2, + number=3, type=2, cpp_type=6, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), ], extensions=[ ], @@ -321,8 +347,8 @@ _POLYGON = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=369, - serialized_end=528, + serialized_start=404, + serialized_end=623, ) @@ -358,8 +384,8 @@ _GCODELAYER = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=530, - serialized_end=568, + serialized_start=625, + serialized_end=663, ) @@ -402,8 +428,8 @@ _OBJECTPRINTTIME = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=570, - serialized_end=638, + serialized_start=665, + serialized_end=733, ) @@ -432,8 +458,8 @@ _SETTINGLIST = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=640, - serialized_end=686, + serialized_start=735, + serialized_end=781, ) @@ -469,8 +495,8 @@ _SETTING = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=688, - serialized_end=726, + serialized_start=783, + serialized_end=821, ) @@ -499,8 +525,8 @@ _GCODEPREFIX = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=728, - serialized_end=755, + serialized_start=823, + serialized_end=850, ) _OBJECTLIST.fields_by_name['objects'].message_type = _OBJECT From 9849283144e0d6287efa17b1d49b443e6747ba3c Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Tue, 16 Jun 2015 13:11:19 +0200 Subject: [PATCH 060/117] Do not render the convex hull node unless the object is selected Contributes to #52 --- cura/ConvexHullNode.py | 3 +++ plugins/LayerView/LayerView.py | 8 ++++++++ 2 files changed, 11 insertions(+) diff --git a/cura/ConvexHullNode.py b/cura/ConvexHullNode.py index a94ae60897..5be5820982 100644 --- a/cura/ConvexHullNode.py +++ b/cura/ConvexHullNode.py @@ -48,6 +48,9 @@ class ConvexHullNode(SceneNode): self.setMeshData(mesh) + def getWatchedNode(self): + return self._node + def render(self, renderer): if not self._material: self._material = renderer.createMaterial(Resources.getPath(Resources.ShadersLocation, "basic.vert"), Resources.getPath(Resources.ShadersLocation, "color.frag")) diff --git a/plugins/LayerView/LayerView.py b/plugins/LayerView/LayerView.py index 7f8c04986d..e32645d9ed 100644 --- a/plugins/LayerView/LayerView.py +++ b/plugins/LayerView/LayerView.py @@ -11,6 +11,9 @@ from . import LayerViewProxy from UM.Scene.Selection import Selection from UM.Math.Color import Color +from cura.ConvexHullNode import ConvexHullNode + + ## View used to display g-code paths. class LayerView(View): def __init__(self): @@ -45,6 +48,11 @@ class LayerView(View): self._selection_material.setUniformValue("u_color", Color(35, 35, 35, 128)) for node in DepthFirstIterator(scene.getRoot()): + # We do not want to render ConvexHullNode as it conflicts with the bottom layers. + # However, it is somewhat relevant when the node is selected, so do render it then. + if type(node) is ConvexHullNode and not Selection.isSelected(node.getWatchedNode()): + continue + if not node.render(renderer): if node.getMeshData() and node.isVisible(): if Selection.isSelected(node): From ad885063256f6461f8fe797a7ed47f2cefd05526 Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Tue, 16 Jun 2015 13:16:45 +0200 Subject: [PATCH 061/117] Use the new layer message properties to properly create layers Now we use the height as provided by the engine to render the layer at. Contributes to #52 --- plugins/CuraEngineBackend/LayerData.py | 74 ++++++++++++++++--- .../ProcessSlicedObjectListJob.py | 21 +++--- 2 files changed, 74 insertions(+), 21 deletions(-) diff --git a/plugins/CuraEngineBackend/LayerData.py b/plugins/CuraEngineBackend/LayerData.py index 546397e460..1c4c2a0b9c 100644 --- a/plugins/CuraEngineBackend/LayerData.py +++ b/plugins/CuraEngineBackend/LayerData.py @@ -2,7 +2,9 @@ # Cura is released under the terms of the AGPLv3 or higher. from UM.Mesh.MeshData import MeshData +from UM.Mesh.MeshBuilder import MeshBuilder from UM.Math.Color import Color +from UM.Math.Vector import Vector import numpy import math @@ -13,12 +15,19 @@ class LayerData(MeshData): self._layers = {} self._element_counts = {} - def addPolygon(self, layer, type, data): + def addLayer(self, layer): if layer not in self._layers: - self._layers[layer] = [] + self._layers[layer] = Layer(layer) - p = Polygon(self, type, data) - self._layers[layer].append(p) + def addPolygon(self, layer, type, data, line_width): + if layer not in self._layers: + self.addLayer(layer) + + p = Polygon(self, type, data, line_width) + self._layers[layer].polygons.append(p) + + def getLayer(self, layer): + return self._layers[layer] def getLayers(self): return self._layers @@ -26,14 +35,61 @@ class LayerData(MeshData): def getElementCounts(self): return self._element_counts + def setLayerHeight(self, layer, height): + if layer not in self._layers: + self.addLayer(layer) + + self._layers[layer].setHeight(height) + + def setLayerThickness(self, layer, thickness): + if layer not in self._layers: + self.addLayer(layer) + + self._layers[layer].setThickness(thickness) + def build(self): for layer, data in self._layers.items(): - if layer not in self._element_counts: - self._element_counts[layer] = [] + data.build() - for polygon in data: - polygon.build() - self._element_counts[layer].append(polygon.elementCount) + self._element_counts[layer] = data.elementCount + +class Layer(): + def __init__(self, id): + self._id = id + self._height = 0.0 + self._thickness = 0.0 + self._polygons = [] + self._element_count = 0 + + @property + def height(self): + return self._height + + @property + def thickness(self): + return self._thickness + + @property + def polygons(self): + return self._polygons + + @property + def elementCount(self): + return self._element_count + + def setHeight(self, height): + self._height = height + + def setThickness(self, thickness): + self._thickness = thickness + + def build(self): + for polygon in self._polygons: + if polygon._type == Polygon.InfillType or polygon._type == Polygon.SupportInfillType: + continue + + polygon.build() + self._element_count += polygon.elementCount class Polygon(): NoneType = 0 diff --git a/plugins/CuraEngineBackend/ProcessSlicedObjectListJob.py b/plugins/CuraEngineBackend/ProcessSlicedObjectListJob.py index 804338e4dc..cef8ff754f 100644 --- a/plugins/CuraEngineBackend/ProcessSlicedObjectListJob.py +++ b/plugins/CuraEngineBackend/ProcessSlicedObjectListJob.py @@ -32,22 +32,24 @@ class ProcessSlicedObjectListJob(Job): settings = Application.getInstance().getActiveMachine() layerHeight = settings.getSettingValueByKey("layer_height") + mesh = MeshData() for object in self._message.objects: - try: + try: node = objectIdMap[object.id] except KeyError: continue - - mesh = MeshData() layerData = LayerData.LayerData() for layer in object.layers: + layerData.addLayer(layer.id) + layerData.setLayerHeight(layer.id, layer.height) + layerData.setLayerThickness(layer.id, layer.thickness) for polygon in layer.polygons: points = numpy.fromstring(polygon.points, dtype="i8") # Convert bytearray to numpy array points = points.reshape((-1,2)) # We get a linear list of pairs that make up the points, so make numpy interpret them correctly. points = numpy.asarray(points, dtype=numpy.float32) points /= 1000 - points = numpy.insert(points, 1, layer.id * layerHeight, axis = 1) + points = numpy.insert(points, 1, (layer.height / 1000), axis = 1) points[:,2] *= -1 @@ -55,16 +57,11 @@ class ProcessSlicedObjectListJob(Job): center = [settings.getSettingValueByKey("machine_width") / 2, 0.0, -settings.getSettingValueByKey("machine_depth") / 2] points -= numpy.array(center) - #points = numpy.pad(points, ((0,0), (0,1)), "constant", constant_values=(0.0, 1.0)) - #inverse = node.getWorldTransformation().getInverse().getData() - #points = points.dot(inverse) - #points = points[:,0:3] layerData.addPolygon(layer.id, polygon.type, points) + # We are done processing all the layers we got from the engine, now create a mesh out of the data + layerData.build() + mesh.layerData = layerData - # We are done processing all the layers we got from the engine, now create a mesh out of the data - layerData.build() - mesh.layerData = layerData - new_node.setMeshData(mesh) new_node.setParent(self._scene.getRoot()) From 75fff176f4d3bd7c0a54962ff21a9a215646da21 Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Tue, 16 Jun 2015 13:17:52 +0200 Subject: [PATCH 062/117] Add support for line width and infill types to LayerData::Polygon Contributes to #52 --- plugins/CuraEngineBackend/LayerData.py | 46 ++++++++++++------- .../ProcessSlicedObjectListJob.py | 2 +- 2 files changed, 31 insertions(+), 17 deletions(-) diff --git a/plugins/CuraEngineBackend/LayerData.py b/plugins/CuraEngineBackend/LayerData.py index 1c4c2a0b9c..15edc16e14 100644 --- a/plugins/CuraEngineBackend/LayerData.py +++ b/plugins/CuraEngineBackend/LayerData.py @@ -98,34 +98,26 @@ class Polygon(): SkinType = 3 SupportType = 4 SkirtType = 5 + InfillType = 6 + SupportInfillType = 7 - def __init__(self, mesh, type, data): + def __init__(self, mesh, type, data, line_width): super().__init__() self._mesh = mesh self._type = type self._data = data + self._line_width = line_width / 1000 def build(self): self._begin = self._mesh._vertex_count self._mesh.addVertices(self._data) self._end = self._begin + len(self._data) - 1 - color = None - if self._type == self.Inset0Type: - color = [1, 0, 0, 1] - elif self._type == self.InsetXType: - color = [0, 1, 0, 1] - elif self._type == self.SkinType: - color = [1, 1, 0, 1] - elif self._type == self.SupportType: - color = [0, 1, 1, 1] - elif self._type == self.SkirtType: - color = [0, 1, 1, 1] - else: - color = [1, 1, 1, 1] + color = self.getColor() + color[3] = 2.0 colors = [color for i in range(len(self._data))] - self._mesh.addColors(numpy.array(colors, dtype=numpy.float32)) + self._mesh.addColors(numpy.array(colors, dtype=numpy.float32) * 0.5) indices = [] for i in range(self._begin, self._end): @@ -136,6 +128,24 @@ class Polygon(): indices.append(self._begin) self._mesh.addIndices(numpy.array(indices, dtype=numpy.int32)) + def getColor(self): + if self._type == self.Inset0Type: + return [1.0, 0.0, 0.0, 1.0] + elif self._type == self.InsetXType: + return [0.0, 1.0, 0.0, 1.0] + elif self._type == self.SkinType: + return [1.0, 1.0, 0.0, 1.0] + elif self._type == self.SupportType: + return [0.0, 1.0, 1.0, 1.0] + elif self._type == self.SkirtType: + return [0.0, 1.0, 1.0, 1.0] + elif self._type == self.InfillType: + return [1.0, 1.0, 0.0, 1.0] + elif self._type == self.SupportInfillType: + return [0.0, 1.0, 1.0, 1.0] + else: + return [1.0, 1.0, 1.0, 1.0] + @property def type(self): return self._type @@ -146,4 +156,8 @@ class Polygon(): @property def elementCount(self): - return (self._end - self._begin) * 2 #The range of vertices multiplied by 2 since each vertex is used twice + return ((self._end - self._begin) + 1) * 2 #The range of vertices multiplied by 2 since each vertex is used twice + + @property + def lineWidth(self): + return self._line_width diff --git a/plugins/CuraEngineBackend/ProcessSlicedObjectListJob.py b/plugins/CuraEngineBackend/ProcessSlicedObjectListJob.py index cef8ff754f..6113da78a0 100644 --- a/plugins/CuraEngineBackend/ProcessSlicedObjectListJob.py +++ b/plugins/CuraEngineBackend/ProcessSlicedObjectListJob.py @@ -57,8 +57,8 @@ class ProcessSlicedObjectListJob(Job): center = [settings.getSettingValueByKey("machine_width") / 2, 0.0, -settings.getSettingValueByKey("machine_depth") / 2] points -= numpy.array(center) + layerData.addPolygon(layer.id, polygon.type, points, polygon.line_width) - layerData.addPolygon(layer.id, polygon.type, points) # We are done processing all the layers we got from the engine, now create a mesh out of the data layerData.build() mesh.layerData = layerData From 06b5e9d0ca4270e90263566534682d97b8e3e353 Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Tue, 16 Jun 2015 13:18:24 +0200 Subject: [PATCH 063/117] Add a createMesh function to LayerData::Layer that creates a mesh from a layer Contributes to #52 --- plugins/CuraEngineBackend/LayerData.py | 51 ++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/plugins/CuraEngineBackend/LayerData.py b/plugins/CuraEngineBackend/LayerData.py index 15edc16e14..b129942c36 100644 --- a/plugins/CuraEngineBackend/LayerData.py +++ b/plugins/CuraEngineBackend/LayerData.py @@ -91,6 +91,57 @@ class Layer(): polygon.build() self._element_count += polygon.elementCount + def createMesh(self): + builder = MeshBuilder() + + for polygon in self._polygons: + poly_color = polygon.getColor() + poly_color = Color(poly_color[0], poly_color[1], poly_color[2], poly_color[3]) + + points = numpy.copy(polygon.data) + if polygon.type == Polygon.InfillType or polygon.type == Polygon.SkinType or polygon.type == Polygon.SupportInfillType: + points[:,1] -= 0.01 + + # Calculate normals for the entire polygon using numpy. + normals = numpy.copy(points) + normals[:,1] = 0.0 # We are only interested in 2D normals + + # Calculate the edges between points. + # The call to numpy.roll shifts the entire array by one so that + # we end up subtracting each next point from the current, wrapping + # around. This gives us the edges from the next point to the current + # point. + normals[:] = normals[:] - numpy.roll(normals, -1, axis = 0) + # Calculate the length of each edge using standard Pythagoras + lengths = numpy.sqrt(normals[:,0] ** 2 + normals[:,2] ** 2) + # The normal of a 2D vector is equal to its x and y coordinates swapped + # and then x inverted. This code does that. + normals[:,[0, 2]] = normals[:,[2, 0]] + normals[:,0] *= -1 + + # Normalize the normals. + normals[:,0] /= lengths + normals[:,2] /= lengths + + # Scale all by the line width of the polygon so we can easily offset. + normals *= (polygon.lineWidth / 2) + + #TODO: Use numpy magic to perform the vertex creation to speed up things. + for i in range(len(points)): + start = points[i - 1] + end = points[i] + + normal = normals[i - 1] + + point1 = Vector(data = start - normal) + point2 = Vector(data = start + normal) + point3 = Vector(data = end + normal) + point4 = Vector(data = end - normal) + + builder.addQuad(point1, point2, point3, point4, color = poly_color) + + return builder.getData() + class Polygon(): NoneType = 0 Inset0Type = 1 From 995b76fad956bd0f747794076679b71e053f5243 Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Tue, 16 Jun 2015 13:19:45 +0200 Subject: [PATCH 064/117] Implement rendering the top 5 layers solid and with infill Fixes #52 --- plugins/LayerView/LayerView.py | 50 ++++++++++++++++++++++++++-------- 1 file changed, 38 insertions(+), 12 deletions(-) diff --git a/plugins/LayerView/LayerView.py b/plugins/LayerView/LayerView.py index e32645d9ed..17cea9988c 100644 --- a/plugins/LayerView/LayerView.py +++ b/plugins/LayerView/LayerView.py @@ -7,12 +7,13 @@ from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator from UM.Resources import Resources from UM.Event import Event, KeyEvent from UM.Signal import Signal -from . import LayerViewProxy from UM.Scene.Selection import Selection from UM.Math.Color import Color +from UM.Mesh.MeshData import MeshData from cura.ConvexHullNode import ConvexHullNode +from . import LayerViewProxy ## View used to display g-code paths. class LayerView(View): @@ -25,6 +26,9 @@ class LayerView(View): self._controller.getScene().sceneChanged.connect(self._onSceneChanged) self._max_layers = 10 self._current_layer_num = 10 + self._current_layer_mesh = None + + self._solid_layers = 5 def getCurrentLayer(self): return self._current_layer_num @@ -63,19 +67,39 @@ class LayerView(View): except AttributeError: continue - start = 0 - end = 0 + # Render all layers below a certain number as line mesh instead of vertices. + if self._current_layer_num - self._solid_layers > -1: + start = 0 + end = 0 + element_counts = layer_data.getElementCounts() + for layer, counts in element_counts.items(): + if layer + self._solid_layers > self._current_layer_num: + break + end += counts - element_counts = layer_data.getElementCounts() - for layer, counts in element_counts.items(): - end += sum(counts) - ## Hack to ensure the end is correct. Not quite sure what causes this - end += 2 * len(counts) + # This uses glDrawRangeElements internally to only draw a certain range of lines. + renderer.queueNode(node, mesh = layer_data, material = self._material, mode = Renderer.RenderLines, start = start, end = end) - if layer >= self._current_layer_num: - break + # We currently recreate the current "solid" layers every time a + if not self._current_layer_mesh: + self._current_layer_mesh = MeshData() + for i in range(self._solid_layers): + layer = self._current_layer_num - i + if layer < 0: + continue - renderer.queueNode(node, mesh = layer_data, material = self._material, mode = Renderer.RenderLines, start = start, end = end) + layer_mesh = layer_data.getLayer(layer).createMesh() + if not layer_mesh or layer_mesh.getVertices() is None: + continue + + self._current_layer_mesh.addVertices(layer_mesh.getVertices()) + + # Scale layer color by a brightness factor based on the current layer number + # This will result in a range of 0.5 - 1.0 to multiply colors by. + brightness = (2.0 - (i / self._solid_layers)) / 2.0 + self._current_layer_mesh.addColors(layer_mesh.getColors() * brightness) + + renderer.queueNode(node, mesh = self._current_layer_mesh, material = self._material) def setLayer(self, value): if self._current_layer_num != value: @@ -84,6 +108,8 @@ class LayerView(View): self._current_layer_num = 0 if self._current_layer_num > self._max_layers: self._current_layer_num = self._max_layers + + self._current_layer_mesh = None self.currentLayerNumChanged.emit() currentLayerNumChanged = Signal() @@ -104,7 +130,7 @@ class LayerView(View): except AttributeError: continue if new_max_layers < len(layer_data.getLayers()): - new_max_layers = len(layer_data.getLayers()) + new_max_layers = len(layer_data.getLayers()) - 1 if new_max_layers > 0 and new_max_layers != self._old_max_layers: self._max_layers = new_max_layers From 7922cb3034b3717716181dcc7083abdb9bd1d283 Mon Sep 17 00:00:00 2001 From: Tamara Hogenhout Date: Tue, 16 Jun 2015 14:14:31 +0200 Subject: [PATCH 065/117] adds styling for a regular progressbar Commits to #44 --- resources/themes/cura/styles.qml | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/resources/themes/cura/styles.qml b/resources/themes/cura/styles.qml index 43bbb8c400..1f87538d30 100644 --- a/resources/themes/cura/styles.qml +++ b/resources/themes/cura/styles.qml @@ -152,7 +152,30 @@ QtObject { } - property Component progressbar: Component{ + property Component progressbar_regular: Component{ + ProgressBarStyle { + background: Rectangle { + implicitWidth: UM.Theme.sizes.progressbar.width + implicitHeight: UM.Theme.sizes.progressbar.height + color: "transparent" + Item { + anchors.fill: parent + UM.AngledCornerRectangle { + cornerSize: UM.Theme.sizes.progressbar_control.height + color: UM.Theme.colors.progressbar_background + width: UM.Theme.sizes.progressbar.width + height: UM.Theme.sizes.progressbar.height + } + } + } + progress: UM.AngledCornerRectangle { + cornerSize: UM.Theme.sizes.progressbar_control.height + color: UM.Theme.colors.progressbar_control + } + } + } + + property Component progressbar_indeterminate: Component{ ProgressBarStyle { background: UM.AngledCornerRectangle { anchors.fill: parent @@ -168,7 +191,6 @@ QtObject { color: UM.Theme.colors.progressbar_background Item { anchors.fill: parent - anchors.margins: UM.Theme.sizes.progressbar_margin.width visible: control.indeterminate Row { Repeater { From 15e66a7a18505db75f1449465f05a3bc056fb64c Mon Sep 17 00:00:00 2001 From: Tamara Hogenhout Date: Tue, 16 Jun 2015 14:42:37 +0200 Subject: [PATCH 066/117] adds styling for both the regular as the indeterminate progressbar Commits to #44 --- resources/themes/cura/styles.qml | 76 ++++++++++---------------------- 1 file changed, 23 insertions(+), 53 deletions(-) diff --git a/resources/themes/cura/styles.qml b/resources/themes/cura/styles.qml index 1f87538d30..243c12aedb 100644 --- a/resources/themes/cura/styles.qml +++ b/resources/themes/cura/styles.qml @@ -152,69 +152,39 @@ QtObject { } - property Component progressbar_regular: Component{ + property Component progressbar: Component{ ProgressBarStyle { - background: Rectangle { + background: UM.AngledCornerRectangle { + cornerSize: UM.Theme.sizes.progressbar_control.height implicitWidth: UM.Theme.sizes.progressbar.width implicitHeight: UM.Theme.sizes.progressbar.height - color: "transparent" - Item { - anchors.fill: parent - UM.AngledCornerRectangle { - cornerSize: UM.Theme.sizes.progressbar_control.height - color: UM.Theme.colors.progressbar_background - width: UM.Theme.sizes.progressbar.width - height: UM.Theme.sizes.progressbar.height - } - } + color: UM.Theme.colors.progressbar_background } progress: UM.AngledCornerRectangle { cornerSize: UM.Theme.sizes.progressbar_control.height - color: UM.Theme.colors.progressbar_control + color: control.indeterminate ? "transparent" : UM.Theme.colors.progressbar_control + + UM.AngledCornerRectangle { + cornerSize: UM.Theme.sizes.progressbar_control.height + color: UM.Theme.colors.progressbar_control + width: UM.Theme.sizes.progressbar_control.width + height: UM.Theme.sizes.progressbar_control.height + visible: control.indeterminate + + SequentialAnimation on x { + id: xAnim + property int animEndPoint: UM.Theme.sizes.progressbar.width - UM.Theme.sizes.progressbar_control.width + running: control.indeterminate + loops: Animation.Infinite + NumberAnimation { from: 0; to: xAnim.animEndPoint; duration: 2000;} + NumberAnimation { from: xAnim.animEndPoint; to: 0; duration: 2000;} + } + } } } } - property Component progressbar_indeterminate: Component{ - ProgressBarStyle { - background: UM.AngledCornerRectangle { - anchors.fill: parent - anchors.left: parent.left - implicitWidth: UM.Theme.sizes.progressbar.width - implicitHeight: UM.Theme.sizes.progressbar.height - color: "transparent" - } - progress: UM.AngledCornerRectangle { - anchors.left: parent.left - anchors.fill: parent - cornerSize: UM.Theme.sizes.progressbar_control.height - color: UM.Theme.colors.progressbar_background - Item { - anchors.fill: parent - visible: control.indeterminate - Row { - Repeater { - UM.AngledCornerRectangle { - cornerSize: UM.Theme.sizes.progressbar_control.height - color: UM.Theme.colors.progressbar_control - width: UM.Theme.sizes.progressbar_control.width - height: UM.Theme.sizes.progressbar_control.height - } - model: 1 - } - SequentialAnimation on x { - id: xAnim - property int animEndPoint: UM.Theme.sizes.progressbar.width - UM.Theme.sizes.progressbar_control.width - running: control.indeterminate - loops: Animation.Infinite - NumberAnimation { from: 0; to: xAnim.animEndPoint; duration: 2000;} - NumberAnimation { from: xAnim.animEndPoint; to: 0; duration: 2000;} - } - } - } - } - } - } + property Component sidebar_category: Component { ButtonStyle { From 5c99e81726190daee67b00b72abed9f58e4562b1 Mon Sep 17 00:00:00 2001 From: Tim Kuipers Date: Tue, 16 Jun 2015 14:43:44 +0200 Subject: [PATCH 067/117] support roof settings (Hammock) + lil stuff on other settings --- resources/settings/fdmprinter.json | 79 +++++++++++++++++++++++++++--- 1 file changed, 73 insertions(+), 6 deletions(-) diff --git a/resources/settings/fdmprinter.json b/resources/settings/fdmprinter.json index e088dd8eb9..bbceda522f 100644 --- a/resources/settings/fdmprinter.json +++ b/resources/settings/fdmprinter.json @@ -107,7 +107,7 @@ "children": { "wall_line_width_0": { - "label": "First Wall Line Width", + "label": "Outer Wall Line Width", "description": "Width of the outermost shell line. By printing a thinner outermost wall line you can print higher details with a larger nozzle.", "unit": "mm", "default": 0.4, @@ -132,7 +132,7 @@ }, "skin_line_width": { "label": "Top/bottom line width", - "description": "Width of a single top/bottom printed line. Which are used to fill up the top/bottom areas of a print.", + "description": "Width of a single top/bottom printed line, used to fill up the top/bottom areas of a print.", "unit": "mm", "default": 0.4, "type": "float", @@ -152,7 +152,23 @@ "unit": "mm", "default": 0.4, "type": "float", - "visible": false + "visible": false, + "active_if" : { + "setting": "support_enable", + "value": true + } + }, + "support_roof_line_width": { + "label": "Support Hammock line width", + "description": "Width of a single hammock line, used to fill the roof of the support.", + "unit": "mm", + "default": 0.4, + "type": "float", + "visible": false, + "active_if" : { + "setting": "support_roof_enable", + "value": true + } } } } @@ -455,7 +471,39 @@ "type": "float", "default": 50.0, "visible": false, - "inherit_function": "speed_wall_0" + "inherit_function": "speed_wall_0", + "active_if" : { + "setting": "support_enable", + "value": true + }, + "children": { + "speed_support_lines": { + "label": "Support Wall Speed", + "description": "The speed at which the walls of exterior support are printed. Printing the walls at higher speeds can improve on the overall duration. ", + "unit": "mm/s", + "type": "float", + "default": 50.0, + "visible": false, + "inherit": true, + "active_if" : { + "setting": "support_roof_enable", + "value": true + } + }, + "speed_support_roof": { + "label": "Support Hammock Speed", + "description": "The speed at which the roofs of exterior support are printed. Printing the hammock at lower speeds can improve on overhang quality. ", + "unit": "mm/s", + "type": "float", + "default": 40.0, + "visible": false, + "inherit": false, + "active_if" : { + "setting": "support_roof_enable", + "value": true + } + } + } } } }, @@ -1054,7 +1102,7 @@ }, "support_angle": { "label": "Overhang Angle", - "description": "The maximum angle of overhangs for which support will be added. With 0 degrees being horizontal, and 90 degrees being vertical.", + "description": "The maximum angle of overhangs for which support will be added. With 0 degrees being vertical, and 90 degrees being horizontal. A smaller overhang angle leads to more support.", "unit": "°", "type": "float", "default": 60.0, @@ -1111,7 +1159,7 @@ "description": "The height of the steps of the stair-like bottom of support resting on the model. Small steps can cause the support to be hard to remove from the top of the model.", "unit": "mm", "type": "float", - "default": 0.5, + "default": 1.0, "visible": false, "active_if": { "setting": "support_type", @@ -1134,6 +1182,25 @@ "default": 0.6, "visible": false }, + "support_roof_enable": { + "label": "Enable Hammock", + "description": "Generate a solid support roof on which the model sits.", + "type": "boolean", + "default": false, + "visible": true + }, + "support_roof_height": { + "label": "Hammock Thickness", + "description": "The height of the support roofs. ", + "unit": "mm", + "type": "float", + "default": 1.0, + "visible": false, + "active_if": { + "setting": "support_roof_enable", + "value": true + } + }, "support_use_towers": { "label": "Use towers.", "description": "Use specialized towers to support tiny overhang areas. These towers have a larger diameter than the region they support. Near the overhang the towers' diameter decreases, forming a roof.", From 3c5096cebc4f20aa99362fa5e4c78d6ca7b6d9b1 Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Tue, 16 Jun 2015 16:18:52 +0200 Subject: [PATCH 068/117] Use python's webbrowser instead of Qt.openUrlExternally for opening urls Qt.openUrlExternally crashes on Windows, but python webbrowser works. So use that instead. Fixes Asana issue 33694049548892 --- cura/CuraActions.py | 15 +++++++++++++++ cura/CuraApplication.py | 3 +++ resources/qml/Cura.qml | 4 ++-- 3 files changed, 20 insertions(+), 2 deletions(-) create mode 100644 cura/CuraActions.py diff --git a/cura/CuraActions.py b/cura/CuraActions.py new file mode 100644 index 0000000000..ee75665e0b --- /dev/null +++ b/cura/CuraActions.py @@ -0,0 +1,15 @@ +from PyQt5.QtCore import QObject, pyqtSignal, pyqtSlot, pyqtProperty + +import webbrowser + +class CuraActions(QObject): + def __init__(self, parent = None): + super().__init__(parent) + + @pyqtSlot() + def openDocumentation(self): + webbrowser.open("http://ultimaker.com/en/support/software") + + @pyqtSlot() + def openBugReportPage(self): + webbrowser.open("http://github.com/Ultimaker/Cura/issues") diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 5135a84471..cde470e075 100644 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -33,6 +33,7 @@ from . import PlatformPhysics from . import BuildVolume from . import CameraAnimation from . import PrintInformation +from . import CuraActions from PyQt5.QtCore import pyqtSlot, QUrl, Qt, pyqtSignal, pyqtProperty from PyQt5.QtGui import QColor @@ -184,6 +185,8 @@ class CuraApplication(QtApplication): engine.rootContext().setContextProperty("Printer", self) self._print_information = PrintInformation.PrintInformation() engine.rootContext().setContextProperty("PrintInformation", self._print_information) + self._cura_actions = CuraActions.CuraActions(self) + engine.rootContext().setContextProperty("CuraActions", self._cura_actions) def onSelectionChanged(self): if Selection.hasSelection(): diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index 7067c88c12..5c6a5c9290 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -337,8 +337,8 @@ UM.MainWindow { preferences.onTriggered: preferences.visible = true; configureMachines.onTriggered: { preferences.visible = true; preferences.setPage(2); } - documentation.onTriggered: Qt.openUrlExternally("https://ultimaker.com/en/support"); - reportBug.onTriggered: Qt.openUrlExternally("https://github.com/Ultimaker/Cura/issues"); + documentation.onTriggered: CuraActions.openDocumentation(); + reportBug.onTriggered: CuraActions.openBugReportPage(); showEngineLog.onTriggered: engineLog.visible = true; about.onTriggered: aboutDialog.visible = true; } From e795aaa45a20577416e942dd6af53bbd9e30de77 Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Tue, 16 Jun 2015 17:10:35 +0200 Subject: [PATCH 069/117] Remember the previuos active tool on selection change This prevents issues where activating a tool before selecting something would change to the translate tool. Fixes #53 --- cura/CuraApplication.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index cde470e075..488fa3404e 100644 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -68,6 +68,7 @@ class CuraApplication(QtApplication): self._output_devices = {} self._print_information = None self._i18n_catalog = None + self._previous_active_tool = None self.activeMachineChanged.connect(self._onActiveMachineChanged) @@ -191,14 +192,21 @@ class CuraApplication(QtApplication): def onSelectionChanged(self): if Selection.hasSelection(): if not self.getController().getActiveTool(): - self.getController().setActiveTool("TranslateTool") + if self._previous_active_tool: + self.getController().setActiveTool(self._previous_active_tool) + self._previous_active_tool = None + else: + self.getController().setActiveTool("TranslateTool") self._camera_animation.setStart(self.getController().getTool("CameraTool").getOrigin()) self._camera_animation.setTarget(Selection.getSelectedObject(0).getWorldPosition()) self._camera_animation.start() else: if self.getController().getActiveTool(): + self._previous_active_tool = self.getController().getActiveTool().getPluginId() self.getController().setActiveTool(None) + else: + self._previous_active_tool = None requestAddPrinter = pyqtSignal() From 8c8885d77e8482d0fcf0399a15efc2e37006d106 Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Wed, 17 Jun 2015 15:16:22 +0200 Subject: [PATCH 070/117] Add missing translations to the list of translations to build Contributes to #57 --- CMakeLists.txt | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4cbb9aebc0..bee239975f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,7 +4,7 @@ cmake_minimum_required(VERSION 2.8.12) set(URANIUM_SCRIPTS_DIR "${CMAKE_SOURCE_DIR}/../uranium/scripts" CACHE DIRECTORY "The location of the scripts directory of the Uranium repository") -if(${URANIUM_SCRIPTS_DIR}) +if(NOT ${URANIUM_SCRIPTS_DIR} STREQUAL "") # Extract Strings add_custom_target(extract-messages ${URANIUM_SCRIPTS_DIR}/extract-messages ${CMAKE_SOURCE_DIR} cura) @@ -27,6 +27,12 @@ if(${URANIUM_SCRIPTS_DIR}) set(languages en x-test + ru + fr + de + it + es + fi ) foreach(lang ${languages}) file(GLOB po_files resources/i18n/${lang}/*.po) @@ -55,7 +61,6 @@ find_package(PythonInterp 3.4.0 REQUIRED) set(PYTHON_SITE_PACKAGES_DIR ${CMAKE_INSTALL_LIBDIR}/python${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR}/site-packages CACHE PATH "Install location of Python package") install(DIRECTORY resources DESTINATION ${CMAKE_INSTALL_DATADIR}/cura) install(DIRECTORY plugins DESTINATION ${CMAKE_INSTALL_LIBDIR}/cura) -file(GLOB cura_SRCS cura/*) -install(FILES ${cura_SRCS} DESTINATION ${PYTHON_SITE_PACKAGES_DIR}/cura) +install(DIRECTORY cura DESTINATION ${PYTHON_SITE_PACKAGES_DIR}) install(FILES cura_app.py DESTINATION ${CMAKE_INSTALL_BINDIR}) From de6ee745cb0ab83786c0d809f3eec4f80a0b16da Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Wed, 17 Jun 2015 15:19:23 +0200 Subject: [PATCH 071/117] Add russian translations Contributes to #57 --- resources/i18n/ru/cura.po | 91 ++ resources/i18n/ru/cura_qt.po | 430 ++++++ resources/i18n/ru/fdmprinter.json.po | 1843 ++++++++++++++++++++++++++ 3 files changed, 2364 insertions(+) create mode 100644 resources/i18n/ru/cura.po create mode 100644 resources/i18n/ru/cura_qt.po create mode 100644 resources/i18n/ru/fdmprinter.json.po diff --git a/resources/i18n/ru/cura.po b/resources/i18n/ru/cura.po new file mode 100644 index 0000000000..d6016202c6 --- /dev/null +++ b/resources/i18n/ru/cura.po @@ -0,0 +1,91 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2015-05-07 16:35+0200\n" +"PO-Revision-Date: 2015-06-01 18:29+0300\n" +"Language: ru\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" +"Last-Translator: \n" +"Language-Team: \n" +"X-Generator: Poedit 1.8.1\n" + +#: /home/ahiemstra/Projects/Ultimaker/cura/src/CuraApplication.py:91 +msgctxt "Save button tooltip" +msgid "Save to Disk" +msgstr "Сохранить на диск" + +#: /home/ahiemstra/Projects/Ultimaker/cura/src/CuraApplication.py:96 +msgctxt "Splash screen message" +msgid "Setting up scene..." +msgstr "Настройка сцены ..." + +#: /home/ahiemstra/Projects/Ultimaker/cura/src/CuraApplication.py:130 +msgctxt "Splash screen message" +msgid "Loading interface..." +msgstr "Загрузка интерфейса ..." + +#: /home/ahiemstra/Projects/Ultimaker/cura/src/CuraApplication.py:373 +#, python-brace-format +msgctxt "Save button tooltip. {0} is sd card name" +msgid "Save to SD Card {0}" +msgstr "Сохранить на SD карту {0}" + +#: /home/ahiemstra/Projects/Ultimaker/cura/src/CuraApplication.py:421 +#, python-brace-format +msgctxt "Saved to SD message, {0} is sdcard, {1} is filename" +msgid "Saved to SD Card {0} as {1}" +msgstr "Сохранено на SD карту {0} как {1}" + +#: /home/ahiemstra/Projects/Ultimaker/cura/src/CuraApplication.py:424 +msgctxt "Message action" +msgid "Eject" +msgstr "Извлечь" + +#: /home/ahiemstra/Projects/Ultimaker/cura/src/CuraApplication.py:426 +#, python-brace-format +msgctxt "Message action tooltip, {0} is sdcard" +msgid "Eject SD Card {0}" +msgstr "Извлечь SD карту {0}" + +#: /home/ahiemstra/Projects/Ultimaker/cura/plugins/CuraEngineBackend/__init__.py:13 +msgctxt "CuraEngine backend plugin description" +msgid "Provides the link to the CuraEngine slicing backend" +msgstr "Обеспечивает связь с Кура слайсинг сервером" + +#: /home/ahiemstra/Projects/Ultimaker/cura/plugins/USBPrinting/USBPrinterManager.py:40 +msgid "Update Firmware" +msgstr "Обновление прошивки" + +#: /home/ahiemstra/Projects/Ultimaker/cura/plugins/USBPrinting/__init__.py:13 +msgctxt "USB Printing plugin description" +msgid "Accepts G-Code and sends them to a printer. Plugin can also update firmware" +msgstr "Принимает G-коды и отправляет их на принтер. Плагин также может обновить прошивку." + +#: /home/ahiemstra/Projects/Ultimaker/cura/plugins/GCodeWriter/__init__.py:13 +msgctxt "GCode Writer Plugin Description" +msgid "Writes GCode to a file" +msgstr "Записывает G-код в файл " + +#: /home/ahiemstra/Projects/Ultimaker/cura/plugins/GCodeWriter/__init__.py:18 +msgctxt "GCode Writer File Description" +msgid "GCode File" +msgstr "G-код" + +#: /home/ahiemstra/Projects/Ultimaker/cura/plugins/LayerView/__init__.py:13 +msgctxt "Layer View plugin description" +msgid "Provides the Layer view." +msgstr "Обеспечивает просмотр слоев." + +#: /home/ahiemstra/Projects/Ultimaker/cura/plugins/LayerView/__init__.py:16 +msgctxt "Layers View mode" +msgid "Layers" +msgstr "Слой" diff --git a/resources/i18n/ru/cura_qt.po b/resources/i18n/ru/cura_qt.po new file mode 100644 index 0000000000..a3522227c4 --- /dev/null +++ b/resources/i18n/ru/cura_qt.po @@ -0,0 +1,430 @@ +msgid "" +msgstr "" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Qt-Contexts: true\n" +"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" +"Project-Id-Version: Cura\n" +"POT-Creation-Date: \n" +"PO-Revision-Date: \n" +"Last-Translator: \n" +"Language-Team: \n" +"Language: ru\n" +"X-Generator: Poedit 1.8.1\n" + +#. About dialog title +#: ../resources/qml/AboutDialog.qml:12 +msgctxt "AboutDialog|" +msgid "About Cura" +msgstr "О Кура" + +#. About dialog application description +#: ../resources/qml/AboutDialog.qml:42 +msgctxt "AboutDialog|" +msgid "End-to-end solution for fused filament 3D printing." +msgstr "Комплексное решение для 3D печати по технологии послойного накладывание пластика." + +#. About dialog application author note +#: ../resources/qml/AboutDialog.qml:47 +msgctxt "AboutDialog|" +msgid "Cura has been developed by Ultimaker B.V. in cooperation with the community." +msgstr "Кура была разработана компании Ultimaker BV в сотрудничестве с сообществом." + +#. Close about dialog button +#: ../resources/qml/AboutDialog.qml:58 +msgctxt "AboutDialog|" +msgid "Close" +msgstr "Закрыть окно" + +#. Undo action +#: ../resources/qml/Actions.qml:37 +msgctxt "Actions|" +msgid "&Undo" +msgstr "&Отмена" + +#. Redo action +#: ../resources/qml/Actions.qml:45 +msgctxt "Actions|" +msgid "&Redo" +msgstr "&Повтор" + +#. Quit action +#: ../resources/qml/Actions.qml:53 +msgctxt "Actions|" +msgid "&Quit" +msgstr "&Выход" + +#. Preferences action +#: ../resources/qml/Actions.qml:61 +msgctxt "Actions|" +msgid "&Preferences..." +msgstr "&Настройки программы..." + +#. Add Printer action +#: ../resources/qml/Actions.qml:68 +msgctxt "Actions|" +msgid "&Add Printer..." +msgstr "&Добавит новый принтер" + +#. Configure Printers action +#: ../resources/qml/Actions.qml:74 +msgctxt "Actions|" +msgid "&Configure Printers" +msgstr "&Настройки принтера" + +#. Show Online Documentation action +#: ../resources/qml/Actions.qml:81 +msgctxt "Actions|" +msgid "Show Online &Documentation" +msgstr "Показать онлайн &справку" + +#. Report a Bug Action +#: ../resources/qml/Actions.qml:89 +msgctxt "Actions|" +msgid "Report a &Bug" +msgstr "Сообщить о &проблеме" + +#. About action +#: ../resources/qml/Actions.qml:96 +msgctxt "Actions|" +msgid "&About..." +msgstr "&О Кура" + +#. Delete selection action +#: ../resources/qml/Actions.qml:103 +msgctxt "Actions|" +msgid "Delete Selection" +msgstr "Удалить выбранное" + +#. Delete object action +#: ../resources/qml/Actions.qml:111 +msgctxt "Actions|" +msgid "Delete Object" +msgstr "Удалить объект" + +#. Center object action +#: ../resources/qml/Actions.qml:118 +msgctxt "Actions|" +msgid "Center Object on Platform" +msgstr "Расположить объект по центру" + +#. Duplicate object action +#: ../resources/qml/Actions.qml:124 +msgctxt "Actions|" +msgid "Duplicate Object" +msgstr "Умножить объект" + +#. Split object action +#: ../resources/qml/Actions.qml:130 +msgctxt "Actions|" +msgid "Split Object into Parts" +msgstr "Разбить объект на частей" + +#. Clear build platform action +#: ../resources/qml/Actions.qml:137 +msgctxt "Actions|" +msgid "Clear Build Platform" +msgstr "Очистить печатающую платформу" + +#. Reload all objects action +#: ../resources/qml/Actions.qml:144 +msgctxt "Actions|" +msgid "Reload All Objects" +msgstr "Перезагрузить все объекты" + +#. Reset all positions action +#: ../resources/qml/Actions.qml:150 +msgctxt "Actions|" +msgid "Reset All Object Positions" +msgstr "Сбросить все позиции объектов" + +#. Reset all positions action +#: ../resources/qml/Actions.qml:156 +msgctxt "Actions|" +msgid "Reset All Object Transformations" +msgstr "Сбросить все преобразования объектов" + +#. Open file action +#: ../resources/qml/Actions.qml:162 +msgctxt "Actions|" +msgid "&Open..." +msgstr "&Открыть..." + +#. Save file action +#: ../resources/qml/Actions.qml:170 +msgctxt "Actions|" +msgid "&Save..." +msgstr "&Сохранить..." + +#. Show engine log action +#: ../resources/qml/Actions.qml:178 +msgctxt "Actions|" +msgid "Show engine &log..." +msgstr "Показать журнал &слайсера" + +#. Add Printer dialog title +#. ---------- +#. Add Printer wizard page title +#: ../resources/qml/AddMachineWizard.qml:12 +#: ../resources/qml/AddMachineWizard.qml:19 +msgctxt "AddMachineWizard|" +msgid "Add Printer" +msgstr "Добавить новый принтер" + +#. Add Printer wizard page description +#: ../resources/qml/AddMachineWizard.qml:25 +msgctxt "AddMachineWizard|" +msgid "Please select the type of printer:" +msgstr "Выберите тип принтера:" + +#. Add Printer wizard field label +#: ../resources/qml/AddMachineWizard.qml:40 +msgctxt "AddMachineWizard|" +msgid "Printer Name:" +msgstr "Название принтера:" + +#. Add Printer wizarad button +#: ../resources/qml/AddMachineWizard.qml:53 +msgctxt "AddMachineWizard|" +msgid "Next" +msgstr "Дальше" + +#. Add Printer wizarad button +#: ../resources/qml/AddMachineWizard.qml:63 +msgctxt "AddMachineWizard|" +msgid "Cancel" +msgstr "Отменить" + +#. USB Printing dialog label, %1 is head temperature +#: ../plugins/USBPrinting/ControlWindow.qml:15 +#, qt-format +msgctxt "ControlWindow|" +msgid "Extruder Temperature %1" +msgstr "Температура экструдера %1" + +#. USB Printing dialog label, %1 is bed temperature +#: ../plugins/USBPrinting/ControlWindow.qml:20 +#, qt-format +msgctxt "ControlWindow|" +msgid "Bed Temperature %1" +msgstr "Температура платформы %1" + +#. USB Printing dialog start print button +#: ../plugins/USBPrinting/ControlWindow.qml:33 +msgctxt "ControlWindow|" +msgid "Print" +msgstr "Печать" + +#. USB Printing dialog cancel print button +#: ../plugins/USBPrinting/ControlWindow.qml:40 +msgctxt "ControlWindow|" +msgid "Cancel" +msgstr "Отменить" + +#. Cura application window title +#: ../resources/qml/Cura.qml:14 +msgctxt "Cura|" +msgid "Cura" +msgstr "Кура" + +#. File menu +#: ../resources/qml/Cura.qml:26 +msgctxt "Cura|" +msgid "&File" +msgstr "&Файл" + +#. Edit menu +#: ../resources/qml/Cura.qml:38 +msgctxt "Cura|" +msgid "&Edit" +msgstr "&Изменить" + +#. Machine menu +#: ../resources/qml/Cura.qml:50 +msgctxt "Cura|" +msgid "&Machine" +msgstr "&Принтер" + +#. Extensions menu +#: ../resources/qml/Cura.qml:76 +msgctxt "Cura|" +msgid "E&xtensions" +msgstr "&Расширения" + +#. Settings menu +#: ../resources/qml/Cura.qml:107 +msgctxt "Cura|" +msgid "&Settings" +msgstr "&Настройки" + +#. Help menu +#: ../resources/qml/Cura.qml:114 +msgctxt "Cura|" +msgid "&Help" +msgstr "&Помощь" + +#. View Mode toolbar button +#: ../resources/qml/Cura.qml:231 +msgctxt "Cura|" +msgid "View Mode" +msgstr "Режим просмотра" + +#. View preferences page title +#: ../resources/qml/Cura.qml:273 +msgctxt "Cura|" +msgid "View" +msgstr "Просмотр" + +#. File open dialog title +#: ../resources/qml/Cura.qml:370 +msgctxt "Cura|" +msgid "Open File" +msgstr "Открыть файл" + +#. File save dialog title +#: ../resources/qml/Cura.qml:386 +msgctxt "Cura|" +msgid "Save File" +msgstr "Сохранить файл" + +#. Engine Log dialog title +#: ../resources/qml/EngineLog.qml:11 +msgctxt "EngineLog|" +msgid "Engine Log" +msgstr "Журнал слайсера" + +#. Close engine log button +#: ../resources/qml/EngineLog.qml:30 +msgctxt "EngineLog|" +msgid "Close" +msgstr "Закрыть" + +#. Firmware update status label +#: ../plugins/USBPrinting/FirmwareUpdateWindow.qml:16 +msgctxt "FirmwareUpdateWindow|" +msgid "Starting firmware update, this may take a while." +msgstr "Начинается обновление прошивки, это может занять некоторое время." + +#. Firmware update status label +#: ../plugins/USBPrinting/FirmwareUpdateWindow.qml:21 +msgctxt "FirmwareUpdateWindow|" +msgid "Firmware update completed." +msgstr "Обновление прошивки завершено." + +#. Firmware update status label +#: ../plugins/USBPrinting/FirmwareUpdateWindow.qml:26 +msgctxt "FirmwareUpdateWindow|" +msgid "Updating firmware." +msgstr "Обновление прошивки." + +#. Print material amount save button label +#: ../resources/qml/SaveButton.qml:149 +#, qt-format +msgctxt "SaveButton|" +msgid "%1m material" +msgstr "%1m материал" + +#. Save button label +#: ../resources/qml/SaveButton.qml:191 +msgctxt "SaveButton|" +msgid "Please load a 3D model" +msgstr "Пожалуйста, загрузите 3D модель" + +#. Save button label +#: ../resources/qml/SaveButton.qml:194 +msgctxt "SaveButton|" +msgid "Calculating Print-time" +msgstr "Вычисляется время печати" + +#. Save button label +#: ../resources/qml/SaveButton.qml:197 +msgctxt "SaveButton|" +msgid "Estimated Print-time" +msgstr "Расчетное время печати" + +#. Simple configuration mode option +#: ../resources/qml/Sidebar.qml:105 +msgctxt "Sidebar|" +msgid "Simple" +msgstr "Простой" + +#. Advanced configuration mode option +#: ../resources/qml/Sidebar.qml:107 +msgctxt "Sidebar|" +msgid "Advanced" +msgstr "Продвинутый" + +#. Configuration mode label +#: ../resources/qml/SidebarHeader.qml:26 +msgctxt "SidebarHeader|" +msgid "Mode:" +msgstr "Режим:" + +#. Machine selection label +#: ../resources/qml/SidebarHeader.qml:70 +msgctxt "SidebarHeader|" +msgid "Machine:" +msgstr "Принтер:" + +#. Sidebar header label +#: ../resources/qml/SidebarHeader.qml:117 +msgctxt "SidebarHeader|" +msgid "Print Setup" +msgstr "Настройка Печати" + +#. Sidebar configuration label +#: ../resources/qml/SidebarSimple.qml:40 +msgctxt "SidebarSimple|" +msgid "No Model Loaded" +msgstr "Модель не загружена" + +#. Sidebar configuration label +#: ../resources/qml/SidebarSimple.qml:45 +msgctxt "SidebarSimple|" +msgid "Calculating..." +msgstr "Высчитывается..." + +#. Sidebar configuration label +#: ../resources/qml/SidebarSimple.qml:50 +msgctxt "SidebarSimple|" +msgid "Estimated Print Time" +msgstr "Расчетное время печати" + +#. Quality slider label +#: ../resources/qml/SidebarSimple.qml:87 +msgctxt "SidebarSimple|" +msgid "" +"Minimum\n" +"Draft" +msgstr "" +"Минимальная \n" +"тяга" + +#. Quality slider label +#: ../resources/qml/SidebarSimple.qml:97 +msgctxt "SidebarSimple|" +msgid "" +"Maximum\n" +"Quality" +msgstr "" +"Максимальное\n" +"Качество" + +#. Setting checkbox +#: ../resources/qml/SidebarSimple.qml:109 +msgctxt "SidebarSimple|" +msgid "Enable Support" +msgstr "Включить Поддержку" + +#. View configuration page title +#: ../resources/qml/ViewPage.qml:9 +msgctxt "ViewPage|" +msgid "View" +msgstr "Просмотр" + +#. Display Overhang preference checkbox +#: ../resources/qml/ViewPage.qml:24 +msgctxt "ViewPage|" +msgid "Display Overhang" +msgstr "Показать где есть нависающие части" diff --git a/resources/i18n/ru/fdmprinter.json.po b/resources/i18n/ru/fdmprinter.json.po new file mode 100644 index 0000000000..63e0854aae --- /dev/null +++ b/resources/i18n/ru/fdmprinter.json.po @@ -0,0 +1,1843 @@ +msgid "" +msgstr "" +"Project-Id-Version: Uranium json setting files\n" +"Report-Msgid-Bugs-To: http://github.com/ultimaker/uranium\n" +"POT-Creation-Date: 2015-05-08 12:17+0000\n" +"PO-Revision-Date: 2015-06-09 18:29+0300\n" +"Language-Team: LANGUAGE\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" +"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" +"Last-Translator: \n" +"Language: ru\n" +"X-Generator: Poedit 1.8.1\n" + +#: fdmprinter.json +msgctxt "resolution label" +msgid "Quality" +msgstr "Качество" + +#: fdmprinter.json +msgctxt "layer_height label" +msgid "Layer Height" +msgstr "Высота слоя" + +#: fdmprinter.json +msgctxt "layer_height description" +msgid "" +"The height of each layer, in mm. Normal quality prints are 0.1mm, high " +"quality is 0.06mm. You can go up to 0.25mm with an Ultimaker for very fast " +"prints at low quality. For most purposes, layer heights between 0.1 and " +"0.2mm give a good tradeoff of speed and surface finish." +msgstr "" +"Высота каждого слоя в мм. Нормальное качество печати — это 0.1 мм, а высокое " +"качество печати — это 0.06 мм. Для очень быстрой печати и низкое качество " +"печати, можно поставит высота слоя до 0.25 мм. В большинство случаев печати, " +"используется высота слоя между 0.1 и 0.2 мм, которая дает хорошее качество." + +#: fdmprinter.json +msgctxt "layer_height_0 label" +msgid "Initial Layer Thickness" +msgstr "Первоначальная толщина слоя" + +#: fdmprinter.json +msgctxt "layer_height_0 description" +msgid "" +"The layer thickness of the bottom layer. A thicker bottom layer makes " +"sticking to the bed easier." +msgstr "" +"Толщина нижнего слоя. Чем толще будет нижний слой, тем лучше будет " +"прилипание к платформе." + +#: fdmprinter.json +msgctxt "shell_thickness label" +msgid "Shell Thickness" +msgstr "Толщина стенки" + +#: fdmprinter.json +msgctxt "shell_thickness description" +msgid "" +"The thickness of the outside shell in the horizontal and vertical direction. " +"This is used in combination with the nozzle size to define the number of " +"perimeter lines and the thickness of those perimeter lines. This is also " +"used to define the number of solid top and bottom layers." +msgstr "" +"Толщина внешней стенки в горизонтальном и вертикальном направлении. Это " +"используется в сочетании с размера сопла, для определения количества линий " +"периметра и толщина этих линий периметра. Это также используется для " +"определения количество верхних и нижних слоев." + +#: fdmprinter.json +msgctxt "wall_thickness label" +msgid "Wall Thickness" +msgstr "Толщина стенки" + +#: fdmprinter.json +msgctxt "wall_thickness description" +msgid "" +"The thickness of the outside walls in the horizontal direction. This is used " +"in combination with the nozzle size to define the number of perimeter lines " +"and the thickness of those perimeter lines." +msgstr "" +"Толщина внешней стенки в горизонтальном направлении. Это используется в " +"сочетании с размера сопла для определения количество линий периметра и " +"толщина этих линий периметра." + +#: fdmprinter.json +msgctxt "wall_line_count label" +msgid "Wall Line Count" +msgstr "Количество линий стенки" + +#: fdmprinter.json +msgctxt "wall_line_count description" +msgid "" +"Number of shell lines. This these lines are called perimeter lines in other " +"tools and impact the strength and structural integrity of your print." +msgstr "" +"Количество линий стенки. Эти линии называются линиями по периметру и влияют " +"на ударной прочности и структурной целостности вашей печати." + +#: fdmprinter.json +msgctxt "wall_line_width label" +msgid "Wall Line Width" +msgstr "Ширина линий стенки" + +#: fdmprinter.json +msgctxt "wall_line_width description" +msgid "" +"Width of a single shell line. Each line of the shell will be printed with " +"this width in mind." +msgstr "" +"Ширина одной линий стенки. Каждая линия стенки, будет напечатанная согласно " +"выбранной шириной." + +#: fdmprinter.json +msgctxt "wall_line_width_0 label" +msgid "First Wall Line Width" +msgstr "Ширина первой линий стенки" + +#: fdmprinter.json +msgctxt "wall_line_width_0 description" +msgid "" +"Width of the outermost shell line. By printing a thinner outermost wall line " +"you can print higher details with a larger nozzle." +msgstr "" +"Ширина внешней стенки. При печати тонкой внешней стенки, Вы можете " +"распечатать высокие детали с большим соплом." + +#: fdmprinter.json +msgctxt "wall_line_width_x label" +msgid "Other Walls Line Width" +msgstr "Ширина линий остальных стенок" + +#: fdmprinter.json +msgctxt "wall_line_width_x description" +msgid "" +"Width of a single shell line for all shell lines except the outermost one." +msgstr "" +"Ширина одной линий стенки для всех линий стенки за исключением на самых " +"крайних." + +#: fdmprinter.json +msgctxt "skirt_line_width label" +msgid "Skirt line width" +msgstr "Ширина линий контура" + +#: fdmprinter.json +msgctxt "skirt_line_width description" +msgid "Width of a single skirt line." +msgstr "Ширина одной линий контура" + +#: fdmprinter.json +msgctxt "skin_line_width label" +msgid "Top/bottom line width" +msgstr "Ширина Верхней/Нижней линий" + +#: fdmprinter.json +msgctxt "skin_line_width description" +msgid "" +"Width of a single top/bottom printed line. Which are used to fill up the top/" +"bottom areas of a print." +msgstr "" +"Ширина одной верхней/нижней линий, которые используются для заполнения " +"верхней/нижней область печатающего объекта." + +#: fdmprinter.json +msgctxt "infill_line_width label" +msgid "Infill line width" +msgstr "Ширина линий заполнения" + +#: fdmprinter.json +msgctxt "infill_line_width description" +msgid "Width of the inner infill printed lines." +msgstr "Ширина линий внутреннего заполнения." + +#: fdmprinter.json +msgctxt "support_line_width label" +msgid "Support line width" +msgstr "Ширина линий поддержек" + +#: fdmprinter.json +msgctxt "support_line_width description" +msgid "Width of the printed support structures lines." +msgstr "Ширина линий поддерживающих структур." + +#: fdmprinter.json +msgctxt "top_bottom_thickness label" +msgid "Bottom/Top Thickness" +msgstr "Толщина верхнего/нижнего слоя" + +#: fdmprinter.json +msgctxt "top_bottom_thickness description" +msgid "" +"This controls the thickness of the bottom and top layers, the amount of " +"solid layers put down is calculated by the layer thickness and this value. " +"Having this value a multiple of the layer thickness makes sense. And keep it " +"near your wall thickness to make an evenly strong part." +msgstr "" +"Это контролирует толщину нижнего и верхнего слоев. Количество сплошных слоев " +"определяется толщины слоя и этим значением. Имея это значение, имеет смысл " +"использовать многократную толщину слоя и при этом приблизительно выровнять " +"ее с толщиной стенки, чтобы сделать равномерно прочный объект." + +#: fdmprinter.json +msgctxt "top_thickness label" +msgid "Top Thickness" +msgstr "Толщина верхнего слоя" + +#: fdmprinter.json +msgctxt "top_thickness description" +msgid "" +"This controls the thickness of the top layers. The number of solid layers " +"printed is calculated from the layer thickness and this value. Having this " +"value be a multiple of the layer thickness makes sense. And keep it nearto " +"your wall thickness to make an evenly strong part." +msgstr "" +"Это контролирует толщину верхних слоев. Количество сплошных слоев " +"определяется толщины слоя и этим значением. Имея это значение, имеет смысл " +"использовать многократную толщину слоя и при этом приблизительно выровнять " +"ее с толщиной стенки, чтобы сделать равномерно прочный объект." + +#: fdmprinter.json +msgctxt "top_layers label" +msgid "Top Layers" +msgstr "Верхние слой" + +#: fdmprinter.json +msgctxt "top_layers description" +msgid "This controls the amount of top layers." +msgstr "Это контролирует количество верхних слоев." + +#: fdmprinter.json +msgctxt "bottom_thickness label" +msgid "Bottom Thickness" +msgstr "Толщина нижнего слоя" + +#: fdmprinter.json +msgctxt "bottom_thickness description" +msgid "" +"This controls the thickness of the bottom layers. The number of solid layers " +"printed is calculated from the layer thickness and this value. Having this " +"value be a multiple of the layer thickness makes sense. And keep it near to " +"your wall thickness to make an evenly strong part." +msgstr "" +"Это контролирует толщину нижних слоев. Количество сплошных слоев " +"определяется толщины слоя и этим значением. Имея это значение, имеет смысл " +"использовать многократную толщину слоя и при этом приблизительно выровнять " +"ее с толщиной стенки, чтобы сделать равномерно прочный объект." + +#: fdmprinter.json +msgctxt "bottom_layers label" +msgid "Bottom Layers" +msgstr "Нижние слой" + +#: fdmprinter.json +msgctxt "bottom_layers description" +msgid "This controls the amount of bottom layers." +msgstr "Это контролирует количество нижних слоев." + +#: fdmprinter.json +msgctxt "wall_overlap_avoid_enabled label" +msgid "Avoid Overlapping Walls" +msgstr "Избежать перекрывание стен" + +#: fdmprinter.json +msgctxt "wall_overlap_avoid_enabled description" +msgid "" +"Remove parts of a wall which share an overlap which would result in " +"overextrusion in some places. These overlaps occur in thin pieces in a model " +"and sharp corners." +msgstr "" +"Удалить части стен, которые разделяют перекрытие, которое в некоторых местах " +"может привести к увеличении экструзии. Эти перекрытия появляются в модели с " +"тонких частей и острых углов." + +#: fdmprinter.json +msgctxt "top_bottom_pattern label" +msgid "Bottom/Top Pattern" +msgstr "Нижняя/Верхняя структура" + +#: fdmprinter.json +msgctxt "top_bottom_pattern description" +msgid "" +"Pattern of the top/bottom solid fill. This normally is done with lines to " +"get the best possible finish, but in some cases a concentric fill gives a " +"nicer end result." +msgstr "Структура верхнего/нижнего заполнения." + +#: fdmprinter.json +msgctxt "skin_outline_count label" +msgid "Skin Perimeter Line Count" +msgstr "Количество линий периметра оболочки" + +#: fdmprinter.json +msgctxt "skin_outline_count description" +msgid "" +"Number of lines around skin regions. Using one or two skin perimeter lines " +"can greatly improve on roofs which would start in the middle of infill cells." +msgstr "" +"Количество линий вокруг оболочки. Использование одного или двух линий по " +"периметру оболочки может значительно улучшить покрытия, которые начнутся в " +"середине заполняющих сеток." + +#: fdmprinter.json +msgctxt "xy_offset label" +msgid "Horizontal expansion" +msgstr "Горизонтальное расширение" + +#: fdmprinter.json +msgctxt "xy_offset description" +msgid "" +"Amount of offset applied all polygons in each layer. Positive values can " +"compensate for too big holes; negative values can compensate for too small " +"holes." +msgstr "" +"Количество всех смещении полигонов в каждый слой. Положительные значения, " +"компенсируют для слишком большие отверстия; а отрицательные значения, " +"компенсируют для очень маленькие отверстия." + +#: fdmprinter.json +msgctxt "material label" +msgid "Material" +msgstr "Материал" + +#: fdmprinter.json +msgctxt "material_print_temperature label" +msgid "Printing Temperature" +msgstr "Температура печати" + +#: fdmprinter.json +msgctxt "material_print_temperature description" +msgid "" +"The temperature used for printing. Set at 0 to pre-heat yourself. For PLA a " +"value of 210C is usually used.\n" +"For ABS a value of 230C or higher is required." +msgstr "" +"Температура, используемая для печати. Установите на значение 0 для " +"предварительного нагрева. Для PLA обычно используется температурный режим " +"210C.\n" +"Для ABS обычно используется температурный режим 230C или выше." + +#: fdmprinter.json +msgctxt "material_bed_temperature label" +msgid "Bed Temperature" +msgstr "Температура платформы" + +#: fdmprinter.json +msgctxt "material_bed_temperature description" +msgid "" +"The temperature used for the heated printer bed. Set at 0 to pre-heat it " +"yourself." +msgstr "" +"Температура, используемая для нагревающей платформы. Установите значение 0 " +"для предварительного нагрева." + +#: fdmprinter.json +msgctxt "material_diameter label" +msgid "Diameter" +msgstr "Диаметр" + +#: fdmprinter.json +msgctxt "material_diameter description" +msgid "" +"The diameter of your filament needs to be measured as accurately as " +"possible.\n" +"If you cannot measure this value you will have to calibrate it, a higher " +"number means less extrusion, a smaller number generates more extrusion." +msgstr "" +"Диаметр вашей нити необходимо вымерить как можно точнее.\n" +"Если вы не можете измерить это значение, вы должны откалибровать его, " +"большое число означает меньше выдавливания материала, а меньшее число " +"производит больше выдавливания пластика." + +#: fdmprinter.json +msgctxt "material_flow label" +msgid "Flow" +msgstr "Выдавливания материала" + +#: fdmprinter.json +msgctxt "material_flow description" +msgid "" +"Flow compensation: the amount of material extruded is multiplied by this " +"value." +msgstr "" +"Компенсация выдавливания: количество выдавленного материала умножается с " +"этим значением." + +#: fdmprinter.json +msgctxt "retraction_enable label" +msgid "Enable Retraction" +msgstr "Включит откат материала" + +#: fdmprinter.json +msgctxt "retraction_enable description" +msgid "" +"Retract the filament when the nozzle is moving over a non-printed area. " +"Details about the retraction can be configured in the advanced tab." +msgstr "" +"Откатывает материал назад, когда печатающая головка перемещается над " +"областью. Подробности по поводу отката материала можете найти в вкладке " +"\"дополнительно\"." + +#: fdmprinter.json +msgctxt "retraction_speed label" +msgid "Retraction Speed" +msgstr "Скорость отката" + +#: fdmprinter.json +msgctxt "retraction_speed description" +msgid "" +"The speed at which the filament is retracted. A higher retraction speed " +"works better, but a very high retraction speed can lead to filament grinding." +msgstr "" +"Скорость, при которой происходит откат материала. Высокая скорость отката " +"материала работает лучше, но очень высокая скорость отката может привести к " +"перемалывания материала с стороне механизма подачи." + +#: fdmprinter.json +msgctxt "retraction_retract_speed label" +msgid "Retraction Retract Speed" +msgstr "Скорость отката" + +#: fdmprinter.json +msgctxt "retraction_retract_speed description" +msgid "" +"The speed at which the filament is retracted. A higher retraction speed " +"works better, but a very high retraction speed can lead to filament grinding." +msgstr "" +"Скорость, при которой происходит откат материала. Высокая скорость отката " +"материала работает лучше, но очень высокая скорость отката может привести к " +"перемалывания материала с стороне механизма подачи." + +#: fdmprinter.json +msgctxt "retraction_prime_speed label" +msgid "Retraction Prime Speed" +msgstr "Скорость после отката" + +#: fdmprinter.json +msgctxt "retraction_prime_speed description" +msgid "The speed at which the filament is pushed back after retraction." +msgstr "Скорость, при которой материал проталкивается обратно после отката." + +#: fdmprinter.json +msgctxt "retraction_amount label" +msgid "Retraction Distance" +msgstr "Расстояние отката" + +#: fdmprinter.json +msgctxt "retraction_amount description" +msgid "" +"The amount of retraction: Set at 0 for no retraction at all. A value of " +"4.5mm seems to generate good results for 3mm filament in Bowden-tube fed " +"printers." +msgstr "" +"Количество отката материала: Установите значение 0, чтобы не происходил " +"откат материала. Значение 4.5 мм достаточное, чтобы получить хорошие " +"результаты при использовании материал, диаметром 3 мм для принтеров где " +"подача материала происходит через Боуден-трубки." + +#: fdmprinter.json +msgctxt "retraction_min_travel label" +msgid "Retraction Minimum Travel" +msgstr "Минимальное перемещение" + +#: fdmprinter.json +msgctxt "retraction_min_travel description" +msgid "" +"The minimum distance of travel needed for a retraction to happen at all. " +"This helps ensure you do not get a lot of retractions in a small area." +msgstr "" +"Минимальное расстояние перемещения для того, чтобы произошел откат " +"материала. Эта настройка обеспечивает, чтобы не происходило слишком большое " +"количество откатов на небольшой области печати." + +#: fdmprinter.json +msgctxt "retraction_combing label" +msgid "Enable Combing" +msgstr "Включит обход" + +#: fdmprinter.json +msgctxt "retraction_combing description" +msgid "" +"Combing keeps the head within the interior of the print whenever possible " +"when traveling from one part of the print to another, and does not use " +"retraction. If combing is disabled the printer head moves straight from the " +"start point to the end point and it will always retract." +msgstr "" +"Обход, это когда печатающая головка позиционируется внутри области печати, " +"при перемещении от одной в другой точке без отката. Если откат выключен, " +"печатающая головка будет перемещаться от начальной до конечной точки " +"напрямую и всегда с откатом." + +#: fdmprinter.json +msgctxt "retraction_minimal_extrusion label" +msgid "Minimal Extrusion Before Retraction" +msgstr "Минимальное выдавливание перед откатом" + +#: fdmprinter.json +msgctxt "retraction_minimal_extrusion description" +msgid "" +"The minimum amount of extrusion that needs to happen between retractions. " +"If a retraction should happen before this minimum is reached, it will be " +"ignored. This avoids retracting repeatedly on the same piece of filament as " +"that can flatten the filament and cause grinding issues." +msgstr "" +"Минимальное количество выдавливаемого материала, необходимого для включения " +"отката. Если не достигнуто минимальное количество выдавливания материала, " +"тогда откат не произойдет. Это предотвращает, чтобы откат повторялся в одно " +"и тоже место, так как это приведет к перемалыванием нити." + +#: fdmprinter.json +msgctxt "retraction_hop label" +msgid "Z Hop when Retracting" +msgstr "Подъем по ось Z когда происходит откат" + +#: fdmprinter.json +msgctxt "retraction_hop description" +msgid "" +"Whenever a retraction is done, the head is lifted by this amount to travel " +"over the print. A value of 0.075 works well. This feature has a lot of " +"positive effect on delta towers." +msgstr "" +"Когда откат закончен, печатающий стол немного опускается. Нормальное " +"значение 0,075 мм. При перемещении печатающей головки от точки А к точке В, " +"уменьшается возможность цепляния модели или рисования полосы на поверхности " +"объекта." + +#: fdmprinter.json +msgctxt "speed label" +msgid "Speed" +msgstr "Скорость" + +#: fdmprinter.json +msgctxt "speed_print label" +msgid "Print Speed" +msgstr "Скорость печати" + +#: fdmprinter.json +msgctxt "speed_print description" +msgid "" +"The speed at which printing happens. A well-adjusted Ultimaker can reach " +"150mm/s, but for good quality prints you will want to print slower. Printing " +"speed depends on a lot of factors, so you will need to experiment with " +"optimal settings for this." +msgstr "" +"Скорость, при которой происходит процесс печати. Хорошо настроенный принтер, " +"может достигать скорость печати до 150 мм/сек, но, для получения хорошее " +"качество печати, рекомендуется печатать на низких скоростях. Скорость печати " +"зависит от многих факторов, поэтому для этого необходимо экспериментировать " +"с оптимальными настройками." + +#: fdmprinter.json +msgctxt "speed_infill label" +msgid "Infill Speed" +msgstr "Скорость заполнения" + +#: fdmprinter.json +msgctxt "speed_infill description" +msgid "" +"The speed at which infill parts are printed. Printing the infill faster can " +"greatly reduce printing time, but this can negatively affect print quality." +msgstr "" +"Скорость, при которой происходит заполнение. Печатать заполнения с более " +"высокой скоростью, значительно уменьшит время печати, но, это может " +"отрицательно повлиять на качество печати." + +#: fdmprinter.json +msgctxt "speed_wall label" +msgid "Shell Speed" +msgstr "Скорость печати стенки" + +#: fdmprinter.json +msgctxt "speed_wall description" +msgid "" +"The speed at which shell is printed. Printing the outer shell at a lower " +"speed improves the final skin quality." +msgstr "" +"Скорость, при которой печатается стенка. Печать внешней стенки с более " +"низкой скоростью улучшает качество поверхности." + +#: fdmprinter.json +msgctxt "speed_wall_0 label" +msgid "Outer Shell Speed" +msgstr "Скорость печати внешней стенки" + +#: fdmprinter.json +msgctxt "speed_wall_0 description" +msgid "" +"The speed at which outer shell is printed. Printing the outer shell at a " +"lower speed improves the final skin quality. However, having a large " +"difference between the inner shell speed and the outer shell speed will " +"effect quality in a negative way." +msgstr "" +"Скорость, при которой печатается внешняя стенка. Печать внешней стенки с " +"более низкой скоростью улучшает качество стенки. Но, большая разница между " +"скоростях печати внутренней и внешней стенки может отрицательно повлиять на " +"качество поверхности." + +#: fdmprinter.json +msgctxt "speed_wall_x label" +msgid "Inner Shell Speed" +msgstr "Скорость печати внутренней стенки" + +#: fdmprinter.json +msgctxt "speed_wall_x description" +msgid "" +"The speed at which all inner shells are printed. Printing the inner shell " +"fasster than the outer shell will reduce printing time. It is good to set " +"this in between the outer shell speed and the infill speed." +msgstr "" +"Скорость, при которой печатается внутренняя стенка. Если печатать внутренней " +"стенки быстрее чем внешней стенки, можно уменьшит время печати. Хорошо " +"установить это значение между скоростях печати внешней стенки и заполнение." + +#: fdmprinter.json +msgctxt "speed_topbottom label" +msgid "Top/Bottom Speed" +msgstr "Скорость печати верхнего/нижнего слоя" + +#: fdmprinter.json +msgctxt "speed_topbottom description" +msgid "" +"Speed at which top/bottom parts are printed. Printing the top/bottom faster " +"can greatly reduce printing time, but this can negatively affect print " +"quality." +msgstr "" +"Скорость, при которой печатаются верхние/нижние части. Печатать верх/низ при " +"больших скоростях, может значительно уменьшит время печати, но, это может " +"негативно повлиять на качество печати." + +#: fdmprinter.json +msgctxt "speed_support label" +msgid "Support Speed" +msgstr "Скорость печати поддержек" + +#: fdmprinter.json +msgctxt "speed_support description" +msgid "" +"The speed at which exterior support is printed. Printing exterior supports " +"at higher speeds can greatly improve printing time. And the surface quality " +"of exterior support is usually not important, so higher speeds can be used." +msgstr "" +"Скорость, при которой печатается поддержка. Печатать поддержек при больших " +"скоростях, может значительно улучшить время печати. Качество печати " +"поддержек не является важным и при этом можно использовать большие скорости " +"печати." + +#: fdmprinter.json +msgctxt "speed_travel label" +msgid "Travel Speed" +msgstr "Скорость перемещения " + +#: fdmprinter.json +msgctxt "speed_travel description" +msgid "" +"The speed at which travel moves are done. A well-built Ultimaker can reach " +"speeds of 250mm/s. But some machines might have misaligned layers then." +msgstr "" +"Скорость перемещения печатающей головки. Хорошо настроенный Ultimaker может " +"достигать скорость перемещения до 250 мм/сек. К некоторым принтерам есть " +"возможность появления неровных слоев." + +#: fdmprinter.json +msgctxt "speed_layer_0 label" +msgid "Bottom Layer Speed" +msgstr "Скорость печати нижнего слоя" + +#: fdmprinter.json +msgctxt "speed_layer_0 description" +msgid "" +"The print speed for the bottom layer: You want to print the first layer " +"slower so it sticks to the printer bed better." +msgstr "" +"Скорость, при которой печатается нижний слой: рекомендуется печатать первого " +"слоя на низкую скорость, чтобы получит лучшее прилипание (адгезию) к " +"печатающей платформы." + +#: fdmprinter.json +msgctxt "skirt_speed label" +msgid "Skirt Speed" +msgstr "Скорость печати контур" + +#: fdmprinter.json +msgctxt "skirt_speed description" +msgid "" +"The speed at which the skirt and brim are printed. Normally this is done at " +"the initial layer speed. But sometimes you want to print the skirt at a " +"different speed." +msgstr "" +"Скорость, при которой печатаются контур и кайма. Обычно это делается при " +"определения начальной скорости слоя, но, иногда есть потребность распечатать " +"подложку с другой скоростью." + +#: fdmprinter.json +msgctxt "speed_slowdown_layers label" +msgid "Amount of Slower Layers" +msgstr "Количество печати медленных слоев" + +#: fdmprinter.json +msgctxt "speed_slowdown_layers description" +msgid "" +"The first few layers are printed slower then the rest of the object, this to " +"get better adhesion to the printer bed and improve the overall success rate " +"of prints. The speed is gradually increased over these layers. 4 layers of " +"speed-up is generally right for most materials and printers." +msgstr "" +"Первые несколько слоев печатаются медленнее чем остальных, чтобы получит " +"лучшее прилипание к печатающей платформы и в целом улучшит качество печати. " +"Скорость постепенно увеличивается на последующих 3-ех слоев. Ускорение по 4 " +"слоя, как правило, подходит для большинство материалов и принтеров." + +#: fdmprinter.json +msgctxt "infill label" +msgid "Infill" +msgstr "Заполнение" + +#: fdmprinter.json +msgctxt "fill_sparse_density label" +msgid "Infill Density" +msgstr "Плотность заполнения" + +#: fdmprinter.json +msgctxt "fill_sparse_density description" +msgid "" +"This controls how densely filled the insides of your print will be. For a " +"solid part use 100%, for an hollow part use 0%. A value around 20% is " +"usually enough. This won't affect the outside of the print and only adjusts " +"how strong the part becomes." +msgstr "" +"Контролирует насколько плотно будет заполненные печатающего объекта. Для " +"прочности используйте 100% заполнение, а для пустоты используйте значение " +"0%. 20% заполнение является вполне достаточным. Это не повлияет на " +"внешностью печатающего объекта, а именно регулирует насколько будет прочный " +"сам объект." + +#: fdmprinter.json +msgctxt "fill_pattern label" +msgid "Infill Pattern" +msgstr "Структура заполнения" + +#: fdmprinter.json +msgctxt "fill_pattern description" +msgid "" +"Cura defaults to switching between grid and line infill. But with this " +"setting visible you can control this yourself. The line infill swaps " +"direction on alternate layers of infill, while the grid prints the full " +"cross-hatching on each layer of infill." +msgstr "" +"В Кура есть две структуры заполнения: сетки и линий. Структура заполнения " +"линий, меняет направление на чередующихся слой, которые заполняются, а " +"структура сетки печатает полной штриховкой на каждом слое, который " +"заполняется." + +#: fdmprinter.json +msgctxt "infill_line_distance label" +msgid "Line distance" +msgstr "Расстояние линий" + +#: fdmprinter.json +msgctxt "infill_line_distance description" +msgid "Distance between the printed infill lines." +msgstr "Расстояние между линиями заполнения" + +#: fdmprinter.json +msgctxt "fill_overlap label" +msgid "Infill Overlap" +msgstr "Заполнения перекрытия" + +#: fdmprinter.json +msgctxt "fill_overlap description" +msgid "" +"The amount of overlap between the infill and the walls. A slight overlap " +"allows the walls to connect firmly to the infill." +msgstr "" +"Количество перекрытия между заполнением и стенки. Небольшое перекрытие " +"улучшает соединение/сцепление стенок и их заполнение." + +#: fdmprinter.json +msgctxt "fill_sparse_thickness label" +msgid "Infill Thickness" +msgstr "Толщина заполнения" + +#: fdmprinter.json +msgctxt "fill_sparse_thickness description" +msgid "" +"The thickness of the sparse infill. This is rounded to a multiple of the " +"layerheight and used to print the sparse-infill in fewer, thicker layers to " +"save printing time." +msgstr "" +"Толщина разреженного заполнения. Это значение определяется согласно " +"многократного умножения высоты слоя и используется для печати разреженного " +"заполнения в нескольких толстых слоев, чтобы сэкономить время печати." + +#: fdmprinter.json +msgctxt "fill_sparse_combine label" +msgid "Infill Layers" +msgstr "Заполнение слоев" + +#: fdmprinter.json +msgctxt "fill_sparse_combine description" +msgid "Amount of layers that are combined together to form sparse infill." +msgstr "" +"Количество слоев, которые объединены вместе, чтобы образовать разреженное " +"заполнение." + +#: fdmprinter.json +msgctxt "cooling label" +msgid "Cooling" +msgstr "Охлаждение" + +#: fdmprinter.json +msgctxt "cool_fan_enabled label" +msgid "Enable Cooling Fan" +msgstr "Включит охлаждающий вентилятор" + +#: fdmprinter.json +msgctxt "cool_fan_enabled description" +msgid "" +"Enable the cooling fan during the print. The extra cooling from the cooling " +"fan helps parts with small cross sections that print each layer quickly." +msgstr "" +"Включит охлаждающий вентилятор во время печати. Дополнительное охлаждение " +"вентиляторов позволяет печатать каждого слоя быстрее." + +#: fdmprinter.json +msgctxt "cool_fan_speed label" +msgid "Fan Speed" +msgstr "Скорость вращения вентиляторов" + +#: fdmprinter.json +msgctxt "cool_fan_speed description" +msgid "Fan speed used for the print cooling fan on the printer head." +msgstr "" +"Скорость вращения вентилятора, которая используется для охлаждения " +"печатающей головки." + +#: fdmprinter.json +msgctxt "cool_fan_speed_min label" +msgid "Minimum Fan Speed" +msgstr "Минимальная скорость вращения вентиляторов" + +#: fdmprinter.json +msgctxt "cool_fan_speed_min description" +msgid "" +"Normally the fan runs at the minimum fan speed. If the layer is slowed down " +"due to minimum layer time, the fan speed adjusts between minimum and maximum " +"fan speed." +msgstr "" +"Обычно вентилятор работает на минимальной скорости вращения. Если слой " +"замедлился из-за минимальное время слоя, скорость вентилятора регулируется " +"между минимальной и максимальной скорости вентилятора." + +#: fdmprinter.json +msgctxt "cool_fan_speed_max label" +msgid "Maximum Fan Speed" +msgstr "Максимальная скорость вращения вентиляторов" + +#: fdmprinter.json +msgctxt "cool_fan_speed_max description" +msgid "" +"Normally the fan runs at the minimum fan speed. If the layer is slowed down " +"due to minimum layer time, the fan speed adjusts between minimum and maximum " +"fan speed." +msgstr "" +"Обычно вентилятор работает на минимальной скорости вращения. Если слой " +"замедлился из-за минимальное время слоя, скорость вентилятора регулируется " +"между минимальной и максимальной скорости вентилятора." + +#: fdmprinter.json +msgctxt "cool_fan_full_at_height label" +msgid "Fan Full on at Height" +msgstr "Полный запуск вентиляторов на определенной высоте" + +#: fdmprinter.json +msgctxt "cool_fan_full_at_height description" +msgid "" +"The height at which the fan is turned on completely. For the layers below " +"this the fan speed is scaled linearly with the fan off for the first layer." +msgstr "" +"Высота, на которой вентилятор включен полностью. Для слоев ниже этого, " +"скорость вращения вентилятора масштабируется линейно с выключенным " +"вентилятором для печати первого слоя." + +#: fdmprinter.json +msgctxt "cool_fan_full_layer label" +msgid "Fan Full on at Layer" +msgstr "Полный запуск вентиляторов при определенное количество слоев" + +#: fdmprinter.json +msgctxt "cool_fan_full_layer description" +msgid "" +"The layer number at which the fan is turned on completely. For the layers " +"below this the fan speed is scaled linearly with the fan off for the first " +"layer." +msgstr "" +"Число слоя, при котором вентилятор полностью включается. Для слоев ниже " +"этого значения, скорость вентилятора масштабируется линейно с выключенным " +"вентилятором для печати первого слоя." + +#: fdmprinter.json +msgctxt "cool_min_layer_time label" +msgid "Minimal Layer Time" +msgstr "Минимальное время накладывания слоев" + +#: fdmprinter.json +msgctxt "cool_min_layer_time description" +msgid "" +"The minimum time spent in a layer: Gives the layer time to cool down before " +"the next one is put on top. If a layer would print in less time, then the " +"printer will slow down to make sure it has spent at least this many seconds " +"printing the layer." +msgstr "" +"Минимальное время, требуемое для печати одного слоя, позволяет слою остит " +"перед тем, как начать накладывать следующего слоя. Если слой будут " +"печататься слишком быстро, тогда принтер замедлиться, чтобы соответствовать " +"данному значению." + +#: fdmprinter.json +msgctxt "cool_min_layer_time_fan_speed_max label" +msgid "Minimal Layer Time Full Fan Speed" +msgstr "Полный запуск вентиляторов при минимальное время накладывания слоев" + +#: fdmprinter.json +msgctxt "cool_min_layer_time_fan_speed_max description" +msgid "" +"The minimum time spent in a layer which will cause the fan to be at minmum " +"speed. The fan speed increases linearly from maximal fan speed for layers " +"taking minimal layer time to minimal fan speed for layers taking the time " +"specified here." +msgstr "" +"Минимальное время, требуемое для печати одного слоя, которое вызовет, чтобы " +"вентилятор вращался с минимальной скоростью. Скорость вращения вентилятора " +"увеличивается линейно от максимуме для слоев с минимальное время печати до " +"минимальное вращение вентиляторов для слоев, которые придерживаются время " +"здесь указанное." + +#: fdmprinter.json +msgctxt "cool_min_speed label" +msgid "Minimum Speed" +msgstr "Минимальная скорость" + +#: fdmprinter.json +msgctxt "cool_min_speed description" +msgid "" +"The minimum layer time can cause the print to slow down so much it starts to " +"droop. The minimum feedrate protects against this. Even if a print gets " +"slowed down it will never be slower than this minimum speed." +msgstr "" +"Минимальное время для печати слоев, может вызвать, чтобы процесс печати " +"замедлился на столько, что начинает свисать. Минимальная скорость подачи " +"предотвращает, чтобы произошло нависание. Даже если процесс печати " +"замедлиться, он никогда не будет медленнее чем минимальной скоростью." + +#: fdmprinter.json +msgctxt "cool_lift_head label" +msgid "Lift Head" +msgstr "Подъем печатающей головки" + +#: fdmprinter.json +msgctxt "cool_lift_head description" +msgid "" +"Lift the head away from the print if the minimum speed is hit because of " +"cool slowdown, and wait the extra time away from the print surface until the " +"minimum layer time is used up." +msgstr "" +"Если достигнутая минимальная скорость , из-за необходимости постепенного " +"охлаждения, поднять печатающую головку и подождать до достижения " +"минимального времени печати слоя." + +#: fdmprinter.json +msgctxt "support label" +msgid "Support" +msgstr "Поддержка" + +#: fdmprinter.json +msgctxt "support_enable label" +msgid "Enable Support" +msgstr "Включит поддержку" + +#: fdmprinter.json +msgctxt "support_enable description" +msgid "" +"Enable exterior support structures. This will build up supporting structures " +"below the model to prevent the model from sagging or printing in mid air." +msgstr "" +"Включите поддержку для внутренних структур. Это построить поддерживающих " +"структур под основного объекта, чтобы предотвратить модели от провисания или " +"печать по воздухе." + +#: fdmprinter.json +msgctxt "support_type label" +msgid "Placement" +msgstr "Размещение" + +#: fdmprinter.json +msgctxt "support_type description" +msgid "" +"Where to place support structures. The placement can be restricted such that " +"the support structures won't rest on the model, which could otherwise cause " +"scarring." +msgstr "" +"Где разместить структур поддержки. Размещение может быть ограничено таким " +"образом, что опорные конструкции не будут опираться на основного объекта, " +"которые в противном случае приведут к образованию отпечатков." + +#: fdmprinter.json +msgctxt "support_angle label" +msgid "Overhang Angle" +msgstr "Угол нависания" + +#: fdmprinter.json +msgctxt "support_angle description" +msgid "" +"The maximum angle of overhangs for which support will be added. With 0 " +"degrees being horizontal, and 90 degrees being vertical." +msgstr "" +"Максимальный угол нависания, для построения поддержки. 0 градусов будет " +"горизонтальны, а 90 градусов будет по вертикали." + +#: fdmprinter.json +msgctxt "support_xy_distance label" +msgid "X/Y Distance" +msgstr "Расстояние по оси X/Y" + +#: fdmprinter.json +msgctxt "support_xy_distance description" +msgid "" +"Distance of the support structure from the print, in the X/Y directions. " +"0.7mm typically gives a nice distance from the print so the support does not " +"stick to the surface." +msgstr "" +"Расстояние структуры поддержки от печатаемого объекта в направлении X и Y. " +"Нормальное значение от 0.7 мм обычно дает хорошее расстояние от печатаемого " +"объекта, чтобы поддерживающая структура не прилипала к основного объекта." + +#: fdmprinter.json +msgctxt "support_z_distance label" +msgid "Z Distance" +msgstr "Расстояние по ось Z" + +#: fdmprinter.json +msgctxt "support_z_distance description" +msgid "" +"Distance from the top/bottom of the support to the print. A small gap here " +"makes it easier to remove the support but makes the print a bit uglier. " +"0.15mm allows for easier separation of the support structure." +msgstr "" +"Расстояние от верхней/нижней поддержки до печатающего объекта. Маленькое " +"расстояние обеспечивает легкое удаление поддерживающих структур, но, при " +"этом дает невысокое качество печати поверхностью. Значение 0.15 мм " +"достаточное для легкое удаление поддерживающих структур." + +#: fdmprinter.json +msgctxt "support_top_distance label" +msgid "Top Distance" +msgstr "Верхнее расстояние" + +#: fdmprinter.json +msgctxt "support_top_distance description" +msgid "Distance from the top of the support to the print." +msgstr "" +"Расстояние от верхней части поддерживающей структуры до печатающего объекта." + +#: fdmprinter.json +msgctxt "support_bottom_distance label" +msgid "Bottom Distance" +msgstr "Нижнее расстояние" + +#: fdmprinter.json +msgctxt "support_bottom_distance description" +msgid "Distance from the print to the bottom of the support." +msgstr "" +"Расстояние от печатающего объекта до нижнюю часть поддерживающей структуры." + +#: fdmprinter.json +msgctxt "support_bottom_stair_step_height label" +msgid "Stair Step Height" +msgstr "Ступенчатая высота" + +#: fdmprinter.json +msgctxt "support_bottom_stair_step_height description" +msgid "" +"The height of the steps of the stair-like bottom of support resting on the " +"model. Small steps can cause the support to be hard to remove from the top " +"of the model." +msgstr "" +"Высота ступеней, ступенчатого низа поддерживающей структуры, которая " +"опирается на основного объекта. Небольшие лестницы, могут усложнит удаление " +"поддерживающих структур с верхней части основного объекта." + +#: fdmprinter.json +msgctxt "support_join_distance label" +msgid "Join Distance" +msgstr "Соединить расстояние" + +#: fdmprinter.json +msgctxt "support_join_distance description" +msgid "" +"The maximum distance between support blocks, in the X/Y directions, such " +"that the blocks will merge into a single block." +msgstr "" +"Максимальное расстояние между поддерживающих блоков в направлении X и Y, так " +"чтобы блоки соединились в один блок." + +#: fdmprinter.json +msgctxt "support_area_smoothing label" +msgid "Area Smoothing" +msgstr "Область сглаживания" + +#: fdmprinter.json +msgctxt "support_area_smoothing description" +msgid "" +"Maximal distance in the X/Y directions of a line segment which is to be " +"smoothed out. Ragged lines are introduced by the join distance and support " +"bridge, which cause the machine to resonate. Smoothing the support areas " +"won't cause them to break with the constraints, except it might change the " +"overhang." +msgstr "" +"Минимальное расстояние в направлении X/Y отрезки, которое должно быть " +"сглажено. Рваные линии представленные с стороне соединённых расстояния и " +"поддерживающего моста, которые вызывают, чтобы машина резонировала. " +"Сглаживание поддерживающих областей не вызовет, чтобы они сломались с " +"ограничениями, за исключением того, что может измениться нависание." + +#: fdmprinter.json +msgctxt "support_use_towers label" +msgid "Use towers." +msgstr "Использовать башни." + +#: fdmprinter.json +msgctxt "support_use_towers description" +msgid "" +"Use specialized towers to support tiny overhang areas. These towers have a " +"larger diameter than the region they support. Near the overhang the towers' " +"diameter decreases, forming a roof." +msgstr "" +"Используйте специализированные башни, чтобы поддержать маленькие нависающие " +"области. Эти башни имеют диаметр больше, чем область, которую они " +"поддерживают." + +#: fdmprinter.json +msgctxt "support_minimal_diameter label" +msgid "Minimal Diameter" +msgstr "Минимальный диаметр" + +#: fdmprinter.json +msgctxt "support_minimal_diameter description" +msgid "" +"Maximal diameter in the X/Y directions of a small area which is to be " +"supported by a specialized support tower. " +msgstr "" +"Максимальный диаметр в направлении X/Y на небольшой области, который будет " +"поддерживаться с помощью специализированной башню." + +#: fdmprinter.json +msgctxt "support_tower_diameter label" +msgid "Tower Diameter" +msgstr "Диаметр башни" + +#: fdmprinter.json +msgctxt "support_tower_diameter description" +msgid "The diameter of a special tower. " +msgstr "Диаметр специальной башне." + +#: fdmprinter.json +msgctxt "support_tower_roof_angle label" +msgid "Tower Roof Angle" +msgstr "Угол крыши башни" + +#: fdmprinter.json +msgctxt "support_tower_roof_angle description" +msgid "" +"The angle of the rooftop of a tower. Larger angles mean more pointy towers. " +msgstr "" +"Угол верхнего покрытия башни. Большие углы означает получит более острые " +"башни." + +#: fdmprinter.json +msgctxt "support_pattern label" +msgid "Pattern" +msgstr "Структура" + +#: fdmprinter.json +msgctxt "support_pattern description" +msgid "" +"Cura supports 3 distinct types of support structure. First is a grid based " +"support structure which is quite solid and can be removed as 1 piece. The " +"second is a line based support structure which has to be peeled off line by " +"line. The third is a structure in between the other two; it consists of " +"lines which are connected in an accordeon fashion." +msgstr "" +"Кура поддерживает 3 типа поддерживающих структур. Сетка - очень прочная " +"структура поддержек и может быт удалена как одно целое. Линия - структура " +"поддержек, которая удаляется по одной за раз. Третий тип структур поддержек " +"это структура между предыдущих два типа, и состоится из линий, которые " +"соединены в последовательной формы." + +#: fdmprinter.json +msgctxt "support_connect_zigzags label" +msgid "Connect ZigZags" +msgstr "Подключите ZigZags" + +#: fdmprinter.json +msgctxt "support_connect_zigzags description" +msgid "" +"Connect the ZigZags. Makes them harder to remove, but prevents stringing of " +"disconnected zigzags." +msgstr "" +"Соединяет ZigZags. Их труднее удалить, но при этом предотвращает появления " +"струны к zigzags, которые несоединённые." + +#: fdmprinter.json +msgctxt "support_fill_rate label" +msgid "Fill Amount" +msgstr "Плотность заполнения" + +#: fdmprinter.json +msgctxt "support_fill_rate description" +msgid "" +"The amount of infill structure in the support, less infill gives weaker " +"support which is easier to remove." +msgstr "" +"Количество заполнения поддерживающей структуры. Меньше заполнение дает не " +"очень прочную поддерживающую поддержку, которую легко удалить." + +#: fdmprinter.json +msgctxt "support_line_distance label" +msgid "Line distance" +msgstr "Расстояние линий" + +#: fdmprinter.json +msgctxt "support_line_distance description" +msgid "Distance between the printed support lines." +msgstr "Расстояние между напечатанным линиям поддержек." + +#: fdmprinter.json +msgctxt "platform_adhesion label" +msgid "Platform Adhesion" +msgstr "Тип прилипания к столу" + +#: fdmprinter.json +msgctxt "adhesion_type label" +msgid "Type" +msgstr "Тип" + +#: fdmprinter.json +msgctxt "adhesion_type description" +msgid "" +"Different options that help in preventing corners from lifting due to " +"warping. Brim adds a single-layer-thick flat area around your object which " +"is easy to cut off afterwards, and it is the recommended option. Raft adds a " +"thick grid below the object and a thin interface between this and your " +"object. (Note that enabling the brim or raft disables the skirt.)" +msgstr "" +"Различные опции, которые предотвращают сгибания углов. Кайма - добавляет " +"однослойная плоская область вокруг вашего объекта, которую легко удалить " +"после завершения процесса печати. Эта опция рекомендуется. Подложка - строит " +"толстую сетку под объекта и тонкий интерфейс между этим и основного " +"печатающего объекта. (Замечание: если Вы включите опцию кайму или подложку, " +"тогда выключается опция контур)" + +#: fdmprinter.json +msgctxt "skirt_line_count label" +msgid "Skirt Line Count" +msgstr "Количество линий контура" + +#: fdmprinter.json +msgctxt "skirt_line_count description" +msgid "" +"The skirt is a line drawn around the first layer of the. This helps to prime " +"your extruder, and to see if the object fits on your platform. Setting this " +"to 0 will disable the skirt. Multiple skirt lines can help to prime your " +"extruder better for small objects." +msgstr "" +"Контур, это линия, которая печатается вокруг печатающего объекта на первого " +"слоя. Это позволяет предварительно расписать экструдер и показывает границы " +"основного объекта. Если установите значение 0, это выключит опцию контур. " +"Несколько контуров (линий), помогут лучше расписать экструдер, для печати " +"маленьких объектов." + +#: fdmprinter.json +msgctxt "skirt_gap label" +msgid "Skirt Distance" +msgstr "Расстояние контур" + +#: fdmprinter.json +msgctxt "skirt_gap description" +msgid "" +"The horizontal distance between the skirt and the first layer of the print.\n" +"This is the minimum distance, multiple skirt lines will extend outwards from " +"this distance." +msgstr "" +"Горизонтальное расстояние между контура и первого слоя основного печатающего " +"объекта.\n" +"Это минимальное расстояние за которое контурные линий будут печататься." + +#: fdmprinter.json +msgctxt "skirt_minimal_length label" +msgid "Skirt Minimum Length" +msgstr "Минимальная длина контура" + +#: fdmprinter.json +msgctxt "skirt_minimal_length description" +msgid "" +"The minimum length of the skirt. If this minimum length is not reached, more " +"skirt lines will be added to reach this minimum length. Note: If the line " +"count is set to 0 this is ignored." +msgstr "" +"Если минимальная длина контура не достигнутая, это добавить дополнительные " +"контурные линий для достижения минимальной длине. Замечание: если количество " +"линий имеет значение 0, тогда эта опция игнорируется." + +#: fdmprinter.json +msgctxt "brim_line_count label" +msgid "Brim Line Count" +msgstr "Количество линий каймы" + +#: fdmprinter.json +msgctxt "brim_line_count description" +msgid "" +"The amount of lines used for a brim: More lines means a larger brim which " +"sticks better, but this also makes your effective print area smaller." +msgstr "" +"Количество линий, который используются для каймы: Большое количество линий " +"означает, что большая кайма будет прилипать к платформы лучше, но, это также " +"делает Вашу эффективную область печати меньше." + +#: fdmprinter.json +msgctxt "raft_margin label" +msgid "Raft Extra Margin" +msgstr "Дополнительные поля подложки" + +#: fdmprinter.json +msgctxt "raft_margin description" +msgid "" +"If the raft is enabled, this is the extra raft area around the object which " +"is also given a raft. Increasing this margin will create a stronger raft " +"while using more material and leaving less area for your print." +msgstr "" +"Если подложка включена, это дополнительная область подложки во круг объекта, " +"котоая также имеет подложку. Увеличение этого поле, создаст более прочную " +"подложку, но, при этом используется больше материала и уменьшиться область " +"печати." + +#: fdmprinter.json +msgctxt "raft_line_spacing label" +msgid "Raft Line Spacing" +msgstr "Расстояние между линиями подложки" + +#: fdmprinter.json +msgctxt "raft_line_spacing description" +msgid "" +"The distance between the raft lines. The first 2 layers of the raft have " +"this amount of spacing between the raft lines." +msgstr "" +"Расстояние между линиями подложки. Первые 2 слоя поддержки имеют такое " +"значение для количество расстояние между линиями подложки." + +#: fdmprinter.json +msgctxt "raft_base_thickness label" +msgid "Raft Base Thickness" +msgstr "Толщина основного слоя подложки" + +#: fdmprinter.json +msgctxt "raft_base_thickness description" +msgid "" +"Layer thickness of the first raft layer. This should be a thick layer which " +"sticks firmly to the printer bed." +msgstr "" +"Толщина основного слоя подложки. Это должен быть толстый слой, который " +"плотно прилипает к печатающей платформы." + +#: fdmprinter.json +msgctxt "raft_base_linewidth label" +msgid "Raft Base Line Width" +msgstr "Ширина основной линии подложки" + +#: fdmprinter.json +msgctxt "raft_base_linewidth description" +msgid "" +"Width of the lines in the first raft layer. These should be thick lines to " +"assist in bed adhesion." +msgstr "" +"Ширина основной линий подложки. Линий должны быт толстимы, чтобы улучшит " +"прилипание к печатающей платформы." + +#: fdmprinter.json +msgctxt "raft_base_speed label" +msgid "Raft Base Print Speed" +msgstr "Основная скорость печати подложку" + +#: fdmprinter.json +msgctxt "raft_base_speed description" +msgid "" +"The speed at which the first raft layer is printed. This should be printed " +"quite slowly, as the amount of material coming out of the nozzle is quite " +"high." +msgstr "" +"Скорость при которой печатается первый слой подложки. Скорость печати должна " +"быт низкая, так как количество выдавливаемого материала достаточно высокая." + +#: fdmprinter.json +msgctxt "raft_interface_thickness label" +msgid "Raft Interface Thickness" +msgstr "Толщина переходной линий подложки" + +#: fdmprinter.json +msgctxt "raft_interface_thickness description" +msgid "Thickness of the 2nd raft layer." +msgstr "Толщина второго слоя подложки." + +#: fdmprinter.json +msgctxt "raft_interface_linewidth label" +msgid "Raft Interface Line Width" +msgstr "Ширина переходной линий подложки" + +#: fdmprinter.json +msgctxt "raft_interface_linewidth description" +msgid "" +"Width of the 2nd raft layer lines. These lines should be thinner than the " +"first layer, but strong enough to attach the object to." +msgstr "" +"Ширина второго слоя подложки. Линий второго слоя должны быт тоньше чем для " +"первого слоя, но, достаточно прочные, чтобы прилипал основной объект." + +#: fdmprinter.json +msgctxt "raft_airgap label" +msgid "Raft Air-gap" +msgstr "Воздушный зазор" + +#: fdmprinter.json +msgctxt "raft_airgap description" +msgid "" +"The gap between the final raft layer and the first layer of the object. Only " +"the first layer is raised by this amount to lower the bonding between the " +"raft layer and the object. Makes it easier to peel off the raft." +msgstr "" +"Зазор между последним слоем подложки и первого слоя основного объекта. На " +"это значение поднимается только первый слой, чтобы уменьшит соединения между " +"слоем подложки и основного объекта." + +#: fdmprinter.json +msgctxt "raft_surface_layers label" +msgid "Raft Surface Layers" +msgstr "Поверхностные слой подложки" + +#: fdmprinter.json +msgctxt "raft_surface_layers description" +msgid "" +"The number of surface layers on top of the 2nd raft layer. These are fully " +"filled layers that the object sits on. 2 layers usually works fine." +msgstr "" +"Количество поверхностных слоев, которые накладываются над вторим слоем " +"подложки. Это полностью заполнение слой, на которых печатается основной " +"объект. Обычно два слоя достаточно." + +#: fdmprinter.json +msgctxt "blackmagic label" +msgid "Fixes" +msgstr "Корректировки" + +#: fdmprinter.json +msgctxt "magic_spiralize label" +msgid "Spiralize the Outer Contour" +msgstr "Поверните внешний контур" + +#: fdmprinter.json +msgctxt "magic_spiralize description" +msgid "" +"Spiralize smooths out the Z move of the outer edge. This will create a " +"steady Z increase over the whole print. This feature turns a solid object " +"into a single walled print with a solid bottom. This feature used to be " +"called ‘Joris’ in older versions." +msgstr "" +"Спиральные сглаживания за пределами ось Z для наружной кромки. Это создаст " +"стабильное выращивание объекта по ось Z в течении всего процесса печати. Эта " +"функция превращает сплошного объекта, в печати одной стеной с сплошным дном. " +"Эта функция в предыдущих версии называлась \"Joris\"." + +#: fdmprinter.json +msgctxt "wireframe_enabled label" +msgid "Wire Printing" +msgstr "Проволочная печать" + +#: fdmprinter.json +msgctxt "wireframe_enabled description" +msgid "" +"Print only the outside surface with a sparse webbed structure, printing 'in " +"thin air'. This is realized by horizontally printing the contours of the " +"model at given Z intervals which are connected via upward and diagonally " +"downward lines." +msgstr "" +"Печатает только наружную поверхность с редкой перепончатой структуры, \"в " +"воздухе\". Это реализуется путем горизонтальной печати контуры объекта через " +"заданные интервалы времени Z, которые соединены с помощью верхних и нижних " +"по диагонали линиями." + +#: fdmprinter.json +msgctxt "wireframe_printspeed label" +msgid "Wire Printing speed" +msgstr "Скорость проволочной печати" + +#: fdmprinter.json +msgctxt "wireframe_printspeed description" +msgid "" +"Speed at which the nozzle moves when extruding material. Only applies to " +"Wire Printing." +msgstr "" +"Скорость, при которой двигается печатающая головка когда выдавливает " +"материал. Относиться только для проволочной печати." + +#: fdmprinter.json +msgctxt "wireframe_printspeed_bottom label" +msgid "Wire Bottom Printing Speed" +msgstr "Скорость проволочной печати нижнего слоя" + +#: fdmprinter.json +msgctxt "wireframe_printspeed_bottom description" +msgid "" +"Speed of printing the first layer, which is the only layer touching the " +"build platform. Only applies to Wire Printing." +msgstr "" +"Скорость, при которой печатается первый слой и является единственный слой, " +"который касается печатающей платформы. Относиться только для проволочной " +"печати." + +#: fdmprinter.json +msgctxt "wireframe_printspeed_up label" +msgid "Wire Upward Printing Speed" +msgstr "Скорость проволочной печати вверх" + +#: fdmprinter.json +msgctxt "wireframe_printspeed_up description" +msgid "" +"Speed of printing a line upward 'in thin air'. Only applies to Wire Printing." +msgstr "" +"Скорость, при которой печатается линия на верх \"в воздухе\". Относиться " +"только для проволочной печати." + +#: fdmprinter.json +msgctxt "wireframe_printspeed_down label" +msgid "Wire Downward Printing Speed" +msgstr "Скорость проволочной печати вниз" + +#: fdmprinter.json +msgctxt "wireframe_printspeed_down description" +msgid "" +"Speed of printing a line diagonally downward. Only applies to Wire Printing." +msgstr "" +"Скорость, при которой печатается линия по диагонали вниз. Относиться только " +"для проволочной печати." + +#: fdmprinter.json +msgctxt "wireframe_printspeed_flat label" +msgid "Wire Horizontal Printing Speed" +msgstr "Скорость проволочной печати по горизонтали" + +#: fdmprinter.json +msgctxt "wireframe_printspeed_flat description" +msgid "" +"Speed of printing the horizontal contours of the object. Only applies to " +"Wire Printing." +msgstr "" +"Скорость, при которой печатаются горизонтальные контуры объекта. Относиться " +"только для проволочной печати." + +#: fdmprinter.json +msgctxt "wireframe_flow label" +msgid "Wire Printing Flow" +msgstr "Выдавливание материала для проволочной печати" + +#: fdmprinter.json +msgctxt "wireframe_flow description" +msgid "" +"Flow compensation: the amount of material extruded is multiplied by this " +"value. Only applies to Wire Printing." +msgstr "" +"Компенсация выдавливания: количество выдавливаемого материала умножается на " +"это значение. Относиться только для проволочной печати." + +#: fdmprinter.json +msgctxt "wireframe_flow_connection label" +msgid "Wire Connection Flow" +msgstr "Соединительное выдавливания материала" + +#: fdmprinter.json +msgctxt "wireframe_flow_connection description" +msgid "Flow compensation when going up or down. Only applies to Wire Printing." +msgstr "" +"Компенсирует выдавливание материала когда перемещается на верх и вниз. " +"Относиться только для проволочной печати." + +#: fdmprinter.json +msgctxt "wireframe_flow_flat label" +msgid "Wire Flat Flow" +msgstr "Плоское выдавливание пластика" + +#: fdmprinter.json +msgctxt "wireframe_flow_flat description" +msgid "" +"Flow compensation when printing flat lines. Only applies to Wire Printing." +msgstr "Компенсирует выдавливание материала, когда печатаются плоские линий." + +#: fdmprinter.json +msgctxt "wireframe_top_delay label" +msgid "Wire Printing Top Delay" +msgstr "Задержка печати верхней линий" + +#: fdmprinter.json +msgctxt "wireframe_top_delay description" +msgid "" +"Delay time after an upward move, so that the upward line can harden. Only " +"applies to Wire Printing." +msgstr "" +"Время задержки после перемещения на верх, так чтобы линия вверх остыла. " +"Относиться только для проволочной печати." + +#: fdmprinter.json +msgctxt "wireframe_bottom_delay label" +msgid "Wire Printing Bottom Delay" +msgstr "Задержка проволочной печати нижней линий" + +#: fdmprinter.json +msgctxt "wireframe_bottom_delay description" +msgid "" +"Delay time after a downward move. Only applies to Wire Printing. Only " +"applies to Wire Printing." +msgstr "" +"Время задержки после перемещения вниз. Относиться только для проволочной " +"печати." + +#: fdmprinter.json +msgctxt "wireframe_flat_delay label" +msgid "Wire Printing Flat Delay" +msgstr "Задержка проволочной печати плоской линий" + +#: fdmprinter.json +msgctxt "wireframe_flat_delay description" +msgid "" +"Delay time between two horizontal segments. Introducing such a delay can " +"cause better adhesion to previous layers at the connection points, while too " +"large delay times cause sagging. Only applies to Wire Printing." +msgstr "" +"Время задержки между два горизонтальных сегменты. Такая задержка дает лучшее " +"прилипание предыдущих слоев в точках соединении, а больше задержки на " +"оборот, вызывают пригибания. Относиться только для проволочной печати." + +#: fdmprinter.json +msgctxt "wireframe_up_half_speed label" +msgid "Wire Printing Ease Upward" +msgstr "Проволочная печать слегка на верх" + +#: fdmprinter.json +msgctxt "wireframe_up_half_speed description" +msgid "" +"Distance of an upward move which is extruded with half speed.\n" +"This can cause better adhesion to previous layers, while not heating the " +"material in those layers too much. Only applies to Wire Printing." +msgstr "" +"Расстояние верхнего хода, который выдавливается с половиной скорости.\n" +"Это дает лучшее прилипание к предыдущим слоем, при этом не нагревая слишком " +"много материал в этих слоях." + +#: fdmprinter.json +msgctxt "wireframe_top_jump label" +msgid "Wire Printing Knot Size" +msgstr "Проволочная печать размер узла" + +#: fdmprinter.json +msgctxt "wireframe_top_jump description" +msgid "" +"Creates a small knot at the top of an upward line, so that the consecutive " +"horizontal layer has a better chance to connect to it. Only applies to Wire " +"Printing." +msgstr "" +"Создает небольшой узелок в верхней части верхней линий, так чтобы " +"последовательный горизонтальный слой имел больше шансов с ней соединится." + +#: fdmprinter.json +msgctxt "wireframe_fall_down label" +msgid "Wire Printing Fall Down" +msgstr "Проволочная печать идет вниз" + +#: fdmprinter.json +msgctxt "wireframe_fall_down description" +msgid "" +"Distance with which the material falls down after an upward extrusion. This " +"distance is compensated for. Only applies to Wire Printing." +msgstr "" +"Расстояние с которое материал падает вниз после выдавливания материала на " +"верх. Это расстояние компенсируется. Относиться только для проволочной " +"печати." + +#: fdmprinter.json +msgctxt "wireframe_drag_along label" +msgid "Wire Printing Drag along" +msgstr "Проволочная печать с перетаскиванием" + +#: fdmprinter.json +msgctxt "wireframe_drag_along description" +msgid "" +"Distance with which the material of an upward extrusion is dragged along " +"with the diagonally downward extrusion. This distance is compensated for. " +"Only applies to Wire Printing." +msgstr "" +"Расстояние, при которое материал выдавливаемый на верх перетаскивается " +"вместе с диагонального выдавливаемого вниз. Это расстояние компенсируется. " +"Относиться только для проволочной печати." + +#: fdmprinter.json +msgctxt "wireframe_strategy label" +msgid "Wire Printing Strategy" +msgstr "Стратегия проволочной печати" + +#: fdmprinter.json +msgctxt "wireframe_strategy description" +msgid "" +"Strategy for making sure two consecutive layers connect at each connection " +"point. Retraction lets the upward lines harden in the right position, but " +"may cause filament grinding. A knot can be made at the end of an upward line " +"to heighten the chance of connecting to it and to let the line cool; however " +"it may require slow printing speeds. Another strategy is to compensate for " +"the sagging of the top of an upward line; however, the lines won't always " +"fall down as predicted." +msgstr "" +"Стратегия, при которой подтверждаем, что 2 последующие слои соединяются в " +"каждой точки соединения. Откат материала позволяет остывание возвышающих " +"линий в правильном положении, но, может привести к перемалывание нити. Чтобы " +"повысить шансы соединении к нему, и позволит остывании линиям, можно сделать " +"узел там где заканчивается возвышающаяся линия; однако возможно потребуется " +"поставит невысокие скорости печати. Другая стратегия заключается в " +"компенсации прогиба на верхней части верхней линии; однако, линии не всегда " +"падают, как предсказано." + +#: fdmprinter.json +msgctxt "wireframe_straight_before_down label" +msgid "Wire Printing Straighten Downward Lines" +msgstr "Проволочная печать прямых линий вниз" + +#: fdmprinter.json +msgctxt "wireframe_straight_before_down description" +msgid "" +"Percentage of a diagonally downward line which is covered by a horizontal " +"line piece. This can prevent sagging of the top most point of upward lines. " +"Only applies to Wire Printing." +msgstr "" +"Процент диагональной линий вниз, который покрыт горизонтальной линии. Это " +"может предотвратить пригибание самой верхней точки верхней линий. Относиться " +"только для проволочной печати." + +#: fdmprinter.json +msgctxt "wireframe_roof_fall_down label" +msgid "Wire Printing Roof Fall Down" +msgstr "Проволочная печать покрытия падает вниз" + +#: fdmprinter.json +msgctxt "wireframe_roof_fall_down description" +msgid "" +"The distance which horizontal roof lines printed 'in thin air' fall down " +"when being printed. This distance is compensated for. Only applies to Wire " +"Printing." +msgstr "" +"Расстояние, при которое напечатанные горизонтальные линий покрытия \"в " +"воздухе\" во время печати падают вниз. Это расстояние компенсируется. " +"Относиться только для проволочной печати." + +#: fdmprinter.json +msgctxt "wireframe_roof_drag_along label" +msgid "Wire Printing Roof Drag Along" +msgstr "Проволочная печать перекрытия падает вниз" + +#: fdmprinter.json +msgctxt "wireframe_roof_drag_along description" +msgid "" +"The distance of the end piece of an inward line which gets dragged along " +"when going back to the outer outline of the roof. This distance is " +"compensated for. Only applies to Wire Printing." +msgstr "" +"Расстояние от конца внутренней линий, которая перемещается, когда " +"возвращается к внешнему контуру покрытия. Это расстояние компенсируется. " +"Относиться только для проволочной печати." + +#: fdmprinter.json +msgctxt "wireframe_roof_outer_delay label" +msgid "Wire Printing Roof Outer Delay" +msgstr "Проволочная печать внешней задержки покрытия" + +#: fdmprinter.json +msgctxt "wireframe_roof_outer_delay description" +msgid "" +"Time spent at the outer perimeters of hole which is to become a roof. Larger " +"times can ensure a better connection. Only applies to Wire Printing." +msgstr "" +"Время, которое требуется внешнего периметра отверстия, чтобы перекрылось. " +"Больше времени, обеспечивает лучшее соединение. Относиться только для " +"проволочной печати." + +#: fdmprinter.json +msgctxt "wireframe_height label" +msgid "Wire Printing Connection Height" +msgstr "Проволочная печать соединения на высоте" + +#: fdmprinter.json +msgctxt "wireframe_height description" +msgid "" +"The height of the upward and diagonally downward lines between two " +"horizontal parts. Only applies to Wire Printing." +msgstr "" +"Высота верхних и диагонально нижних линий между две горизонтальные части. " +"Относиться только для проволочной печати." + +#: fdmprinter.json +msgctxt "wireframe_roof_inset label" +msgid "Wire Printing Roof Inset Distance" +msgstr "Wire Printing Roof Inset Distance" + +#: fdmprinter.json +msgctxt "wireframe_roof_inset description" +msgid "" +"The distance covered when making a connection from a roof outline inward. " +"Only applies to Wire Printing." +msgstr "" +"Пройденное расстояние при соединении из контура крыши на внутрь. Относиться " +"только для проволочной печати." + +#: fdmprinter.json +msgctxt "wireframe_nozzle_clearance label" +msgid "Wire Printing Nozzle Clearance" +msgstr "Wire Printing Nozzle Clearance" + +#: fdmprinter.json +msgctxt "wireframe_nozzle_clearance description" +msgid "" +"Distance between the nozzle and horizontally downward lines. Larger " +"clearance results in diagonally downward lines with a less steep angle, " +"which in turn results in less upward connections with the next layer. Only " +"applies to Wire Printing." +msgstr "" +"Расстояние между соплом и горизонтальных линии вниз. Лучшие результаты " +"оформления диагональных линий вниз с менее крутым углом, что, в свою " +"очередь, приводит к уменьшению верхних соединении с следующего слоя. " +"Относиться только для проволочной печати." From e85499c9186b71af3ad5966d116564399187f09e Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Wed, 17 Jun 2015 16:02:15 +0200 Subject: [PATCH 072/117] Properly translate mode menu Contributes to #57 --- resources/qml/Sidebar.qml | 7 +++++++ resources/qml/SidebarHeader.qml | 4 ++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/resources/qml/Sidebar.qml b/resources/qml/Sidebar.qml index 3b641a4538..a40f1fdcb3 100644 --- a/resources/qml/Sidebar.qml +++ b/resources/qml/Sidebar.qml @@ -111,4 +111,11 @@ UM.AngledCornerRectangle { //: Advanced configuration mode option ListElement { text: QT_TR_NOOP("Advanced"); file: "SidebarAdvanced.qml" } } + + Component.onCompleted: { + for(var i = 0; i < modesListModel.count; ++i) + { + modesListModel.setProperty(i, "text", qsTr(modesListModel.get(i).text)); + } + } } diff --git a/resources/qml/SidebarHeader.qml b/resources/qml/SidebarHeader.qml index 6059d48155..efc6949668 100644 --- a/resources/qml/SidebarHeader.qml +++ b/resources/qml/SidebarHeader.qml @@ -33,7 +33,7 @@ Column { } ToolButton { - text: base.modesModel ? qsTr(base.modesModel.get(modeMenu.currentIndex).text) : ""; + text: base.modesModel ? base.modesModel.get(modeMenu.currentIndex).text : ""; style: UM.Theme.styles.sidebar_header_button; @@ -48,7 +48,7 @@ Column { model: base.modesModel; MenuItem { - text: qsTr(model.text); + text: model.text; checkable: true; checked: modeMenu.currentIndex == index; exclusiveGroup: modeMenuGroup; From bc98eb40bab2e84ff2e5600755469e54cc79e3c9 Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Wed, 17 Jun 2015 16:03:07 +0200 Subject: [PATCH 073/117] Change "Update" back to "Update Firmware" String change that should not have happened. Contributes to #57 --- plugins/USBPrinting/USBPrinterManager.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/plugins/USBPrinting/USBPrinterManager.py b/plugins/USBPrinting/USBPrinterManager.py index 27ff31647b..42081b184b 100644 --- a/plugins/USBPrinting/USBPrinterManager.py +++ b/plugins/USBPrinting/USBPrinterManager.py @@ -19,8 +19,7 @@ from PyQt5.QtQuick import QQuickView from PyQt5.QtCore import QUrl, QObject, pyqtSlot, pyqtProperty, pyqtSignal from UM.i18n import i18nCatalog -i18n_catalog = i18nCatalog("uranium") - +i18n_catalog = i18nCatalog("cura") class USBPrinterManager(QObject, SignalEmitter, Extension): def __init__(self, parent = None): @@ -41,7 +40,7 @@ class USBPrinterManager(QObject, SignalEmitter, Extension): ## Add menu item to top menu of the application. self.setMenuName("Firmware") - self.addMenuItem(i18n_catalog.i18n("Update"), self.updateAllFirmware) + self.addMenuItem(i18n_catalog.i18n("Update Firmware"), self.updateAllFirmware) pyqtError = pyqtSignal(str, arguments = ["amount"]) processingProgress = pyqtSignal(float, arguments = ["amount"]) From 24860305841b3519586247d5657120ac9b856d45 Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Wed, 17 Jun 2015 16:14:11 +0200 Subject: [PATCH 074/117] Format after translating, not before Contributes to #57 --- cura/CuraApplication.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 488fa3404e..0c566d2d57 100644 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -429,7 +429,7 @@ class CuraApplication(QtApplication): self.addOutputDevice(drive, { "id": drive, "function": self._writeToSD, - "description": self._i18n_catalog.i18nc("Save button tooltip. {0} is sd card name", "Save to SD Card {0}".format(drive)), + "description": self._i18n_catalog.i18nc("Save button tooltip. {0} is sd card name", "Save to SD Card {0}").format(drive), "icon": "save_sd", "priority": 1 }) @@ -482,7 +482,7 @@ class CuraApplication(QtApplication): "eject", self._i18n_catalog.i18nc("Message action", "Eject"), "eject", - self._i18n_catalog.i18nc("Message action tooltip, {0} is sdcard", "Eject SD Card {0}".format(job._sdcard)) + self._i18n_catalog.i18nc("Message action tooltip, {0} is sdcard", "Eject SD Card {0}").format(job._sdcard) ) message._sdcard = job._sdcard message.actionTriggered.connect(self._onMessageActionTriggered) From 46cd9e081bae95996d438043041eb95b84f5193b Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Wed, 17 Jun 2015 17:38:51 +0200 Subject: [PATCH 075/117] Make sure the tool panel background is at least as wide as the active item This prevents odd rendering issues when items are differently sorted. Contributes to #57 --- resources/qml/Toolbar.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/qml/Toolbar.qml b/resources/qml/Toolbar.qml index d5126db481..ec7a6bd839 100644 --- a/resources/qml/Toolbar.qml +++ b/resources/qml/Toolbar.qml @@ -18,6 +18,7 @@ Item { id: activeItemBackground; anchors.bottom: parent.bottom; + anchors.bottomMargin: UM.Theme.sizes.default_margin.height; width: UM.Theme.sizes.button.width; height: UM.Theme.sizes.button.height * 2; @@ -59,7 +60,6 @@ Item { MouseArea { anchors.fill: parent; onClicked: parent.checked ? UM.Controller.setActiveTool(null) : UM.Controller.setActiveTool(model.id); - } } } @@ -72,7 +72,7 @@ Item { anchors.bottom: buttons.top; anchors.bottomMargin: UM.Theme.sizes.default_margin.height; - width: panel.item ? panel.width + 2 * UM.Theme.sizes.default_margin.width : 0; + width: panel.item ? Math.max(panel.width + 2 * UM.Theme.sizes.default_margin.width, activeItemBackground.x + activeItemBackground.width) : 0; height: panel.item ? panel.height + 2 * UM.Theme.sizes.default_margin.height : 0; opacity: panel.item ? 1 : 0 From ddbb952d9ce55bf2f528d6c9c9f0cca5d09dafd4 Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Wed, 17 Jun 2015 17:43:03 +0200 Subject: [PATCH 076/117] Add a background to the tooltip label so long strings also have background Contributes to #57 --- resources/themes/cura/styles.qml | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/resources/themes/cura/styles.qml b/resources/themes/cura/styles.qml index 243c12aedb..d172fdb6d0 100644 --- a/resources/themes/cura/styles.qml +++ b/resources/themes/cura/styles.qml @@ -103,12 +103,17 @@ QtObject { opacity: control.hovered ? 1.0 : 0.0; Behavior on opacity { NumberAnimation { duration: 100; } } - Label { - id: label + Rectangle { anchors.horizontalCenter: parent.horizontalCenter; - text: control.text.replace("&", ""); - font: UM.Theme.fonts.button_tooltip; - color: UM.Theme.colors.button_tooltip_text; + width: childrenRect.width; + height: childrenRect.height; + + Label { + id: label + text: control.text.replace("&", ""); + font: UM.Theme.fonts.button_tooltip; + color: UM.Theme.colors.button_tooltip_text; + } } } From 414047d7688139b089188c3009b544bb7c8397b5 Mon Sep 17 00:00:00 2001 From: Tim Kuipers Date: Thu, 18 Jun 2015 12:07:40 +0200 Subject: [PATCH 077/117] bugfix: avoid overlapping walls OFF by default --- resources/settings/fdmprinter.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/settings/fdmprinter.json b/resources/settings/fdmprinter.json index 03ee5d8ee3..42bee91167 100644 --- a/resources/settings/fdmprinter.json +++ b/resources/settings/fdmprinter.json @@ -210,7 +210,7 @@ "label": "Avoid Overlapping Walls", "description": "Remove parts of a wall which share an overlap which would result in overextrusion in some places. These overlaps occur in thin pieces in a model and sharp corners.", "type": "boolean", - "default": true, + "default": false, "visible": false }, "top_bottom_pattern": { From cfa55da3dbbda5d1ab4394b12b0ba83b95698dc4 Mon Sep 17 00:00:00 2001 From: Tim Kuipers Date: Thu, 18 Jun 2015 13:25:23 +0200 Subject: [PATCH 078/117] split remove_overlapping_perimeters into outer and other --- resources/settings/fdmprinter.json | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/resources/settings/fdmprinter.json b/resources/settings/fdmprinter.json index bbceda522f..33da700c79 100644 --- a/resources/settings/fdmprinter.json +++ b/resources/settings/fdmprinter.json @@ -236,12 +236,30 @@ } } }, - "wall_overlap_avoid_enabled": { - "label": "Avoid Overlapping Walls", + "remove_overlapping_walls_enabled": { + "label": "Remove Overlapping Wall Parts", "description": "Remove parts of a wall which share an overlap which would result in overextrusion in some places. These overlaps occur in thin pieces in a model and sharp corners.", "type": "boolean", - "default": true, - "visible": false + "default": false, + "visible": false, + "children": { + "remove_overlapping_walls_0_enabled": { + "label": "Remove Overlapping Outer Wall Parts", + "description": "Remove parts of an outer wall which share an overlap which would result in overextrusion in some places. These overlaps occur in thin pieces in a model and sharp corners.", + "type": "boolean", + "default": false, + "visible": false, + "inherit": true + }, + "remove_overlapping_walls_x_enabled": { + "label": "Remove Overlapping Other Wall Parts", + "description": "Remove parts of an inner wall which share an overlap which would result in overextrusion in some places. These overlaps occur in thin pieces in a model and sharp corners.", + "type": "boolean", + "default": true, + "visible": false, + "inherit": false + } + } }, "fill_perimeter_gaps":{ "label": "Fill Gaps Between Walls", @@ -255,7 +273,7 @@ "default": "Everywhere", "visible": false, "active_if": { - "setting": "wall_overlap_avoid_enabled", + "setting": "remove_overlapping_walls_x_enabled", "value": true } }, From 221906099a3f0d48b3971552caff767874580de9 Mon Sep 17 00:00:00 2001 From: Tim Kuipers Date: Thu, 18 Jun 2015 16:36:28 +0200 Subject: [PATCH 079/117] Update README.md --- README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README.md b/README.md index acd87f1b0e..28942eeb5d 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,13 @@ Dependencies * PySerial Only required for USB printing support. +Configuring Cura +---------------- +* link your CuraEngine backend by inserting the following line in home/.config/cura/config.cfg : +[backend] +location = /[path_to_the..]/CuraEngine/build/CuraEngine + + Build scripts ------------- From dfb379dfc8c0f15f38cbe903e7a5636d6268d844 Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Fri, 19 Jun 2015 11:58:24 +0200 Subject: [PATCH 080/117] Display the rotation angle when we are performing a rotation Contributes to Asana issue 36760525983768 --- resources/qml/Cura.qml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index 5c6a5c9290..830470c9c6 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -279,6 +279,19 @@ UM.MainWindow { configureMachinesAction: actions.configureMachines; saveAction: actions.save; } + + Rectangle { + x: base.mouseX; + y: base.mouseY; + + width: childrenRect.width; + height: childrenRect.height; + Label { + text: UM.ActiveTool.properties.Rotation != undefined ? "%1°".arg(UM.ActiveTool.properties.Rotation) : ""; + } + + visible: UM.ActiveTool.valid && UM.ActiveTool.properties.Rotation != undefined; + } } } From 86dae6768f8110a189eb8373141cb70e5b52ae42 Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Fri, 19 Jun 2015 12:08:43 +0200 Subject: [PATCH 081/117] Add a precision value to the time/quality slider settings and use that to round values Fixes Asana issue 33676923773310 --- cura/PrintInformation.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cura/PrintInformation.py b/cura/PrintInformation.py index f36f2509cf..fdd835e9a0 100644 --- a/cura/PrintInformation.py +++ b/cura/PrintInformation.py @@ -51,8 +51,8 @@ class PrintInformation(QObject): self._time_quality_changed_timer.timeout.connect(self._updateTimeQualitySettings) self._interpolation_settings = { - "layer_height": { "minimum": "low", "maximum": "high", "curve": "linear" }, - "fill_sparse_density": { "minimum": "low", "maximum": "high", "curve": "linear" } + "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 @@ -196,7 +196,7 @@ class PrintInformation(QObject): else: continue - setting_value = minimum_value + (maximum_value - minimum_value) * (self._time_quality_value / 100) + 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): From 6ef82c57b6beecfafac486909f1c0b78b2ecfb0d Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Fri, 19 Jun 2015 13:19:03 +0200 Subject: [PATCH 082/117] Do not hide the entire material category for UM2 but just the unused settings Fixes #55 --- resources/settings/ultimaker2.json | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/resources/settings/ultimaker2.json b/resources/settings/ultimaker2.json index d6fc97803a..6cee1d8eb4 100644 --- a/resources/settings/ultimaker2.json +++ b/resources/settings/ultimaker2.json @@ -38,7 +38,20 @@ "categories": { "material": { - "visible": false + "settings": { + "material_print_temperature": { + "visible": false + }, + "material_bed_temperature": { + "visible": false + }, + "material_diameter": { + "visible": false + }, + "material_flow": { + "visible": false + } + } } } } From 5680c889f8aa92d25952c18d00d7532821af8d4f Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Fri, 19 Jun 2015 13:22:24 +0200 Subject: [PATCH 083/117] Hide the bed temperature for UMO The standard UMO has no heated bed, so do not show the setting for it. --- resources/settings/ultimaker_original.json | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/resources/settings/ultimaker_original.json b/resources/settings/ultimaker_original.json index 07be53e46c..ab8f0290a9 100644 --- a/resources/settings/ultimaker_original.json +++ b/resources/settings/ultimaker_original.json @@ -20,5 +20,15 @@ "machine_nozzle_offset_x_1": { "default": 18.0 }, "machine_nozzle_offset_y_1": { "default": 0.0 }, "machine_gcode_flavor": { "default": "RepRap (Marlin/Sprinter)" } + }, + + "categories": { + "material": { + "settings": { + "material_bed_temperature": { + "visible": false + } + } + } } } From f1ebb3389db352d1ce5bbc00ad0ee17384248900 Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Fri, 19 Jun 2015 13:41:49 +0200 Subject: [PATCH 084/117] Update changelog --- CHANGES | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/CHANGES b/CHANGES index f33001afd5..64858ff8ea 100644 --- a/CHANGES +++ b/CHANGES @@ -7,6 +7,22 @@ Cura 15.06 is a new release built from the ground up on a completely new framework called Uranium. This framework has been designed to make it easier to extend Cura with additional functionality as well as provide a cleaner UI. +Changes since 15.05.94 +---------------------- + +* Added Russian translations +* Fixed: Infill not displayed in layer view +* Fixed: Cannot select/scale/rotate when first activating the tool and then trying to select a model. +* Fixed: Improved font rendering on Windows +* Fixed: Help > Show Documentation crashes Cura on Windows +* Fixed: "There is no disk in the drive" repeating messages on Windows +* Fixed: Retraction settings not visible for Ultimaker2 +* Fixed: Display rotation angle when rotating an object +* Fixed: Time/Quality slider values are properly rounded +* Fixed: Improved clarity of buttons and text +* Fixed: No indication that anything is happening when loading a model +* Fixed: Eject device now works on Windows + Changes since 15.05.93 ---------------------- @@ -123,10 +139,9 @@ Features from earlier versions not (yet) in this release port certain features that we require. * X-Ray view is missing. Will be implemented as a (you might have guessed it) plugin. -* Infill display in the layer view is missing. - Like several other features, the existing implementation of this - functionality is not salvageable and will need to be reimplemented. - +* Fixes: Follow Mesh Surface + Has been removed from the engine, the same result can be + achieved using no infill or top/bottom layers. Known Issues ------------ From 2c139c4037160d2867670e9e1326110a0a549a69 Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Fri, 19 Jun 2015 06:10:55 -0700 Subject: [PATCH 085/117] Bump version to 15.05.95 --- cura/CuraApplication.py | 2 +- installer.nsi | 9 ++++++--- setup.py | 10 +++++++--- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 0c566d2d57..45d4f3a74f 100644 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -50,7 +50,7 @@ class CuraApplication(QtApplication): if not hasattr(sys, "frozen"): Resources.addResourcePath(os.path.join(os.path.abspath(os.path.dirname(__file__)), "..")) - super().__init__(name = "cura", version = "15.05.94") + super().__init__(name = "cura", version = "15.05.95") self.setRequiredPlugins([ "CuraEngineBackend", diff --git a/installer.nsi b/installer.nsi index 1259aeba7a..b5adb4b572 100644 --- a/installer.nsi +++ b/installer.nsi @@ -1,5 +1,5 @@ !ifndef VERSION - !define VERSION 'BETA' + !define VERSION '15.05.95' !endif ; The name of the installer @@ -44,6 +44,9 @@ SetCompressor /SOLID lzma !define MUI_FINISHPAGE_RUN_TEXT "Start Cura ${VERSION}" !define MUI_FINISHPAGE_RUN_FUNCTION "LaunchLink" +;Add an option to show release notes +!define MUI_FINISHPAGE_SHOWREADME "$INSTDIR\release_notes.txt" + ; Pages ;!insertmacro MUI_PAGE_WELCOME !insertmacro MUI_PAGE_DIRECTORY @@ -101,10 +104,10 @@ FunctionEnd Section "Install Visual Studio 2010 Redistributable" SetOutPath "$INSTDIR" - File "vcredist_2010_x86.exe" + File "vcredist_2010_20110908_x86.exe" IfSilent +2 - ExecWait '"$INSTDIR\vcredist_2010_x86.exe"' + ExecWait '"$INSTDIR\vcredist_2010_20110908_x86.exe" /silent /norestart' SectionEnd diff --git a/setup.py b/setup.py index cfd59c47d2..1c82f07653 100644 --- a/setup.py +++ b/setup.py @@ -22,7 +22,7 @@ def copytree(src, dst, symlinks=False, ignore=None): else: shutil.copy2(s, d) -includes = ["sip", "ctypes", "UM", "PyQt5.QtNetwork", "PyQt5._QOpenGLFunctions_2_0", "serial", "Arcus", "google", "google.protobuf", "google.protobuf.descriptor", "xml.etree", "xml.etree.ElementTree", "src"] +includes = ["sip", "ctypes", "UM", "PyQt5.QtNetwork", "PyQt5._QOpenGLFunctions_2_0", "serial", "Arcus", "google", "google.protobuf", "google.protobuf.descriptor", "xml.etree", "xml.etree.ElementTree", "cura"] # Include all the UM modules in the includes. As py2exe fails to properly find all the dependencies due to the plugin architecture. for dirpath, dirnames, filenames in os.walk(os.path.dirname(UM.__file__)): if "__" in dirpath: @@ -41,7 +41,7 @@ print("Removing previous distribution package") shutil.rmtree("dist", True) setup(name="Cura", - version="2.0", + version="15.05.95", author="Ultimaker", author_email="d.braam@ultimaker.com", url="http://software.ultimaker.com/", @@ -54,7 +54,7 @@ setup(name="Cura", print("Coping Cura plugins.") shutil.copytree(os.path.dirname(UM.__file__) + "/../plugins", "dist/plugins") for path in os.listdir("plugins"): - shutil.copytree("plugins/" + path, "dist/plugins/" + path) + shutil.copytree("plugins/" + path, "dist/plugins/" + path) print("Coping resources.") shutil.copytree(os.path.dirname(UM.__file__) + "/../resources", "dist/resources") copytree("resources", "dist/resources") @@ -70,3 +70,7 @@ for site_package in site.getsitepackages(): shutil.copytree(os.path.join(qt_origin_path, "qml/QtQuick.2"), "dist/qml/QtQuick.2") print("Coping PyQt5 svg library from: %s" % qt_origin_path) shutil.copy(os.path.join(qt_origin_path, "Qt5Svg.dll"), "dist/Qt5Svg.dll") + print("Copying Angle libraries from %s" % qt_origin_path) + shutil.copy(os.path.join(qt_origin_path, "libEGL.dll"), "dist/libEGL.dll") + shutil.copy(os.path.join(qt_origin_path, "libGLESv2.dll"), "dist/libGLESv2.dll") +os.rename("dist/cura_app.exe", "dist/Cura.exe") \ No newline at end of file From 12a32d3dc38a228217a39b6b99a7624d767a974a Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Mon, 22 Jun 2015 13:57:15 +0200 Subject: [PATCH 086/117] Add application icons for all three platforms Fixes Ultimaker/Uranium#5 --- icons/cura-128.png | Bin 0 -> 2145 bytes icons/cura-32.png | Bin 0 -> 625 bytes icons/cura-48.png | Bin 0 -> 1030 bytes icons/cura-64.png | Bin 0 -> 1133 bytes icons/cura.icns | Bin 0 -> 27026 bytes icons/cura.ico | Bin 0 -> 31206 bytes 6 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 icons/cura-128.png create mode 100644 icons/cura-32.png create mode 100644 icons/cura-48.png create mode 100644 icons/cura-64.png create mode 100644 icons/cura.icns create mode 100644 icons/cura.ico diff --git a/icons/cura-128.png b/icons/cura-128.png new file mode 100644 index 0000000000000000000000000000000000000000..ecfe0d1e4efe064ae9e5154e423d15b303fa4dd2 GIT binary patch literal 2145 zcmWkwc|6o#7ycS##u8%*$(EY2HI#m%R5Mi8m?XSYVk{F9M%fL`s4R)_mVFIlDdZJl z!cfXGS+n%ZI+Cq0k#z>|ckbtN?z!iA&NDdMeML z2f+Cw#)Szw0so8Uwoq_{Ly56q4D&O);ST`j13cp^HICZ?LHPh*dqA+u{o6or za4_nQ?_Ga47r)ym(tVGt<@1LDKxo4JvXN~__EL6)x09V>J9ch@b3WpGiD<@uQ8#0B z?4l)vm;53er6Dc*vozJ~{H#afa(0d_oKNHPZW3KIHe_b0}%Qj7hcQx(4 zmRa-|h&X-Nf*z^Y8kKisSY+zM)kCiZ{aDDvT1Y+nYpVs{t*r%@900`)_@4zJ`(OvJ zMin6(&=T-tFRsIi1R32OYS0_V-G^`Mfsi!G?Mp^Ko3V6)`lQ{`41 z(qLvF!t1yN9*JpGj`*!Rcz2#8|UfJ%Dr0Htso%c(%L_rd(WR%+ z%8XUW5SfGsyZ4@`AlCXqfY%0uqiiX`=hjbb;u|uUM?JKuf4_{F5(-zJD%am2rRzx= zhRvFiIi+KJR)71bwjyH8JAz5M+xDva3UJ62d|TCd3E7 zBE*k>gNtA>RWV&TBXqWuyA9uG_vUY{&y>zz{P~WQe^n{XOI6=e0&HLSPYU4;0>gcU z7YUs{0r}=v27=2=TLo6k$Ul3%d|Q>mGkvy(0r1{HR7>)nzvLc|g%QB^B{h(a3D zuvtPhh2`4#Y&*n%A~AElhuHoFu!mYxW+L0%83rW@)3TMv>%F67-j1VZ1WQMt%9F(A z%2oAS+lp~o^lMfpW`F5Jry-Yh&OgDN^{grO@}(_AzlR;V)?q+IHhCbgpsvt6~3ET#~FL7*HA3*YF#m(sO;%3-yoxMdM%VPH0D)=bAn453R;;g z)@!-xUGiPhPK^&YO2M3+Z{6(Pd}(U2d|8Ft zWpTO(yW|TXTZ*y@uk9#AHM)U!xsy|pBcB24r2Q3fG3))^-h?5hNHwI9!*BA&W9S_> z+Oz%o=f$6V|6Z5=3D?HERys-BH2gA{<|lU#dHb~`_H%n^CchSbn94WYKK_TfkG@%V z4KAy)HD6*)tM1PXo09m5rDyJ3+I)KJY_H@r#9AAEE39^#UMoCACd)P7k!N9*0)re7aV7KHIjN!F zah04!89yW+M&+gWss-cK0K@6K>&J4r*N>APuSg8@~nx+USHQn9`(~cWWutFtyjYh(Q_IN z9jGAY{>i}Z`6C1X_CzqeyKXndxqq(6p;McbyZwV`0swZo#&^~aSlnIqpM;$@-lNPW zKG)#;3|<%Nk4m19H!^``Vjhh0F&WjgK)0B&Nh(#84g}_qKCRM^wbj z)8=A>e2^2hIi4iK@?$xNjY)KP)x4CEd7&vTUwl~&Gl<=BNn)v%CKxs$#t*mBn18-w ziRBXusk?PtA18qh5jW;ukwSB4$1o=AQc`l0~ZC?)leROR-1>P!b^hqKzjjL zkc3K&0yp@#N;ljOQuK(%lMK~92xfJ#E`aeg^<^ MW8CG+i_Q=K4@tB4iU0rr literal 0 HcmV?d00001 diff --git a/icons/cura-32.png b/icons/cura-32.png new file mode 100644 index 0000000000000000000000000000000000000000..2ad22313e9833424a16d12688dc8ba618a4f8884 GIT binary patch literal 625 zcmV-%0*?KOP)i(@G;~XGkRo5j0+7 zp&3Fkfwje@OuMLkoN9p1)8ip`DyTm=&UaD+<@{qMyDHT93R30F2oM+_e>C zuRB_BEacZuanq5r3q;+O(Qg4DG@a7@WyG=RSlB_M;BBk`fL5(>_|YoHrR%A}2a{GK z_6YVP%nGIEGrUHV;m1{EuZ!H>2G@z6S1bp^e_9Q0*6r9POgl=^8BAF>n6KNApa-&q z=PD2chXk)-DsVw(FzJUw4m+R(oq-t)&s-o5o~b|*cxD1g;Q+q@yQ|R)l&`4Q00000 LNkvXXu0mjfB;gk^ literal 0 HcmV?d00001 diff --git a/icons/cura-48.png b/icons/cura-48.png new file mode 100644 index 0000000000000000000000000000000000000000..5fadb70f0fad9253ccdadd00406856234e8fdec5 GIT binary patch literal 1030 zcmV+h1o``kP)&R41+ms zFdY*Q;KpfG7w=4V^2tBK(Kg8cF!y2o$&+h`AT9P0N>OaWSVt&Lu?b@pp|r&&O!Ek( zG&W(HMkuYZ2@`1`KRq`BqMoFrS|OPK3t5~;?k_>L3?kO;oo&0wYy+WR8D>bUb*ceo8f{!HnLE}n>^NW z@c>SKx;O%k^&};MkN52t$nw2*YqP>N=+^VW>mOU;Y&8^~;b31@I8Cf~0Tl-apQ zRqtxY`PE;~L#-MuVD}qze{ce`c@SgbZ8tQi_5thpUjSzBhCum+0Ut6`595{z>=c-GG`3#zC-wbTyA{6yh7#V z81CM;aQD59vt>IdCpOcQ$Mp2t*Y{1&sUDZCEK~YyKeA9($>zP2Wbcg^%$j#@F9M)>i9>0;zhcdiA$a0C&%uxUat-r)f(O@M0M-ZWrCdC&=vG z-)c?P2zVmFcq{_|Cfh^LkyB&~pFpm!mD<)tpo}<&OGAUFV;g{aPjnp`CHLizxUU|B z{!OjY7>R&~$moN@vu7dZ9~@zfamWljPv*!ef~5ugTfYF%u&&QsBg z!bhA2OC$dJ%a)GiSW2M5tRQUHq5o#tN$wlkUU%(^r0)slSYeCK&4vvc-j6^2t7ua@kLo&X*&J$D>YCMQc8JQx&^M0hYH zAgSU@E{P726%`lAT96^K|q?|VUq&V1`nGMkVbe2DIl%z08UUr;rPXF z+tS}+5?=fG^K(#NM6c9A`;n(ocWuSmc7OEoclu@FRJCNgx(w`!+yT(d6})q0+{rTj zg`e@SOha=;GUZ0>eM1yp`5^N6+x`U5gA@<8ee}*mezke9-m($|KwIa6&5W_Fsr2hJI z_?2@;7T1bc>o3IcZj~)1N?}anM_X78s%g};~j)MJn!;vc#~&B*Q>4v$vplncHe%i z?LA@uX#%&xL2)NO4_9beWQUKC85}`bwzRFW$OLGO`{OsEN^>J`lj%PwY8OYW;F8co#2T4^YnOr>?A(pr-79GmYeWL{jcZ^v5{(x>aByD1iNIUK_-nJOes#`_QteEhlD0tI^9fmPQ}h z)VaUf-m`ihuwxgh&;eHT>!9u_;XL$cM zp`)OTegQSER6J!57!?4Jed2lYM?Qg$PSaDHOvO|7K>gs3%fyc(vo;`vs8eYk7(?>}svfd4a!X%G9BE5DxDZh^MR9pEAD z0s`P6%>qKeLs|udgohLg(CC@4@Q^YA8a?AH=RjC^NRfaj@Q@M#QQ;v40wnN|Z~;m; literal 0 HcmV?d00001 diff --git a/icons/cura.icns b/icons/cura.icns new file mode 100644 index 0000000000000000000000000000000000000000..eac1092e0e1c2e8cdebe3d510ad59004501d4feb GIT binary patch literal 27026 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 MHhgCA(}$=31qu%R?f?J) literal 0 HcmV?d00001 diff --git a/icons/cura.ico b/icons/cura.ico new file mode 100644 index 0000000000000000000000000000000000000000..c6b554301c76a062174db69999f308d51b596708 GIT binary patch literal 31206 zcmeHQcW@oW8UJ1{Crh}xnbjq!899FOfi_I2cZX3O%2_2 zl8|90fee8tlL91!^g zmO_$Njz7g>5&rzV0het{!IJ55c>cO9Y->-#dna=+Ze$GZIFx}mZ_UBMbt#z97>AD^ z&%>9mRl(Xt33%X=4BWIo4cRonq_Hu0;>s+Htd7C=?##iSt|ZK#9EaaMZNSSnW?}Qv zB>dxL115})!PD1d;mWg8&^9vxi8#PtPaCjsY8<{<$iblvDd=66gwJ0wplM7Dp1n2; zj~~lGCI#@tt5wig7lZG7EeGGeC5Pt*Fo1TyL{8sD%yr1=N#w8>vF}H2LuF_LhDJa? z0@bP_n5)#)_#VWIT5lO>^rnGM&keMCW}w%X3|#coK-6avP1CZhq=*7f($bGTlb|&C;D-6uO%-a`bJBZTa#v@zP3f~w&WTTVD_T8)l+Km0n8IUmWiy4 zVut1wFiLvbM+Y8_c#z9NK136EkdInlrGQrbcnsk2FFgK@ z$A9qnFCGM-SiqwnkAc!Wg}inT;$hBH_{GLPv5!=sAIZZ*M>BAIR~q)ONy6#{379q^4tHOeq4Sm6Gh(o- zGYOx)WWXm+ybnbKErWD+9eg^u^NyDkjGmygh7n$df`3sp7{p{g9 zomXvYO+Zgu67Iby1J~|O;~d9;9FBT5j>8#MK<7+T$H(C(59R6nXwAX|Y+as&yDrGU z^?TFw!o-nB8smTYngL&s1LsMfW1Qj0>F;=cop>&fAcs?z;dq>F|6dTtc&I%z0$;@u zFx<-qnX|jOVaN_%_OgSgz2x9+Z+GxvZ*%ZtFFN?L7aaWATMj<$O$WdB+(Bu-Oe)l< zbfum4n`X|NJif70D050i)Iql2oXPdbudyl2h&V|0y){#1YXELf&cSZqvczdE*rS8Z zzPB`6lzq(j`0}>dZ?jEZO*g^bX1?WQ|6~loTyp^?FL;3v|_b=Fq++v0W%t zLVOS$rVVHIQYF5gL3F6El`Ko5-5xfYaLbyOs~jYI!NEw<1(M_>+l!P|+Lx2m(_t?< zbS$2g9%Tn`+KZG7qb1W+J#vjz8q5`Seaa3V85!DvUruQOG*=kIxx!q&G!#+GOo` zC$0m@8t=V(b5K7j2K#V5dL6Fy$Qtj<*Q@9{@UdfAAZyO)EXPiA>p#UcAIY;9a1BZF z-y}DFY)1+{d(nXAaWR_n-id42wwZDG%~N@J>gp`aYmNh1i;{IF$>aCmpQkzU)h!7) zd2t5UkXgEJ{qzL`-nlymyH_P?ZvP%!7n6GcB%lBLXAEecouKQ>Po6N~_Csm97A5P- z@tD7#h`IhL^`*7%M(g4qm)I3)@#D z>H3(g<8L{b2C{Y~Yxd7E_TTWlKE-lL>2NbU?EXC} zf3_YrU2F}>UqI!}>XkMJN$~Wa$Uv; zn9fkYu2o~?(ru-J&f4Z;rc-S-2S3-MvvKR+Wzt{aW~S9G=Kf2@X>HsTIFl`vS~n}b zj-&=@nWwjL(@f_6^WCiUI`fuhhu+3bGl%21yIJXV<~n`5-o{NsZwpfnJKW5)x^B)N zGFlrqMe7_~5fKwGcVd~8P=_Z3yqd@b2~{nqU{c=?7L-HV;ZoN;4a9Ohz9_*YLF@S_Lvuzqn8 zZa$Eqx#XRlDcoOF1v8uCuyh95Bb254ir%}I?19R|rQ6c*^G6MM;Y1ED+FaW2RAaSU_pMIBxwwa%>{%kY zXR>E}_^=qw`+oLf72S8WY*qr^zB^C%cE55{4lcmE#UwBO@TFP0m#T112Hr((A3kEh zs`&{xjrnArb0+(%jBE@>)xeP7S&+oM zdJ^8~%h5euhkMevmpu!YY)Qk3J!#B=XCRK(kmTH}@q9_%djxW=!}D&ajnn-2M>sAY zoHF2jJjX>Cw-v|l9pv6VCjoEWhWpcT&-@#>SL}7fcnPn^X&l4na4-8aI2QZy+C7Bl zbOm0!q4NJX0@kjIGPVK?V;NC}Q2q%BPUUR+CQwdLF_vEfqEA`dJ_(c+(-zDh0h>#C z1z!Zp3u%w$hk)Iq%#sfRWk!_3c@vNv$}M^lC^w)S&x?R;Uv}ApK-uve0o@5W%*)^5 zN}&93&WLUVoYoQaxDbc{O%YO0Kw%t3mzF>jV5*op0xH```ZNS0!BPfQ6HuB))2Sp7 z4U{^nih$ZOqFx1oh>$d4odh(7QFS{AL)EACiM?UIXkb60=08s8+~L~!fbGx8FDsFsxmpA&FHbLf(nTXFriboh*b14G{t z=iAf$LQ^{}eu;n{PS&ig4}{B&_4t&4jG~qMj_`=VY8QM$K#M0S&fF0U*0grRM*dKCil6t1zm9#^DGMT4;(GH zLqQ1+4L2VR9xH^$DCMrJm6@&(vBJ3YsZM|1^e~}9`KWe0k8TSUDvVPX{|>D%3_ETJ z6Dgbz`_8g}4sd?BP@%jQ*^sHQsxwria88PE7|#e7DV*2Uj`KgOHC&`{URekJwc!GV z^J;cZ`xb|b6wXVr+48(_k-~ZPI))#v2p1`w*L4p39hLdmVRV-{@JEL`k&s^79QgGi zBZYIj-hp4)^LK6(o>Qq0emY=^*Z=%*BN@(XzhnGieYi;ByqeALU6@2hhKm%^tHs3S zjVU1`g>$=x!HnCRHp5H3pQ?r3Vf(Z`t~2J;p(|60*5zLY!bdfg%5C9Q-%Hw);Oo2 zB}U^G_AJ;p*x~izZ+>-7IS_Nb$}D#X?*wRCHvmdLj!uvKkBTv>#`h%P{>?iNB0liF2prw&f#Ih1gYQLPe2Du_oRcqnF9KHuzU{{0z4E`@&i%hM zzkVe*3|jj;f6srz>YAXy%-%1)*CnV^KIX)mly7`50yFU&AR&r>d@lmcvz@B}Z$bg^ zy$IxwhZuq&_?`rg4wJsdT;B8d{4dDok0z%CyB`wh{CyC}=Z{_75P(8Je9ucj->CK` zFYIpd_i7A^?~edsarT7jF&i(K>QSzAe`i&^fZQF*tX1IqAAvG(QQ=pTfD*rg1l0JI zBcQ>r7y%uAr3h&8D?~t#Ul{@}_!S}GhF=K+uJ{!o;Eo?X0T2Ah33%a0O~4aBVglay z(Gu{1A1MJp_)!w@g&!dSfB4Z6@QEK80l)ZB5%7&45dr`B(Galk@!>G!K_F(qhs4-q zd{~T4#)rn(5#;4MVct8o5@Z_=Kc0DB^=qUkF?z7uNha@*jG9e)P69S@4 z2#7KvAj*V*P41KA4oPm5Wd2+p9)v%ihs*pqJzO@Q*TXxcc+h9(gCoE#OLe0p^Cz`) zEB?fGF7qe1bJ=`?JMWOv<;txGE{~};Hx_V1&La} zBo9b3e{K}-W{>~pqWL=lyo3E502j^Q72qBG_XSM3pSMaidt8*+$$xi%cktgIFlGKj z2CRes9)YQs-U0aYsVsdzZ_=7SuZnl_=T=#=eFTd9hY*-SQ6eC}1*2Txu}F<_za$Sx zvh5O}y^R3Mwh=(tb_r0njR27g0sxWwC3!%SMFPZLBtT@50FgxkL>389xnKcMxnGh8 GEct)Bztd;{ literal 0 HcmV?d00001 From 049f3c25ffdbb5d5fefd9ae0ea63541ed64a6e70 Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Mon, 22 Jun 2015 17:04:30 +0200 Subject: [PATCH 087/117] Performance: Only calculate the platform center once, not for every poly Contributes to #52 --- .../CuraEngineBackend/ProcessSlicedObjectListJob.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/plugins/CuraEngineBackend/ProcessSlicedObjectListJob.py b/plugins/CuraEngineBackend/ProcessSlicedObjectListJob.py index 6113da78a0..27ba592c78 100644 --- a/plugins/CuraEngineBackend/ProcessSlicedObjectListJob.py +++ b/plugins/CuraEngineBackend/ProcessSlicedObjectListJob.py @@ -32,6 +32,12 @@ class ProcessSlicedObjectListJob(Job): settings = Application.getInstance().getActiveMachine() layerHeight = settings.getSettingValueByKey("layer_height") + center = None + if not settings.getSettingValueByKey("machine_center_is_zero"): + center = numpy.array([settings.getSettingValueByKey("machine_width") / 2, 0.0, -settings.getSettingValueByKey("machine_depth") / 2]) + else: + center = numpy.array([0.0, 0.0, 0.0]) + mesh = MeshData() for object in self._message.objects: try: @@ -53,9 +59,7 @@ class ProcessSlicedObjectListJob(Job): points[:,2] *= -1 - if not settings.getSettingValueByKey("machine_center_is_zero"): - center = [settings.getSettingValueByKey("machine_width") / 2, 0.0, -settings.getSettingValueByKey("machine_depth") / 2] - points -= numpy.array(center) + points -= numpy.array(center) layerData.addPolygon(layer.id, polygon.type, points, polygon.line_width) From bc055a8031cb8a9811bc5ab961dfcc357d9e81ec Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Mon, 22 Jun 2015 17:06:46 +0200 Subject: [PATCH 088/117] Rework LayerData mesh generation for improved performance Rather than using a lot of calls to MeshData::addVertices which is very slow, pre-allocate numpy arrays, fill them with data from the polygons and then assign them to the mesh data's arrays. This greatly increases performance of the layer data calculation. Contributes to #52 --- plugins/CuraEngineBackend/LayerData.py | 78 +++++++++++++++++--------- 1 file changed, 53 insertions(+), 25 deletions(-) diff --git a/plugins/CuraEngineBackend/LayerData.py b/plugins/CuraEngineBackend/LayerData.py index b129942c36..c793c17504 100644 --- a/plugins/CuraEngineBackend/LayerData.py +++ b/plugins/CuraEngineBackend/LayerData.py @@ -8,6 +8,7 @@ from UM.Math.Vector import Vector import numpy import math +import copy class LayerData(MeshData): def __init__(self): @@ -48,11 +49,23 @@ class LayerData(MeshData): self._layers[layer].setThickness(thickness) def build(self): + vertex_count = 0 for layer, data in self._layers.items(): - data.build() + vertex_count += data.vertexCount() + vertices = numpy.empty((vertex_count, 3), numpy.float32) + colors = numpy.empty((vertex_count, 4), numpy.float32) + indices = numpy.empty((vertex_count, 2), numpy.int32) + + offset = 0 + for layer, data in self._layers.items(): + offset = data.build(offset, vertices, colors, indices) self._element_counts[layer] = data.elementCount + self.addVertices(vertices) + self.addColors(colors) + self.addIndices(indices.flatten()) + class Layer(): def __init__(self, id): self._id = id @@ -83,20 +96,30 @@ class Layer(): def setThickness(self, thickness): self._thickness = thickness - def build(self): + def vertexCount(self): + result = 0 + for polygon in self._polygons: + result += polygon.vertexCount() + + return result + + def build(self, offset, vertices, colors, indices): + result = offset for polygon in self._polygons: if polygon._type == Polygon.InfillType or polygon._type == Polygon.SupportInfillType: continue - polygon.build() + polygon.build(result, vertices, colors, indices) + result += polygon.vertexCount() self._element_count += polygon.elementCount + return result + def createMesh(self): builder = MeshBuilder() for polygon in self._polygons: poly_color = polygon.getColor() - poly_color = Color(poly_color[0], poly_color[1], poly_color[2], poly_color[3]) points = numpy.copy(polygon.data) if polygon.type == Polygon.InfillType or polygon.type == Polygon.SkinType or polygon.type == Polygon.SupportInfillType: @@ -159,43 +182,48 @@ class Polygon(): self._data = data self._line_width = line_width / 1000 - def build(self): - self._begin = self._mesh._vertex_count - self._mesh.addVertices(self._data) - self._end = self._begin + len(self._data) - 1 + def build(self, offset, vertices, colors, indices): + self._begin = offset color = self.getColor() - color[3] = 2.0 + color.setValues(color.r * 0.5, color.g * 0.5, color.b * 0.5, color.a) - colors = [color for i in range(len(self._data))] - self._mesh.addColors(numpy.array(colors, dtype=numpy.float32) * 0.5) + for i in range(len(self._data)): + vertices[offset + i, :] = self._data[i, :] + colors[offset + i, 0] = color.r + colors[offset + i, 1] = color.g + colors[offset + i, 2] = color.b + colors[offset + i, 3] = color.a + + self._end = self._begin + len(self._data) - 1 - indices = [] for i in range(self._begin, self._end): - indices.append(i) - indices.append(i + 1) + indices[i, 0] = i + indices[i, 1] = i + 1 - indices.append(self._end) - indices.append(self._begin) - self._mesh.addIndices(numpy.array(indices, dtype=numpy.int32)) + indices[self._end, 0] = self._end + indices[self._end, 1] = self._begin def getColor(self): if self._type == self.Inset0Type: - return [1.0, 0.0, 0.0, 1.0] + return Color(1.0, 0.0, 0.0, 1.0) elif self._type == self.InsetXType: - return [0.0, 1.0, 0.0, 1.0] + return Color(0.0, 1.0, 0.0, 1.0) elif self._type == self.SkinType: - return [1.0, 1.0, 0.0, 1.0] + return Color(1.0, 1.0, 0.0, 1.0) elif self._type == self.SupportType: - return [0.0, 1.0, 1.0, 1.0] + return Color(0.0, 1.0, 1.0, 1.0) elif self._type == self.SkirtType: - return [0.0, 1.0, 1.0, 1.0] + return Color(0.0, 1.0, 1.0, 1.0) elif self._type == self.InfillType: - return [1.0, 1.0, 0.0, 1.0] + return Color(1.0, 1.0, 0.0, 1.0) elif self._type == self.SupportInfillType: - return [0.0, 1.0, 1.0, 1.0] + return Color(0.0, 1.0, 1.0, 1.0) else: - return [1.0, 1.0, 1.0, 1.0] + return Color(1.0, 1.0, 1.0, 1.0) + + def vertexCount(self): + return len(self._data) @property def type(self): From 72b1302f9e0a8e40e732808ca494ff04cdcaa65f Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Mon, 22 Jun 2015 17:23:32 +0200 Subject: [PATCH 089/117] Disable slicing and platform physics when an operation is being performed This prevents the model from jumping around during rotation/scale Fixes #56 --- cura/PlatformPhysics.py | 14 ++++++++++++++ plugins/CuraEngineBackend/CuraEngineBackend.py | 12 ++++++++++++ 2 files changed, 26 insertions(+) diff --git a/cura/PlatformPhysics.py b/cura/PlatformPhysics.py index 5e4bd5a415..d7276c9773 100644 --- a/cura/PlatformPhysics.py +++ b/cura/PlatformPhysics.py @@ -24,8 +24,12 @@ class PlatformPhysics: super().__init__() self._controller = controller self._controller.getScene().sceneChanged.connect(self._onSceneChanged) + self._controller.toolOperationStarted.connect(self._onToolOperationStarted) + self._controller.toolOperationStopped.connect(self._onToolOperationStopped) self._build_volume = volume + self._enabled = True + self._change_timer = QTimer() self._change_timer.setInterval(100) self._change_timer.setSingleShot(True) @@ -35,6 +39,9 @@ class PlatformPhysics: self._change_timer.start() def _onChangeTimerFinished(self): + if not self._enabled: + return + root = self._controller.getScene().getRoot() for node in BreadthFirstIterator(root): if node is root or type(node) is not SceneNode: @@ -93,3 +100,10 @@ class PlatformPhysics: if node.getBoundingBox().intersectsBox(self._build_volume.getBoundingBox()) == AxisAlignedBox.IntersectionResult.FullIntersection: op = ScaleToBoundsOperation(node, self._build_volume.getBoundingBox()) op.push() + + def _onToolOperationStarted(self, tool): + self._enabled = False + + def _onToolOperationStopped(self, tool): + self._enabled = True + self._onChangeTimerFinished() diff --git a/plugins/CuraEngineBackend/CuraEngineBackend.py b/plugins/CuraEngineBackend/CuraEngineBackend.py index 45a2148892..9fbd7a47a1 100644 --- a/plugins/CuraEngineBackend/CuraEngineBackend.py +++ b/plugins/CuraEngineBackend/CuraEngineBackend.py @@ -59,6 +59,8 @@ class CuraEngineBackend(Backend): self._save_polygons = True self._report_progress = True + self._enabled = True + self.backendConnected.connect(self._onBackendConnected) def getEngineCommand(self): @@ -86,6 +88,9 @@ class CuraEngineBackend(Backend): # If False, this method will do nothing when already slicing. True by default. # - report_progress: True if the slicing progress should be reported, False if not. Default is True. def slice(self, **kwargs): + if not self._enabled: + return + if self._slicing: if not kwargs.get("force_restart", True): return @@ -235,3 +240,10 @@ class CuraEngineBackend(Backend): if self._restart: self._onChanged() self._restart = False + + def _onToolOperationStarted(self, tool): + self._enabled = False + + def _onToolOperationStopped(self, tool): + self._enabled = True + self._onChanged() From d937eb7a177b7afa085e45c2437323edb0a27c81 Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Tue, 23 Jun 2015 11:54:22 +0200 Subject: [PATCH 090/117] Defer opening the webbrowser until the next run of the event loop Opening a web browser from a signal handler connected to a menu crashes Cura, so instead defer the call. Fixes #63 --- cura/CuraActions.py | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/cura/CuraActions.py b/cura/CuraActions.py index ee75665e0b..e585b261d0 100644 --- a/cura/CuraActions.py +++ b/cura/CuraActions.py @@ -1,4 +1,8 @@ -from PyQt5.QtCore import QObject, pyqtSignal, pyqtSlot, pyqtProperty +from PyQt5.QtCore import QObject, pyqtSignal, pyqtSlot, pyqtProperty, QUrl +from PyQt5.QtGui import QDesktopServices + +from UM.Event import CallFunctionEvent +from UM.Application import Application import webbrowser @@ -8,8 +12,16 @@ class CuraActions(QObject): @pyqtSlot() def openDocumentation(self): - webbrowser.open("http://ultimaker.com/en/support/software") + # Starting a web browser from a signal handler connected to a menu will crash on windows. + # So instead, defer the call to the next run of the event loop, since that does work. + # Note that weirdly enough, only signal handlers that open a web browser fail like that. + event = CallFunctionEvent(self._openUrl, [QUrl("http://ultimaker.com/en/support/software")], {}) + Application.getInstance().functionEvent(event) @pyqtSlot() def openBugReportPage(self): - webbrowser.open("http://github.com/Ultimaker/Cura/issues") + event = CallFunctionEvent(self._openUrl, [QUrl("http://github.com/Ultimaker/Cura/issues")], {}) + Application.getInstance().functionEvent(event) + + def _openUrl(self, url): + QDesktopServices.openUrl(url) \ No newline at end of file From 8a63b8d110d5925a127cd1d2b3659428dc126af9 Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Tue, 23 Jun 2015 11:56:15 +0200 Subject: [PATCH 091/117] Fix recent files on Windows Contributes to Asana issue 33694049548880 --- cura/CuraApplication.py | 12 ++++++++---- resources/qml/Cura.qml | 8 +++++--- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 45d4f3a74f..be663545e6 100644 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -85,7 +85,7 @@ class CuraApplication(QtApplication): if not os.path.isfile(f): continue - self._recent_files.append(f) + self._recent_files.append(QUrl.fromLocalFile(f)) ## Handle loading of all plugin types (and the backend explicitly) # \sa PluginRegistery @@ -330,7 +330,7 @@ class CuraApplication(QtApplication): return log recentFilesChanged = pyqtSignal() - @pyqtProperty("QStringList", notify = recentFilesChanged) + @pyqtProperty("QVariantList", notify = recentFilesChanged) def recentFiles(self): return self._recent_files @@ -508,7 +508,7 @@ class CuraApplication(QtApplication): if type(job) is not ReadMeshJob: return - f = job.getFileName() + f = QUrl.fromLocalFile(job.getFileName()) if f in self._recent_files: self._recent_files.remove(f) @@ -516,5 +516,9 @@ class CuraApplication(QtApplication): if len(self._recent_files) > 10: del self._recent_files[10] - Preferences.getInstance().setValue("cura/recent_files", ";".join(self._recent_files)) + pref = "" + for path in self._recent_files: + pref += path.toLocalFile() + ";" + + Preferences.getInstance().setValue("cura/recent_files", pref) self.recentFilesChanged.emit() diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index 830470c9c6..464424130a 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -37,9 +37,11 @@ UM.MainWindow { Instantiator { model: Printer.recentFiles MenuItem { - property url filePath: modelData; - text: (index + 1) + ". " + modelData.slice(modelData.lastIndexOf("/") + 1); - onTriggered: UM.MeshFileHandler.readLocalFile(filePath); + text: { + var path = modelData.toString() + return (index + 1) + ". " + path.slice(path.lastIndexOf("/") + 1); + } + onTriggered: UM.MeshFileHandler.readLocalFile(modelData); } onObjectAdded: fileMenu.insertItem(index, object) onObjectRemoved: fileMenu.removeItem(object) From d28f84d7327298cbcb5974e3d6741870394675a6 Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Tue, 23 Jun 2015 12:29:05 +0200 Subject: [PATCH 092/117] Abort attempts to connect if an error is thrown when connecting to the serial port This fixes an issue where thread.join() would be called from the current thread which fails. --- plugins/USBPrinting/PrinterConnection.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/plugins/USBPrinting/PrinterConnection.py b/plugins/USBPrinting/PrinterConnection.py index 19c5b5c07a..1d651516c5 100644 --- a/plugins/USBPrinting/PrinterConnection.py +++ b/plugins/USBPrinting/PrinterConnection.py @@ -173,6 +173,10 @@ class PrinterConnection(SignalEmitter): Logger.log("i", "Could not establish connection on %s: %s. Device is not arduino based." %(self._serial_port,str(e))) except Exception as e: Logger.log("i", "Could not establish connection on %s, unknown reasons. Device is not arduino based." % self._serial_port) + + if not self._serial or not programmer.serial: + self._is_connecting = False + return # If the programmer connected, we know its an atmega based version. Not all that usefull, but it does give some debugging information. for baud_rate in self._getBaudrateList(): # Cycle all baud rates (auto detect) From ab9f30a852bcf2b574962032b7b9b54cda74666d Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Tue, 23 Jun 2015 14:31:50 +0200 Subject: [PATCH 093/117] Offset the displayed rotation angle so it does not overlap the mouse cursor Contributes to Asana issue 33752130551782 --- resources/qml/Cura.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index 464424130a..9756b3550f 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -283,8 +283,8 @@ UM.MainWindow { } Rectangle { - x: base.mouseX; - y: base.mouseY; + x: base.mouseX + UM.Theme.sizes.default_margin.width; + y: base.mouseY + UM.Theme.sizes.default_margin.height; width: childrenRect.width; height: childrenRect.height; From 3faccd5b3e99c0eafb94de4e377bb020dda4e8ec Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Tue, 23 Jun 2015 14:56:51 +0200 Subject: [PATCH 094/117] If findObject returns none but object_id != 0 use the selected object This works around an issue where the tool handles overlap the selected object and thus context menu entries would not work. Fixes #64 --- cura/CuraApplication.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index be663545e6..1e7936f0c9 100644 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -215,6 +215,9 @@ class CuraApplication(QtApplication): def deleteObject(self, object_id): object = self.getController().getScene().findObject(object_id) + if not object and object_id != 0: #Workaround for tool handles overlapping the selected object + object = Selection.getSelectedObject(0) + if object: op = RemoveSceneNodeOperation(object) op.push() @@ -224,6 +227,9 @@ class CuraApplication(QtApplication): def multiplyObject(self, object_id, count): node = self.getController().getScene().findObject(object_id) + if not node and object_id != 0: #Workaround for tool handles overlapping the selected object + node = Selection.getSelectedObject(0) + if node: op = GroupedOperation() for i in range(count): @@ -240,6 +246,9 @@ class CuraApplication(QtApplication): def centerObject(self, object_id): node = self.getController().getScene().findObject(object_id) + if not node and object_id != 0: #Workaround for tool handles overlapping the selected object + node = Selection.getSelectedObject(0) + if node: op = SetTransformOperation(node, Vector()) op.push() From 6b5c3d5878d27e16c74effd940a8b9fb5497e418 Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Tue, 23 Jun 2015 17:09:42 +0200 Subject: [PATCH 095/117] Display progress information during processing of layer data Contributes to Asana issue 38547857084327 --- .../ProcessSlicedObjectListJob.py | 39 +++++++++++++++++++ plugins/LayerView/LayerView.py | 3 ++ 2 files changed, 42 insertions(+) diff --git a/plugins/CuraEngineBackend/ProcessSlicedObjectListJob.py b/plugins/CuraEngineBackend/ProcessSlicedObjectListJob.py index 27ba592c78..4c6e0fd2ea 100644 --- a/plugins/CuraEngineBackend/ProcessSlicedObjectListJob.py +++ b/plugins/CuraEngineBackend/ProcessSlicedObjectListJob.py @@ -7,18 +7,30 @@ from UM.Scene.SceneNode import SceneNode from UM.Application import Application from UM.Mesh.MeshData import MeshData +from UM.Message import Message +from UM.i18n import i18nCatalog + from . import LayerData import numpy import struct +catalog = i18nCatalog("cura") + class ProcessSlicedObjectListJob(Job): def __init__(self, message): super().__init__() self._message = message self._scene = Application.getInstance().getController().getScene() + self._progress = None + Application.getInstance().getController().activeViewChanged.connect(self._onActiveViewChanged) + def run(self): + if Application.getInstance().getController().getActiveView().getPluginId() == "LayerView": + self._progress = Message(catalog.i18nc("Layers View mode", "Layers"), 0, False, 0) + self._progress.show() + objectIdMap = {} new_node = SceneNode() ## Put all nodes in a dict identified by ID @@ -38,6 +50,9 @@ class ProcessSlicedObjectListJob(Job): else: center = numpy.array([0.0, 0.0, 0.0]) + if self._progress: + self._progress.setProgress(2) + mesh = MeshData() for object in self._message.objects: try: @@ -63,9 +78,33 @@ class ProcessSlicedObjectListJob(Job): layerData.addPolygon(layer.id, polygon.type, points, polygon.line_width) + if self._progress: + self._progress.setProgress(50) + # We are done processing all the layers we got from the engine, now create a mesh out of the data layerData.build() mesh.layerData = layerData + if self._progress: + self._progress.setProgress(100) + new_node.setMeshData(mesh) new_node.setParent(self._scene.getRoot()) + + view = Application.getInstance().getController().getActiveView() + if view.getPluginId() == "LayerView": + view.resetLayerData() + + if self._progress: + self._progress.hide() + + def _onActiveViewChanged(self): + if self.isRunning(): + if Application.getInstance().getController().getActiveView().getPluginId() == "LayerView": + if not self._progress: + self._progress = Message(catalog.i18nc("Layers View mode", "Layers"), 0, False, 0) + self._progress.show() + else: + if self._progress: + self._progress.hide() + diff --git a/plugins/LayerView/LayerView.py b/plugins/LayerView/LayerView.py index 17cea9988c..617dda411a 100644 --- a/plugins/LayerView/LayerView.py +++ b/plugins/LayerView/LayerView.py @@ -39,6 +39,9 @@ class LayerView(View): def getMaxLayers(self): return self._max_layers + def resetLayerData(self): + self._current_layer_mesh = None + def beginRendering(self): scene = self.getController().getScene() renderer = self.getRenderer() From cbfdd08800a2eaf82943db3ec20c9f725bf7fd51 Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Tue, 23 Jun 2015 17:58:47 +0200 Subject: [PATCH 096/117] Correct the bottom offset we add when setting the volume for scale to max Contributes to Asana issue 37107676459484 --- cura/CuraApplication.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 1e7936f0c9..1fb243ab0a 100644 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -477,7 +477,9 @@ class CuraApplication(QtApplication): self._volume.rebuild() if self.getController().getTool("ScaleTool"): - self.getController().getTool("ScaleTool").setMaximumBounds(self._volume.getBoundingBox()) + bbox = self._volume.getBoundingBox() + bbox.setBottom(0.0) + self.getController().getTool("ScaleTool").setMaximumBounds(bbox) offset = machine.getSettingValueByKey("machine_platform_offset") if offset: From 074ebb92430e5a35e23f12ae4d882c249b393f4e Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Wed, 24 Jun 2015 12:06:00 +0200 Subject: [PATCH 097/117] Update changelog --- CHANGES | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/CHANGES b/CHANGES index 64858ff8ea..bd9c26751c 100644 --- a/CHANGES +++ b/CHANGES @@ -7,6 +7,22 @@ Cura 15.06 is a new release built from the ground up on a completely new framework called Uranium. This framework has been designed to make it easier to extend Cura with additional functionality as well as provide a cleaner UI. +Changes since 15.05.95 +---------------------- + +* Fixed: Selection ghost remains visible after deleting an object +* Fixed: Window does not show up immediately after starting application on OSX +* Fixed: Added display of rotation angle during rotation +* Fixed: Object changes position while rotating/scaling +* Fixed: Loading improvements in the layer view +* Fixed: Added application icons +* Fixed: Improved feedback when loading models +* Fixed: Eject device on MacOSX now provides proper feedback +* Fixed: Make it possible to show retraction settings for UM2 +* Fixed: Opening the machine preferences page will switch to the first available machine +* Fixed: Improved tool handle hit area size +* Fixed: Render lines with a thickness based on screen DPI + Changes since 15.05.94 ---------------------- @@ -150,18 +166,8 @@ For an up to date list of all known issues, please see https://github.com/Ultimaker/Cura/issues and https://github.com/Ultimaker/Uranium/issues . -* The application has no application icon yet. -* The Windows version starts a console before starting the - application. This is intentional for the beta and it will be - removed for the final version. -* Opening the machine preferences page will switch to the first - available machine instead of keeping the current machine - selected. * Some OBJ files are rendered as black objects due to missing normals. -* The developer documentation for Uranium (available at - http://software.ultimaker.com/uranium/index.html) is not yet - complete. * Disabling plugins does not work correctly yet. * Unicorn occasionally still requires feeding. Do not feed it after midnight. From 27de74848d75a9cb2107f0b9a110714c75ec237a Mon Sep 17 00:00:00 2001 From: Tamara Hogenhout Date: Wed, 24 Jun 2015 13:44:36 +0200 Subject: [PATCH 098/117] Restyling of the save to toolpath button Fixes #54 --- resources/qml/SaveButton.qml | 301 ++++++++++++++++--------------- resources/qml/Sidebar.qml | 15 +- resources/themes/cura/theme.json | 14 +- 3 files changed, 176 insertions(+), 154 deletions(-) diff --git a/resources/qml/SaveButton.qml b/resources/qml/SaveButton.qml index 07bc0a9ccc..e45d5b07b6 100644 --- a/resources/qml/SaveButton.qml +++ b/resources/qml/SaveButton.qml @@ -8,7 +8,7 @@ import QtQuick.Layouts 1.1 import UM 1.0 as UM -Button { +Rectangle { id: base; property Action saveAction; @@ -16,8 +16,6 @@ Button { property real progress: UM.Backend.progress; Behavior on progress { NumberAnimation { duration: 250; } } - enabled: progress >= 0.95; - property string currentDevice: "local_file" property bool defaultOverride: false; property bool defaultAmbiguous: false; @@ -25,9 +23,6 @@ Button { property variant printDuration: PrintInformation.currentPrintTime; property real printMaterialAmount: PrintInformation.materialAmount; - iconSource: UM.Theme.icons[Printer.outputDevices[base.currentDevice].icon]; - tooltip: Printer.outputDevices[base.currentDevice].description; - Connections { target: Printer; onOutputDevicesChanged: { @@ -51,161 +46,183 @@ Button { } } - style: ButtonStyle { - background: UM.AngledCornerRectangle { - implicitWidth: control.width; - implicitHeight: control.height; + Rectangle{ + id: background + implicitWidth: base.width; + implicitHeight: parent.height; + color: UM.Theme.colors.save_button_background; + border.width: UM.Theme.sizes.save_button_border.width + border.color: UM.Theme.colors.save_button_border - color: UM.Theme.colors.save_button_border; - cornerSize: UM.Theme.sizes.default_margin.width; + Rectangle { + id: infoBox + width: parent.width - UM.Theme.sizes.default_margin.width * 2; + height: UM.Theme.sizes.save_button_slicing_bar.height - UM.AngledCornerRectangle { - anchors.fill: parent; - anchors.margins: UM.Theme.sizes.save_button_border.width; - cornerSize: UM.Theme.sizes.default_margin.width; - color: UM.Theme.colors.save_button; - } + anchors.top: parent.top + anchors.topMargin: UM.Theme.sizes.default_margin.height; + anchors.left: parent.left + anchors.leftMargin: UM.Theme.sizes.default_margin.width; - UM.AngledCornerRectangle { - anchors { - left: parent.left; - top: parent.top; - bottom: parent.bottom; - } - - width: Math.max(parent.height + (parent.width - parent.height) * control.progress, parent.height); - cornerSize: UM.Theme.sizes.default_margin.width; - - color: !control.enabled ? UM.Theme.colors.save_button_inactive : control.hovered ? UM.Theme.colors.save_button_active_hover : UM.Theme.colors.save_button_active; - Behavior on color { ColorAnimation { duration: 50; } } - } - - UM.AngledCornerRectangle { - anchors.left: parent.left; - width: parent.height + UM.Theme.sizes.save_button_border.width; - height: parent.height; - cornerSize: UM.Theme.sizes.default_margin.width; - color: UM.Theme.colors.save_button; - } - - UM.AngledCornerRectangle { - anchors.left: parent.left; - width: parent.height + UM.Theme.sizes.save_button_border.width; - height: parent.height; - cornerSize: UM.Theme.sizes.default_margin.width; - - color: UM.Theme.colors.save_button; - } - - UM.AngledCornerRectangle { - id: icon; - - anchors.left: parent.left; - width: parent.height; - height: parent.height; - cornerSize: UM.Theme.sizes.default_margin.width; - color: !control.enabled ? UM.Theme.colors.save_button_inactive : control.hovered ? UM.Theme.colors.save_button_active_hover : UM.Theme.colors.save_button_active; - Behavior on color { ColorAnimation { duration: 50; } } - - Image { - anchors.centerIn: parent; - - width: UM.Theme.sizes.button_icon.width; - height: UM.Theme.sizes.button_icon.height; - - sourceSize.width: width; - sourceSize.height: height; - - source: control.iconSource; - } - } - } - - label: Column { + border.width: UM.Theme.sizes.save_button_border.width + border.color: UM.Theme.colors.save_button_border + color: UM.Theme.colors.save_button_estimated_text_background; Label { id: label; - anchors.left: parent.left; - anchors.leftMargin: control.height + UM.Theme.sizes.save_button_label_margin.width; - - color: UM.Theme.colors.save_button_text; - font: UM.Theme.fonts.default; - - text: control.text; + anchors.verticalCenter: parent.verticalCenter + anchors.left: parent.left + anchors.leftMargin: UM.Theme.sizes.save_button_text_margin.width; + visible: base.progress >= 0 && base.progress < 0.99 ? false : true + color: UM.Theme.colors.save_button_estimated_text; + font: UM.Theme.fonts.small; + text: + if(base.progress < 0) { + //: Save button label + return qsTr("Please load a 3D model"); + } else if (base.progress < 0.99) { + //: Save button label + return qsTr("Calculating Print-time"); + } else if (base.progress > 0.99){ + //: Save button label + return qsTr("Estimated Print-time"); + } else if (base.progress == null){ + return qsTr(""); + } } Label { - anchors.left: parent.left; - anchors.leftMargin: control.height + UM.Theme.sizes.save_button_label_margin.width; - - color: UM.Theme.colors.save_button_text; - font: UM.Theme.fonts.default; - - text: (!control.printDuration || !control.printDuration.valid) ? "" : control.printDuration.getDisplayString(UM.DurationFormat.Long) + id: printDurationLabel + anchors.verticalCenter: parent.verticalCenter + anchors.left: label.right; + anchors.leftMargin: UM.Theme.sizes.save_button_text_margin.width; + color: UM.Theme.colors.save_button_printtime_text; + font: UM.Theme.fonts.small; + visible: base.progress < 0.99 ? false : true + text: (!base.printDuration || !base.printDuration.valid) ? "" : base.printDuration.getDisplayString(UM.DurationFormat.Long); } Label { - anchors.left: parent.left; - anchors.leftMargin: control.height + UM.Theme.sizes.save_button_label_margin.width; - - color: UM.Theme.colors.save_button_text; - font: UM.Theme.fonts.default; - + id: printMaterialLabel + anchors.verticalCenter: parent.verticalCenter + anchors.left: printDurationLabel.right; + anchors.leftMargin: UM.Theme.sizes.save_button_text_margin.width; + color: UM.Theme.colors.save_button_printtime_text; + font: UM.Theme.fonts.small; + visible: base.progress < 0.99 ? false : true //: Print material amount save button label - text: control.printMaterialAmount < 0 ? "" : qsTr("%1m material").arg(control.printMaterialAmount); + text: base.printMaterialAmount < 0 ? "" : qsTr("%1m material").arg(base.printMaterialAmount); } } - } + Rectangle { + id: infoBoxOverlay + anchors { + left: infoBox.left; + top: infoBox.top; + bottom: infoBox.bottom; + } + width: Math.max(infoBox.width * base.progress); + color: UM.Theme.colors.save_button_active + visible: base.progress > 0.99 ? false : true + } - MouseArea { - anchors.fill: parent; + Button { + id: saveToButton + anchors.top: infoBox.bottom + anchors.topMargin: UM.Theme.sizes.save_button_text_margin.height; + anchors.left: parent.left + anchors.leftMargin: UM.Theme.sizes.default_margin.width; + tooltip: '' + enabled: progress >= 0.99; - acceptedButtons: Qt.RightButton; - - onClicked: devicesMenu.popup(); - } - - Menu { - id: devicesMenu; - - Instantiator { - model: Printer.outputDeviceNames; - MenuItem { - text: Printer.outputDevices[modelData].description; - checkable: true; - checked: base.defaultAmbiguous ? false : modelData == base.currentDevice; - exclusiveGroup: devicesMenuGroup; - onTriggered: { - base.defaultOverride = true; - base.currentDevice = modelData; - if(base.defaultAmbiguous) { - base.defaultAmbiguous = false; - Printer.writeToOutputDevice(modelData); + width: infoBox.width/6*4.5 + height: UM.Theme.sizes.save_button_save_to_button.height + style: ButtonStyle { + background: Rectangle { + color: !control.enabled ? UM.Theme.colors.save_button_inactive : control.hovered ? UM.Theme.colors.save_button_active_hover : UM.Theme.colors.save_button_active; + Label { + anchors.verticalCenter: parent.verticalCenter + anchors.horizontalCenter: parent.horizontalCenter + color: UM.Theme.colors.save_button_safe_to_text; + font: UM.Theme.fonts.sidebar_save_to; + text: Printer.outputDevices[base.currentDevice].description; } } } - onObjectAdded: devicesMenu.insertItem(index, object) - onObjectRemoved: devicesMenu.removeItem(object) + onClicked: + if(base.defaultAmbiguous) { + devicesMenu.popup(); + } else { + Printer.writeToOutputDevice(base.currentDevice); + } } - ExclusiveGroup { id: devicesMenuGroup; } - } + Button { + id: deviceSelectionMenu; + anchors.top: infoBox.bottom + anchors.topMargin: UM.Theme.sizes.save_button_text_margin.height + anchors.right: parent.right + anchors.rightMargin: UM.Theme.sizes.default_margin.width; + tooltip: '' - text: { - if(base.progress < 0) { - //: Save button label - return qsTr("Please load a 3D model"); - } else if (base.progress < 0.95) { - //: Save button label - return qsTr("Calculating Print-time"); - } else { - //: Save button label - return qsTr("Estimated Print-time"); + width: infoBox.width/6*1.3 - UM.Theme.sizes.save_button_text_margin.height; + height: UM.Theme.sizes.save_button_save_to_button.height + + style: ButtonStyle { + background: Rectangle { + color: UM.Theme.colors.save_button_background; + border.width: control.hovered ? UM.Theme.sizes.save_button_border.width : 0 + border.color: UM.Theme.colors.save_button_border + Rectangle { + id: deviceSelectionIcon + color: UM.Theme.colors.save_button_background; + anchors.left: parent.left + anchors.leftMargin: UM.Theme.sizes.save_button_text_margin.width / 2; + anchors.verticalCenter: parent.verticalCenter; + width: parent.height - UM.Theme.sizes.save_button_text_margin.width ; + height: parent.height - UM.Theme.sizes.save_button_text_margin.width; + UM.RecolorImage { + anchors.centerIn: parent; + width: parent.width; + height: parent.height; + sourceSize.width: width; + sourceSize.height: height; + color: UM.Theme.colors.save_button_active + source: UM.Theme.icons[Printer.outputDevices[base.currentDevice].icon]; + } + } + Label { + id: deviceSelectionArrow + anchors.right: parent.right; + anchors.rightMargin: UM.Theme.sizes.save_button_text_margin.height + anchors.verticalCenter: parent.verticalCenter; + text: "▼"; + font: UM.Theme.fonts.tiny; + color: UM.Theme.colors.save_button_active; + } + } + } + + menu: Menu { + id: devicesMenu; + Instantiator { + model: Printer.outputDeviceNames; + MenuItem { + text: Printer.outputDevices[modelData].description; + checkable: true; + checked: base.defaultAmbiguous ? false : modelData == base.currentDevice; + exclusiveGroup: devicesMenuGroup; + onTriggered: { + base.defaultOverride = true; + base.currentDevice = modelData; + if(base.defaultAmbiguous) { + base.defaultAmbiguous = false; + Printer.writeToOutputDevice(modelData); + } + } + } + onObjectAdded: devicesMenu.insertItem(index, object) + onObjectRemoved: devicesMenu.removeItem(object) + } + ExclusiveGroup { id: devicesMenuGroup; } + } } } - - onClicked: { - if(base.defaultAmbiguous) { - devicesMenu.popup(); - } else { - Printer.writeToOutputDevice(base.currentDevice); - } - } -} +} \ No newline at end of file diff --git a/resources/qml/Sidebar.qml b/resources/qml/Sidebar.qml index a40f1fdcb3..23ddfe4ed7 100644 --- a/resources/qml/Sidebar.qml +++ b/resources/qml/Sidebar.qml @@ -8,15 +8,13 @@ import QtQuick.Layouts 1.1 import UM 1.0 as UM -UM.AngledCornerRectangle { +Rectangle { id: base; property Action addMachineAction; property Action configureMachinesAction; property alias saveAction: saveButton.saveAction; - cornerSize: UM.Theme.sizes.default_margin.width; - color: UM.Theme.colors.sidebar; function showTooltip(item, position, text) { @@ -41,7 +39,6 @@ UM.AngledCornerRectangle { ColumnLayout { anchors.fill: parent; anchors.topMargin: UM.Theme.sizes.default_margin.height; - anchors.bottomMargin: UM.Theme.sizes.window_margin.height; spacing: UM.Theme.sizes.default_margin.height; @@ -75,7 +72,7 @@ UM.AngledCornerRectangle { property Item sidebar: base; onLoaded: - { + { if(item) { item.configureSettings = base.configureMachinesAction; @@ -93,13 +90,11 @@ UM.AngledCornerRectangle { SaveButton { id: saveButton; - Layout.preferredWidth: base.width - UM.Theme.sizes.default_margin.width * 2; - Layout.preferredHeight: UM.Theme.sizes.button.height; - Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter; + implicitWidth: base.width + implicitHeight: UM.Theme.sizes.save_button_text_margin.height * 2 + UM.Theme.sizes.save_button_slicing_bar.height + UM.Theme.sizes.save_button_save_to_button.height + UM.Theme.sizes.default_margin.height } - } - + SidebarTooltip { id: tooltip; } diff --git a/resources/themes/cura/theme.json b/resources/themes/cura/theme.json index 1e34028046..88a236d7ce 100644 --- a/resources/themes/cura/theme.json +++ b/resources/themes/cura/theme.json @@ -27,6 +27,10 @@ "capitalize": true, "family": "Roboto" }, + "sidebar_save_to": { + "size": 1.0, + "family": "Roboto" + }, "timeslider_time": { "size": 1.0, "bold": true, @@ -105,12 +109,15 @@ "tooltip": [255, 225, 146, 255], - "save_button": [255, 255, 255, 255], "save_button_border": [205, 202, 201, 255], "save_button_inactive": [205, 202, 201, 255], "save_button_active": [12, 159, 227, 255], "save_button_active_hover": [34, 150, 190, 255], - "save_button_text": [35, 35, 35, 255], + "save_button_safe_to_text": [255, 255, 255, 255], + "save_button_estimated_text": [140, 144, 154, 255], + "save_button_estimated_text_background": [255, 255, 255, 255], + "save_button_printtime_text": [12, 169, 227, 255], + "save_button_background": [249, 249, 249, 255], "message": [205, 202, 201, 255], "message_text": [35, 35, 35, 255], @@ -153,7 +160,10 @@ "tooltip_margins": [1.0, 1.0], "save_button_border": [0.06, 0.06], + "save_button_text_margin": [0.6, 0.6], + "save_button_slicing_bar": [0.0, 2.2], "save_button_label_margin": [0.5, 0.5], + "save_button_save_to_button": [0.3, 2.7], "message": [30.0, 5.0], "message_close": [1.25, 1.25] From 492f6309b0c1b928265cc8a32499318bce3d1d04 Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Wed, 24 Jun 2015 14:51:45 +0200 Subject: [PATCH 099/117] If we skip an object because it does not have a bounding box, retrigger the change timer This causes the platform physics to run again a few ms later so that we actually do not stop trying to push objects apart until everything is correctly updated Fixes Asana issue 33694499624771 --- cura/PlatformPhysics.py | 1 + 1 file changed, 1 insertion(+) diff --git a/cura/PlatformPhysics.py b/cura/PlatformPhysics.py index d7276c9773..444fe2d3cc 100644 --- a/cura/PlatformPhysics.py +++ b/cura/PlatformPhysics.py @@ -49,6 +49,7 @@ class PlatformPhysics: bbox = node.getBoundingBox() if not bbox or not bbox.isValid(): + self._change_timer.start() continue # Mark the node as outside the build volume if the bounding box test fails. From d0287b97d77a3d79cce807dabb153018e98b453c Mon Sep 17 00:00:00 2001 From: Tamara Hogenhout Date: Wed, 24 Jun 2015 16:11:25 +0200 Subject: [PATCH 100/117] lowers the printbed so the mesh and grid no longer z-fight Lowers the height of the printbed of the Ultimaker Original platvorm mesh by 1 mm Fixes #66 --- resources/meshes/ultimaker_platform.stl | Bin 174284 -> 174284 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/resources/meshes/ultimaker_platform.stl b/resources/meshes/ultimaker_platform.stl index cd8893143e69a7c66e1403b69a7679d8a75e3f5b..e81aeefb04fb9adf008cb71280b8cb1051b6bfef 100644 GIT binary patch delta 51813 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 UkafOjawo{RV-Pe=TY*SXQSyHqp`H9Xl9JpU3q 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 oo$R9yMCcw)O Date: Wed, 24 Jun 2015 16:02:05 +0200 Subject: [PATCH 101/117] Update setting ranges to reflect those that are used in the current Cura Fixes Asana issue 35497030487594 --- resources/settings/fdmprinter.json | 141 ++++++++++++++++++++++++----- 1 file changed, 119 insertions(+), 22 deletions(-) diff --git a/resources/settings/fdmprinter.json b/resources/settings/fdmprinter.json index 42bee91167..35fae31182 100644 --- a/resources/settings/fdmprinter.json +++ b/resources/settings/fdmprinter.json @@ -41,9 +41,9 @@ "unit": "mm", "type": "float", "default": 0.1, - "min_value": 0.00001, + "min_value": 0.0001, "min_value_warning": 0.04, - "max_value_warning": 2.0, + "max_value_warning": 0.32, "always_visible": true, "children": { "layer_height_0": { @@ -52,9 +52,9 @@ "unit": "mm", "type": "float", "default": 0.3, - "min_value": 0.0, + "min_value": 0.0001, "min_value_warning": 0.04, - "max_value_warning": 2.0, + "max_value_warning": 0.32, "visible": false } } @@ -66,7 +66,8 @@ "type": "float", "default": 0.8, "min_value": 0.0, - "max_value": 5.0, + "min_value_warning": 0.2, + "max_value_warning": 5.0, "children": { "wall_thickness": { "label": "Wall Thickness", @@ -74,9 +75,8 @@ "unit": "mm", "default": 0.8, "min_value": 0.0, - "max_value": 5.0, - "min_value_warning": 0.4, - "max_value_warning": 2.0, + "min_value_warning": 0.2, + "max_value_warning": 5.0, "type": "float", "visible": false, @@ -84,6 +84,7 @@ "wall_line_count": { "label": "Wall Line Count", "description": "Number of shell lines. This these lines are called perimeter lines in other tools and impact the strength and structural integrity of your print.", + "min_value": 0, "default": 2, "type": "int", "visible": false, @@ -93,6 +94,9 @@ "label": "Wall Line Width", "description": "Width of a single shell line. Each line of the shell will be printed with this width in mind.", "unit": "mm", + "min_value": 0.0, + "min_value_warning": 0.2, + "max_value_warning": 5.0, "default": 0.4, "type": "float", "visible": false, @@ -103,6 +107,9 @@ "label": "First Wall Line Width", "description": "Width of the outermost shell line. By printing a thinner outermost wall line you can print higher details with a larger nozzle.", "unit": "mm", + "min_value": 0.0, + "min_value_warning": 0.2, + "max_value_warning": 5.0, "default": 0.4, "type": "float", "visible": false @@ -111,6 +118,9 @@ "label": "Other Walls Line Width", "description": "Width of a single shell line for all shell lines except the outermost one.", "unit": "mm", + "min_value": 0.0, + "min_value_warning": 0.2, + "max_value_warning": 5.0, "default": 0.4, "type": "float", "visible": false @@ -119,6 +129,9 @@ "label": "Skirt line width", "description": "Width of a single skirt line.", "unit": "mm", + "min_value": 0.0, + "min_value_warning": 0.2, + "max_value_warning": 5.0, "default": 0.4, "type": "float", "visible": false @@ -127,6 +140,9 @@ "label": "Top/bottom line width", "description": "Width of a single top/bottom printed line. Which are used to fill up the top/bottom areas of a print.", "unit": "mm", + "min_value": 0.0, + "min_value_warning": 0.2, + "max_value_warning": 5.0, "default": 0.4, "type": "float", "visible": false @@ -135,6 +151,9 @@ "label": "Infill line width", "description": "Width of the inner infill printed lines.", "unit": "mm", + "min_value": 0.0, + "min_value_warning": 0.2, + "max_value_warning": 5.0, "default": 0.4, "type": "float", "visible": false @@ -143,6 +162,9 @@ "label": "Support line width", "description": "Width of the printed support structures lines.", "unit": "mm", + "min_value": 0.0, + "min_value_warning": 0.2, + "max_value_warning": 5.0, "default": 0.4, "type": "float", "visible": false @@ -168,6 +190,7 @@ "label": "Top Thickness", "description": "This controls the thickness of the top layers. The number of solid layers printed is calculated from the layer thickness and this value. Having this value be a multiple of the layer thickness makes sense. And keep it nearto your wall thickness to make an evenly strong part.", "unit": "mm", + "min_value": 0.0, "default": 0.8, "type": "float", "visible": false, @@ -176,6 +199,7 @@ "top_layers": { "label": "Top Layers", "description": "This controls the amount of top layers.", + "min_value": 0, "default": 4, "type": "int", "visible": false, @@ -187,6 +211,7 @@ "label": "Bottom Thickness", "description": "This controls the thickness of the bottom layers. The number of solid layers printed is calculated from the layer thickness and this value. Having this value be a multiple of the layer thickness makes sense. And keep it near to your wall thickness to make an evenly strong part.", "unit": "mm", + "min_value": 0.0, "default": 0.8, "type": "float", "visible": false, @@ -195,6 +220,7 @@ "bottom_layers": { "label": "Bottom Layers", "description": "This controls the amount of bottom layers.", + "min_value": 0, "default": 4, "type": "int", "visible": false, @@ -256,18 +282,18 @@ "description": "The temperature used for printing. Set at 0 to pre-heat yourself. For PLA a value of 210C is usually used.\nFor ABS a value of 230C or higher is required.", "unit": "°C", "type": "float", - "default": 220, - "min_value": 10, - "max_value": 340 + "default": 210, + "min_value": 0, + "max_value_warning": 260 }, "material_bed_temperature": { "label": "Bed Temperature", "description": "The temperature used for the heated printer bed. Set at 0 to pre-heat it yourself.", "unit": "°C", "type": "float", - "default": 70, + "default": 60, "min_value": 0, - "max_value": 340 + "max_value_warning": 260 }, "material_diameter": { "label": "Diameter", @@ -275,8 +301,8 @@ "unit": "mm", "type": "float", "default": 2.85, - "min_value": 0.4, - "max_value": 5.0 + "min_value_warning": 0.4, + "max_value_warning": 3.5 }, "material_flow": { "label": "Flow", @@ -285,7 +311,8 @@ "default": 100.0, "type": "float", "min_value": 5.0, - "max_value": 300.0 + "min_value_warning": 50.0, + "max_value_warning": 150.0 }, "retraction_enable": { "label": "Enable Retraction", @@ -299,7 +326,8 @@ "description": "The speed at which the filament is retracted. A higher retraction speed works better, but a very high retraction speed can lead to filament grinding.", "unit": "mm/s", "type": "float", - "default": 25.0, + "min_value": 0.1, + "default": 40.0, "visible": false, "inherit": false, @@ -309,6 +337,7 @@ "description": "The speed at which the filament is retracted. A higher retraction speed works better, but a very high retraction speed can lead to filament grinding.", "unit": "mm/s", "type": "float", + "min_value": 0.1, "default": 25.0, "visible": false }, @@ -317,6 +346,7 @@ "description": "The speed at which the filament is pushed back after retraction.", "unit": "mm/s", "type": "float", + "min_value": 0.1, "default": 25.0, "visible": false } @@ -327,6 +357,7 @@ "description": "The amount of retraction: Set at 0 for no retraction at all. A value of 4.5mm seems to generate good results for 3mm filament in Bowden-tube fed printers.", "unit": "mm", "type": "float", + "min_value": 0.0, "default": 4.5, "visible": false, "inherit": false @@ -336,7 +367,8 @@ "description": "The minimum distance of travel needed for a retraction to happen at all. This helps ensure you do not get a lot of retractions in a small area.", "unit": "mm", "type": "float", - "default": 4.5, + "min_value": 0.0, + "default": 1.5, "visible": false, "inherit": false }, @@ -353,6 +385,7 @@ "description": "The minimum amount of extrusion that needs to happen between retractions. If a retraction should happen before this minimum is reached, it will be ignored. This avoids retracting repeatedly on the same piece of filament as that can flatten the filament and cause grinding issues.", "unit": "mm", "type": "float", + "min_value": 0.0, "default": 0.02, "visible": false, "inherit": false @@ -362,6 +395,7 @@ "description": "Whenever a retraction is done, the head is lifted by this amount to travel over the print. A value of 0.075 works well. This feature has a lot of positive effect on delta towers.", "unit": "mm", "type": "float", + "min_value": 0.0, "default": 0.0, "visible": false, "inherit": false @@ -380,6 +414,8 @@ "description": "The speed at which printing happens. A well-adjusted Ultimaker can reach 150mm/s, but for good quality prints you will want to print slower. Printing speed depends on a lot of factors, so you will need to experiment with optimal settings for this.", "unit": "mm/s", "type": "float", + "min_value": 0.1, + "max_value_warning": 150.0, "default": 50.0, "children": { @@ -388,6 +424,8 @@ "description": "The speed at which infill parts are printed. Printing the infill faster can greatly reduce printing time, but this can negatively affect print quality.", "unit": "mm/s", "type": "float", + "min_value": 0.1, + "max_value_warning": 150.0, "default": 50.0, "visible": false }, @@ -396,6 +434,8 @@ "description": "The speed at which shell is printed. Printing the outer shell at a lower speed improves the final skin quality.", "unit": "mm/s", "type": "float", + "min_value": 0.1, + "max_value_warning": 150.0, "default": 50.0, "visible": false, @@ -405,6 +445,8 @@ "description": "The speed at which outer shell is printed. Printing the outer shell at a lower speed improves the final skin quality. However, having a large difference between the inner shell speed and the outer shell speed will effect quality in a negative way.", "unit": "mm/s", "type": "float", + "min_value": 0.1, + "max_value_warning": 150.0, "default": 50.0, "visible": false }, @@ -413,6 +455,8 @@ "description": "The speed at which all inner shells are printed. Printing the inner shell fasster than the outer shell will reduce printing time. It is good to set this in between the outer shell speed and the infill speed.", "unit": "mm/s", "type": "float", + "min_value": 0.1, + "max_value_warning": 150.0, "default": 50.0, "visible": false } @@ -423,6 +467,8 @@ "description": "Speed at which top/bottom parts are printed. Printing the top/bottom faster can greatly reduce printing time, but this can negatively affect print quality.", "unit": "mm/s", "type": "float", + "min_value": 0.1, + "max_value_warning": 150.0, "default": 50.0, "visible": false }, @@ -431,6 +477,8 @@ "description": "The speed at which exterior support is printed. Printing exterior supports at higher speeds can greatly improve printing time. And the surface quality of exterior support is usually not important, so higher speeds can be used.", "unit": "mm/s", "type": "float", + "min_value": 0.1, + "max_value_warning": 150.0, "default": 50.0, "visible": false, "inherit_function": "speed_wall_0" @@ -442,6 +490,8 @@ "description": "The speed at which travel moves are done. A well-built Ultimaker can reach speeds of 250mm/s. But some machines might have misaligned layers then.", "unit": "mm/s", "type": "float", + "min_value": 0.1, + "max_value_warning": 300.0, "default": 150.0 }, "speed_layer_0": { @@ -449,6 +499,7 @@ "description": "The print speed for the bottom layer: You want to print the first layer slower so it sticks to the printer bed better.", "unit": "mm/s", "type": "float", + "min_value": 0.1, "default": 15.0, "visible": false, @@ -458,6 +509,7 @@ "description": "The speed at which the skirt and brim are printed. Normally this is done at the initial layer speed. But sometimes you want to print the skirt at a different speed.", "unit": "mm/s", "type": "float", + "min_value": 0.1, "default": 15.0, "visible": false } @@ -467,6 +519,7 @@ "label": "Amount of Slower Layers", "description": "The first few layers are printed slower then the rest of the object, this to get better adhesion to the printer bed and improve the overall success rate of prints. The speed is gradually increased over these layers. 4 layers of speed-up is generally right for most materials and printers.", "type": "int", + "min_value": 0, "default": 4, "visible": false } @@ -482,6 +535,8 @@ "description": "This controls how densely filled the insides of your print will be. For a solid part use 100%, for an hollow part use 0%. A value around 20% is usually enough. This won't affect the outside of the print and only adjusts how strong the part becomes.", "unit": "%", "type": "float", + "min_value": 0.0, + "max_value": 100.0, "default": 20.0, "children": { @@ -504,6 +559,7 @@ "description": "Distance between the printed infill lines.", "unit": "mm", "type": "float", + "min_value": 0.0, "default": 2.0, "visible": false, "inherit_function": "(infill_line_width * 100) / parent_value" @@ -515,6 +571,8 @@ "description": "The amount of overlap between the infill and the walls. A slight overlap allows the walls to connect firmly to the infill.", "unit": "%", "type": "float", + "min_value": 0.0, + "max_value": 100.0, "default": 15.0, "visible": false }, @@ -523,6 +581,7 @@ "description": "The thickness of the sparse infill. This is rounded to a multiple of the layerheight and used to print the sparse-infill in fewer, thicker layers to save printing time.", "unit": "mm", "type": "float", + "min_value": 0.0, "default": 0.1, "visible": false, @@ -556,6 +615,8 @@ "description": "Fan speed used for the print cooling fan on the printer head.", "unit": "%", "type": "float", + "min_value": 0.0, + "max_value": 100.0, "default": 100.0, "visible": false, "inherit_function": "100.0 if parent_value else 0.0", @@ -566,6 +627,8 @@ "description": "Normally the fan runs at the minimum fan speed. If the layer is slowed down due to minimum layer time, the fan speed adjusts between minimum and maximum fan speed.", "unit": "%", "type": "float", + "min_value": 0.0, + "max_value": 100.0, "default": 100.0, "visible": false }, @@ -574,6 +637,8 @@ "description": "Normally the fan runs at the minimum fan speed. If the layer is slowed down due to minimum layer time, the fan speed adjusts between minimum and maximum fan speed.", "unit": "%", "type": "float", + "min_value": 0.0, + "max_value": 100.0, "default": 100.0, "visible": false } @@ -586,6 +651,7 @@ "description": "The height at which the fan is turned on completely. For the layers below this the fan speed is scaled linearly with the fan off for the first layer.", "unit": "mm", "type": "float", + "min_value": 0.0, "default": 0.5, "visible": false, @@ -594,6 +660,7 @@ "label": "Fan Full on at Layer", "description": "The layer number at which the fan is turned on completely. For the layers below this the fan speed is scaled linearly with the fan off for the first layer.", "type": "int", + "min_value": 0, "default": 4, "visible": false, "inherit_function": "int((parent_value - layer_height_0 + 0.001) / layer_height)" @@ -605,6 +672,7 @@ "description": "The minimum time spent in a layer: Gives the layer time to cool down before the next one is put on top. If a layer would print in less time, then the printer will slow down to make sure it has spent at least this many seconds printing the layer.", "unit": "sec", "type": "float", + "min_value": 0.0, "default": 5.0, "visible": false }, @@ -613,6 +681,7 @@ "description": "The minimum time spent in a layer which will cause the fan to be at minmum speed. The fan speed increases linearly from maximal fan speed for layers taking minimal layer time to minimal fan speed for layers taking the time specified here.", "unit": "sec", "type": "float", + "min_value": 0.0, "default": 10.0, "visible": false }, @@ -621,6 +690,7 @@ "description": "The minimum layer time can cause the print to slow down so much it starts to droop. The minimum feedrate protects against this. Even if a print gets slowed down it will never be slower than this minimum speed.", "unit": "mm/s", "type": "float", + "min_value": 0.0, "default": 10.0, "visible": false }, @@ -649,7 +719,6 @@ "description": "Where to place support structures. The placement can be restricted such that the support structures won't rest on the model, which could otherwise cause scarring.", "type": "enum", "options": [ - "None", "Touching Buildplate", "Everywhere" ], @@ -665,6 +734,8 @@ "description": "The maximum angle of overhangs for which support will be added. With 0 degrees being horizontal, and 90 degrees being vertical.", "unit": "°", "type": "float", + "min_value": 0.0, + "max_value": 90.0, "default": 60.0, "visible": false, "active_if": { @@ -677,6 +748,8 @@ "description": "Distance of the support structure from the print, in the X/Y directions. 0.7mm typically gives a nice distance from the print so the support does not stick to the surface.", "unit": "mm", "type": "float", + "min_value": 0.0, + "max_value_warning": 10.0, "default": 0.7, "visible": false, "active_if": { @@ -689,6 +762,8 @@ "description": "Distance from the top/bottom of the support to the print. A small gap here makes it easier to remove the support but makes the print a bit uglier. 0.15mm allows for easier separation of the support structure.", "unit": "mm", "type": "float", + "min_value": 0.0, + "max_value_warning": 10.0, "default": 0.15, "visible": false, "active_if": { @@ -700,6 +775,8 @@ "label": "Top Distance", "description": "Distance from the top of the support to the print.", "unit": "mm", + "min_value": 0.0, + "max_value_warning": 10.0, "default": 0.15, "type": "float", "visible": false, @@ -712,6 +789,8 @@ "label": "Bottom Distance", "description": "Distance from the print to the bottom of the support.", "unit": "mm", + "min_value": 0.0, + "max_value_warning": 10.0, "default": 0.15, "type": "float", "visible": false, @@ -798,6 +877,8 @@ "description": "The angle of the rooftop of a tower. Larger angles mean more pointy towers. ", "unit": "°", "type": "int", + "min_value": 0, + "max_value": 90, "default": 65, "visible": false, "active_if": { @@ -837,6 +918,8 @@ "description": "The amount of infill structure in the support, less infill gives weaker support which is easier to remove.", "unit": "%", "type": "float", + "min_value": 0.0, + "max_value": 100.0, "default": 15, "visible": false, "active_if": { @@ -850,6 +933,7 @@ "description": "Distance between the printed support lines.", "unit": "mm", "type": "float", + "min_value": 0.0, "default": 2.66, "visible": false, "active_if": { @@ -882,6 +966,8 @@ "label": "Skirt Line Count", "description": "The skirt is a line drawn around the first layer of the. This helps to prime your extruder, and to see if the object fits on your platform. Setting this to 0 will disable the skirt. Multiple skirt lines can help to prime your extruder better for small objects.", "type": "int", + "min_value": 0, + "max_value_warning": 100, "default": 1, "active_if": { "setting": "adhesion_type", @@ -893,6 +979,7 @@ "description": "The horizontal distance between the skirt and the first layer of the print.\nThis is the minimum distance, multiple skirt lines will extend outwards from this distance.", "unit": "mm", "type": "float", + "min_value": 0.0, "default": 3.0, "active_if": { "setting": "adhesion_type", @@ -904,7 +991,8 @@ "description": "The minimum length of the skirt. If this minimum length is not reached, more skirt lines will be added to reach this minimum length. Note: If the line count is set to 0 this is ignored.", "unit": "mm", "type": "float", - "default": 250, + "min_value": 0.0, + "default": 250.0, "active_if": { "setting": "adhesion_type", "value": "None" @@ -914,7 +1002,9 @@ "label": "Brim Line Count", "description": "The amount of lines used for a brim: More lines means a larger brim which sticks better, but this also makes your effective print area smaller.", "type": "int", - "default": 10, + "min_value": 0, + "max_value_warning": 100, + "default": 20, "active_if": { "setting": "adhesion_type", "value": "Brim" @@ -925,6 +1015,7 @@ "description": "If the raft is enabled, this is the extra raft area around the object which is also given a raft. Increasing this margin will create a stronger raft while using more material and leaving less area for your print.", "unit": "mm", "type": "float", + "min_value": 0.0, "default": 5.0, "active_if": { "setting": "adhesion_type", @@ -936,7 +1027,8 @@ "description": "The distance between the raft lines. The first 2 layers of the raft have this amount of spacing between the raft lines.", "unit": "mm", "type": "float", - "default": 5.0, + "min_value": 0.0, + "default": 3.0, "active_if": { "setting": "adhesion_type", "value": "Raft" @@ -947,6 +1039,7 @@ "description": "Layer thickness of the first raft layer. This should be a thick layer which sticks firmly to the printer bed.", "unit": "mm", "type": "float", + "min_value": 0.0, "default": 0.3, "active_if": { "setting": "adhesion_type", @@ -980,6 +1073,7 @@ "description": "Thickness of the 2nd raft layer.", "unit": "mm", "type": "float", + "min_value": 0.0, "default": 0.27, "active_if": { "setting": "adhesion_type", @@ -991,6 +1085,7 @@ "description": "Width of the 2nd raft layer lines. These lines should be thinner than the first layer, but strong enough to attach the object to.", "unit": "mm", "type": "float", + "min_value": 0.0, "default": 0.4, "active_if": { "setting": "adhesion_type", @@ -1002,6 +1097,7 @@ "description": "The gap between the final raft layer and the first layer of the object. Only the first layer is raised by this amount to lower the bonding between the raft layer and the object. Makes it easier to peel off the raft.", "unit": "mm", "type": "float", + "min_value": 0.0, "default": 0.22, "active_if": { "setting": "adhesion_type", @@ -1012,6 +1108,7 @@ "label": "Raft Surface Layers", "description": "The number of surface layers on top of the 2nd raft layer. These are fully filled layers that the object sits on. 2 layers usually works fine.", "type": "int", + "min_value": 0, "default": 2, "active_if": { "setting": "adhesion_type", From ca2c1037e149bdc85e63ee95806b0e3a41cfd114 Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Wed, 24 Jun 2015 17:00:33 +0200 Subject: [PATCH 102/117] Do not hide enable retraction check box even if all children are visible Fixes Asana issue 38958940640341 --- resources/settings/fdmprinter.json | 1 + 1 file changed, 1 insertion(+) diff --git a/resources/settings/fdmprinter.json b/resources/settings/fdmprinter.json index 35fae31182..351f92b480 100644 --- a/resources/settings/fdmprinter.json +++ b/resources/settings/fdmprinter.json @@ -319,6 +319,7 @@ "description": "Retract the filament when the nozzle is moving over a non-printed area. Details about the retraction can be configured in the advanced tab.", "type": "boolean", "default": true, + "always_visible": true, "children": { "retraction_speed": { From 5f2d76aca9c48d9e874d0dfffccf238e41157270 Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Wed, 24 Jun 2015 17:01:13 +0200 Subject: [PATCH 103/117] Update UMO start/end gcode to reflect what is used in Cura 15.04 Fixes Asana issue 38958940640350 --- resources/settings/ultimaker_original.json | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/resources/settings/ultimaker_original.json b/resources/settings/ultimaker_original.json index ab8f0290a9..360bbbe6b6 100644 --- a/resources/settings/ultimaker_original.json +++ b/resources/settings/ultimaker_original.json @@ -19,7 +19,14 @@ "machine_nozzle_gantry_distance": { "default": 55 }, "machine_nozzle_offset_x_1": { "default": 18.0 }, "machine_nozzle_offset_y_1": { "default": 0.0 }, - "machine_gcode_flavor": { "default": "RepRap (Marlin/Sprinter)" } + "machine_gcode_flavor": { "default": "RepRap (Marlin/Sprinter)" }, + + "machine_start_gcode": { + "default": "G21 ;metric values\nG90 ;absolute positioning\nM82 ;set extruder to absolute mode\nM107 ;start with the fan off\nG28 X0 Y0 ;move X/Y to min endstops\nG28 Z0 ;move Z to min endstops\nG1 Z15.0 F9000 ;move the platform down 15mm\nG92 E0 ;zero the extruded length\nG1 F200 E3 ;extrude 3mm of feed stock\nG92 E0 ;zero the extruded length again\nG1 F9000\n;Put printing message on LCD screen\nM117 Printing..." + }, + "machine_end_gcode": { + "default": "M104 S0 ;extruder heater off\nM140 S0 ;heated bed heater off (if you have it)\nG91 ;relative positioning\nG1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\nG1 Z+0.5 E-5 X-20 Y-20 F9000 ;move Z up a bit and retract filament even more\nG28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\nM84 ;steppers off\nG90 ;absolute positioning" + } }, "categories": { From 22fecc4cd0d8bd3af48d303bf2b452a5a65c7ccf Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Wed, 24 Jun 2015 10:43:24 -0700 Subject: [PATCH 104/117] Properly add and set the window icon --- cura/CuraApplication.py | 4 +++- resources/images/cura-icon.png | Bin 0 -> 2145 bytes 2 files changed, 3 insertions(+), 1 deletion(-) create mode 100644 resources/images/cura-icon.png diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 1fb243ab0a..718f7f1e38 100644 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -36,7 +36,7 @@ from . import PrintInformation from . import CuraActions from PyQt5.QtCore import pyqtSlot, QUrl, Qt, pyqtSignal, pyqtProperty -from PyQt5.QtGui import QColor +from PyQt5.QtGui import QColor, QIcon import platform import sys @@ -52,6 +52,8 @@ class CuraApplication(QtApplication): super().__init__(name = "cura", version = "15.05.95") + self.setWindowIcon(QIcon(Resources.getPath(Resources.ImagesLocation, "cura-icon.png"))) + self.setRequiredPlugins([ "CuraEngineBackend", "MeshView", diff --git a/resources/images/cura-icon.png b/resources/images/cura-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..ecfe0d1e4efe064ae9e5154e423d15b303fa4dd2 GIT binary patch literal 2145 zcmWkwc|6o#7ycS##u8%*$(EY2HI#m%R5Mi8m?XSYVk{F9M%fL`s4R)_mVFIlDdZJl z!cfXGS+n%ZI+Cq0k#z>|ckbtN?z!iA&NDdMeML z2f+Cw#)Szw0so8Uwoq_{Ly56q4D&O);ST`j13cp^HICZ?LHPh*dqA+u{o6or za4_nQ?_Ga47r)ym(tVGt<@1LDKxo4JvXN~__EL6)x09V>J9ch@b3WpGiD<@uQ8#0B z?4l)vm;53er6Dc*vozJ~{H#afa(0d_oKNHPZW3KIHe_b0}%Qj7hcQx(4 zmRa-|h&X-Nf*z^Y8kKisSY+zM)kCiZ{aDDvT1Y+nYpVs{t*r%@900`)_@4zJ`(OvJ zMin6(&=T-tFRsIi1R32OYS0_V-G^`Mfsi!G?Mp^Ko3V6)`lQ{`41 z(qLvF!t1yN9*JpGj`*!Rcz2#8|UfJ%Dr0Htso%c(%L_rd(WR%+ z%8XUW5SfGsyZ4@`AlCXqfY%0uqiiX`=hjbb;u|uUM?JKuf4_{F5(-zJD%am2rRzx= zhRvFiIi+KJR)71bwjyH8JAz5M+xDva3UJ62d|TCd3E7 zBE*k>gNtA>RWV&TBXqWuyA9uG_vUY{&y>zz{P~WQe^n{XOI6=e0&HLSPYU4;0>gcU z7YUs{0r}=v27=2=TLo6k$Ul3%d|Q>mGkvy(0r1{HR7>)nzvLc|g%QB^B{h(a3D zuvtPhh2`4#Y&*n%A~AElhuHoFu!mYxW+L0%83rW@)3TMv>%F67-j1VZ1WQMt%9F(A z%2oAS+lp~o^lMfpW`F5Jry-Yh&OgDN^{grO@}(_AzlR;V)?q+IHhCbgpsvt6~3ET#~FL7*HA3*YF#m(sO;%3-yoxMdM%VPH0D)=bAn453R;;g z)@!-xUGiPhPK^&YO2M3+Z{6(Pd}(U2d|8Ft zWpTO(yW|TXTZ*y@uk9#AHM)U!xsy|pBcB24r2Q3fG3))^-h?5hNHwI9!*BA&W9S_> z+Oz%o=f$6V|6Z5=3D?HERys-BH2gA{<|lU#dHb~`_H%n^CchSbn94WYKK_TfkG@%V z4KAy)HD6*)tM1PXo09m5rDyJ3+I)KJY_H@r#9AAEE39^#UMoCACd)P7k!N9*0)re7aV7KHIjN!F zah04!89yW+M&+gWss-cK0K@6K>&J4r*N>APuSg8@~nx+USHQn9`(~cWWutFtyjYh(Q_IN z9jGAY{>i}Z`6C1X_CzqeyKXndxqq(6p;McbyZwV`0swZo#&^~aSlnIqpM;$@-lNPW zKG)#;3|<%Nk4m19H!^``Vjhh0F&WjgK)0B&Nh(#84g}_qKCRM^wbj z)8=A>e2^2hIi4iK@?$xNjY)KP)x4CEd7&vTUwl~&Gl<=BNn)v%CKxs$#t*mBn18-w ziRBXusk?PtA18qh5jW;ukwSB4$1o=AQc`l0~ZC?)leROR-1>P!b^hqKzjjL zkc3K&0yp@#N;ljOQuK(%lMK~92xfJ#E`aeg^<^ MW8CG+i_Q=K4@tB4iU0rr literal 0 HcmV?d00001 From 753e9826e0824c96b4288417d29ec56a490a24d7 Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Wed, 24 Jun 2015 10:47:04 -0700 Subject: [PATCH 105/117] Bump version to 15.05.96 --- cura/CuraApplication.py | 2 +- installer.nsi | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 718f7f1e38..56a43dfc24 100644 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -50,7 +50,7 @@ class CuraApplication(QtApplication): if not hasattr(sys, "frozen"): Resources.addResourcePath(os.path.join(os.path.abspath(os.path.dirname(__file__)), "..")) - super().__init__(name = "cura", version = "15.05.95") + super().__init__(name = "cura", version = "15.05.96") self.setWindowIcon(QIcon(Resources.getPath(Resources.ImagesLocation, "cura-icon.png"))) diff --git a/installer.nsi b/installer.nsi index b5adb4b572..54dd23a14d 100644 --- a/installer.nsi +++ b/installer.nsi @@ -1,5 +1,5 @@ !ifndef VERSION - !define VERSION '15.05.95' + !define VERSION '15.05.96' !endif ; The name of the installer From 2182163cbf2a81c4d532b2f1ad620132fd9e9d66 Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Wed, 24 Jun 2015 10:51:44 -0700 Subject: [PATCH 106/117] Use the application as source for the shortcut icon --- installer.nsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/installer.nsi b/installer.nsi index 54dd23a14d..e9d02fcaa4 100644 --- a/installer.nsi +++ b/installer.nsi @@ -92,7 +92,7 @@ Section "Cura ${VERSION}" CreateDirectory "$SMPROGRAMS\Cura ${VERSION}" CreateShortCut "$SMPROGRAMS\Cura ${VERSION}\Uninstall Cura ${VERSION}.lnk" "$INSTDIR\uninstall.exe" "" "$INSTDIR\uninstall.exe" 0 - CreateShortCut "$SMPROGRAMS\Cura ${VERSION}\Cura ${VERSION}.lnk" "$INSTDIR\Cura.exe" '' "$INSTDIR\resources\cura.ico" 0 + CreateShortCut "$SMPROGRAMS\Cura ${VERSION}\Cura ${VERSION}.lnk" "$INSTDIR\Cura.exe" '' "$INSTDIR\Cura.exe" 0 SectionEnd From 55118c184bcc1d7f3e3d1be1ebe063d43fe7ae2c Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Wed, 24 Jun 2015 10:54:15 -0700 Subject: [PATCH 107/117] Properly run the vcredist installer in quiet mode --- installer.nsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/installer.nsi b/installer.nsi index e9d02fcaa4..35c7fcb596 100644 --- a/installer.nsi +++ b/installer.nsi @@ -107,7 +107,7 @@ Section "Install Visual Studio 2010 Redistributable" File "vcredist_2010_20110908_x86.exe" IfSilent +2 - ExecWait '"$INSTDIR\vcredist_2010_20110908_x86.exe" /silent /norestart' + ExecWait '"$INSTDIR\vcredist_2010_20110908_x86.exe" /q /norestart' SectionEnd From 6d21ff40f538652ff013b124cd9492f1d19919da Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Wed, 24 Jun 2015 10:55:21 -0700 Subject: [PATCH 108/117] Switch to using a proper windows application --- setup.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index 1c82f07653..7204eedbf7 100644 --- a/setup.py +++ b/setup.py @@ -47,8 +47,8 @@ setup(name="Cura", url="http://software.ultimaker.com/", license="GNU AFFERO GENERAL PUBLIC LICENSE (AGPL)", scripts=["cura_app.py"], - #windows=[{"script": "printer.py", "dest_name": "Cura"}], - console=[{"script": "cura_app.py"}], + windows=[{"script": "cura_app.py", "dest_name": "Cura", "icon_resources": [(1, "icons/cura.ico")]}], + #console=[{"script": "cura_app.py"}], options={"py2exe": {"skip_archive": False, "includes": includes}}) print("Coping Cura plugins.") From 1f167987cbc6d9125e74f0766d39cfb49cbe2504 Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Wed, 24 Jun 2015 10:58:42 -0700 Subject: [PATCH 109/117] Exclude ConsoleLogger and MLP* and OBJWriter plugins from the windows installer ConsoleLogger causes problems with the proper windows build and the other three are not currently working. --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 7204eedbf7..43074ab177 100644 --- a/setup.py +++ b/setup.py @@ -52,7 +52,7 @@ setup(name="Cura", options={"py2exe": {"skip_archive": False, "includes": includes}}) print("Coping Cura plugins.") -shutil.copytree(os.path.dirname(UM.__file__) + "/../plugins", "dist/plugins") +shutil.copytree(os.path.dirname(UM.__file__) + "/../plugins", "dist/plugins", ignore = shutil.ignore_patterns("ConsoleLogger", "OBJWriter", "MLPWriter", "MLPReader")) for path in os.listdir("plugins"): shutil.copytree("plugins/" + path, "dist/plugins/" + path) print("Coping resources.") From fa242bd02109f58729d1d37761e0d66c81f41bf5 Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Thu, 25 Jun 2015 11:54:38 +0200 Subject: [PATCH 110/117] Remove sidebar right margin Contributes to Ultimaker/Uranium#24 --- resources/qml/Cura.qml | 1 - 1 file changed, 1 deletion(-) diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index 9756b3550f..226f4f59a9 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -272,7 +272,6 @@ UM.MainWindow { top: parent.top; bottom: parent.bottom; right: parent.right; - rightMargin: UM.Theme.sizes.window_margin.width; } width: UM.Theme.sizes.panel.width; From ad68111c0cac4bc9bd4d705b9f6951a0b9dc175f Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Thu, 25 Jun 2015 11:55:56 +0200 Subject: [PATCH 111/117] Disable the low/high quality slicing and slider settings if we are not in simple mode Contributes to #11 --- cura/PrintInformation.py | 24 ++++++++++++++++++++++-- resources/qml/SidebarSimple.qml | 3 +++ 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/cura/PrintInformation.py b/cura/PrintInformation.py index fdd835e9a0..d0346fbbb5 100644 --- a/cura/PrintInformation.py +++ b/cura/PrintInformation.py @@ -38,6 +38,8 @@ class PrintInformation(QObject): 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) @@ -103,6 +105,21 @@ class PrintInformation(QObject): 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: @@ -132,7 +149,10 @@ class PrintInformation(QObject): self._material_amount = round(amount / 10) / 100 self.materialAmountChanged.emit() - if self._slice_reason != self.SliceReason.SettingChanged: + 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: @@ -166,7 +186,7 @@ class PrintInformation(QObject): self._slice_reason = self.SliceReason.ActiveMachineChanged def _updateTimeQualitySettings(self): - if not self._current_settings: + if not self._current_settings or not self._enabled: return if not self._low_quality_settings: diff --git a/resources/qml/SidebarSimple.qml b/resources/qml/SidebarSimple.qml index 1b41da7d6f..c27f992031 100644 --- a/resources/qml/SidebarSimple.qml +++ b/resources/qml/SidebarSimple.qml @@ -20,6 +20,9 @@ Item { property variant minimumPrintTime: PrintInformation.minimumPrintTime; property variant maximumPrintTime: PrintInformation.maximumPrintTime; + Component.onCompleted: PrintInformation.enabled = true + Component.onDestruction: PrintInformation.enabled = false + ColumnLayout { anchors.fill: parent; From 1c9ed0d5de3f6a17474c826bbc65462328d62e48 Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Thu, 25 Jun 2015 12:06:05 +0200 Subject: [PATCH 112/117] Move initial layer thickness from layer height children into category This makes it possible to have a different initial layer height. Fixes Asana issue 38958940640338 --- resources/settings/fdmprinter.json | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/resources/settings/fdmprinter.json b/resources/settings/fdmprinter.json index 351f92b480..ca5785d046 100644 --- a/resources/settings/fdmprinter.json +++ b/resources/settings/fdmprinter.json @@ -44,20 +44,17 @@ "min_value": 0.0001, "min_value_warning": 0.04, "max_value_warning": 0.32, - "always_visible": true, - "children": { - "layer_height_0": { - "label": "Initial Layer Thickness", - "description": "The layer thickness of the bottom layer. A thicker bottom layer makes sticking to the bed easier.", - "unit": "mm", - "type": "float", - "default": 0.3, - "min_value": 0.0001, - "min_value_warning": 0.04, - "max_value_warning": 0.32, - "visible": false - } - } + }, + "layer_height_0": { + "label": "Initial Layer Thickness", + "description": "The layer thickness of the bottom layer. A thicker bottom layer makes sticking to the bed easier.", + "unit": "mm", + "type": "float", + "default": 0.3, + "min_value": 0.0001, + "min_value_warning": 0.04, + "max_value_warning": 0.32, + "visible": false }, "shell_thickness": { "label": "Shell Thickness", From 254f86faf75d0490e6e119ffd00e22ff5df7c397 Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Thu, 25 Jun 2015 12:17:59 +0200 Subject: [PATCH 113/117] Fix the JSON file after moving the initial layer height setting Contributes to Asana issue 38958940640338 --- resources/settings/fdmprinter.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/settings/fdmprinter.json b/resources/settings/fdmprinter.json index ca5785d046..914a05f698 100644 --- a/resources/settings/fdmprinter.json +++ b/resources/settings/fdmprinter.json @@ -43,7 +43,7 @@ "default": 0.1, "min_value": 0.0001, "min_value_warning": 0.04, - "max_value_warning": 0.32, + "max_value_warning": 0.32 }, "layer_height_0": { "label": "Initial Layer Thickness", From 8ca0e8eef2595a88136103b469f6529209f0b8d4 Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Thu, 25 Jun 2015 13:53:33 +0200 Subject: [PATCH 114/117] Account for skirt size and disallowed areas to set the scale to max size Contributes to Asana issue 37107676459484 --- cura/BuildVolume.py | 37 ++++++++++++++++++++++++++++++++----- 1 file changed, 32 insertions(+), 5 deletions(-) diff --git a/cura/BuildVolume.py b/cura/BuildVolume.py index 97a3888c6e..cf085bfe61 100644 --- a/cura/BuildVolume.py +++ b/cura/BuildVolume.py @@ -42,6 +42,9 @@ class BuildVolume(SceneNode): def setDepth(self, depth): self._depth = depth + def getDisallowedAreas(self): + return self._disallowed_areas + def setDisallowedAreas(self, areas): self._disallowed_areas = areas @@ -109,19 +112,43 @@ class BuildVolume(SceneNode): v = self._grid_mesh.getVertex(n) self._grid_mesh.setVertexUVCoordinates(n, v[0], v[2]) + disallowed_area_size = 0 if self._disallowed_areas: mb = MeshBuilder() - for area in self._disallowed_areas: + for polygon in self._disallowed_areas: + points = polygon.getPoints() mb.addQuad( - area[0], - area[1], - area[2], - area[3], + Vector(points[0, 0], 0.1, points[0, 1]), + Vector(points[1, 0], 0.1, points[1, 1]), + Vector(points[2, 0], 0.1, points[2, 1]), + Vector(points[3, 0], 0.1, points[3, 1]), color = Color(174, 174, 174, 255) ) + # Find the largest disallowed area to exclude it from the maximum scale bounds + size = abs(numpy.max(points[:, 1]) - numpy.min(points[:, 1])) + disallowed_area_size = max(size, disallowed_area_size) self._disallowed_area_mesh = mb.getData() else: self._disallowed_area_mesh = None self._aabb = AxisAlignedBox(minimum = Vector(minW, minH - 1.0, minD), maximum = Vector(maxW, maxH, maxD)) + + settings = Application.getInstance().getActiveMachine() + + skirt_size = 0.0 + if settings.getSettingValueByKey("adhesion_type") == "None": + skirt_size = settings.getSettingValueByKey("skirt_line_count") * settings.getSettingValueByKey("skirt_line_width") + settings.getSettingValueByKey("skirt_gap") + elif settings.getSettingValueByKey("adhesion_type") == "Brim": + skirt_size = settings.getSettingValueByKey("brim_line_count") * settings.getSettingValueByKey("skirt_line_width") + else: + skirt_size = settings.getSettingValueByKey("skirt_line_width") + + skirt_size += settings.getSettingValueByKey("skirt_line_width") + + scale_to_max_bounds = AxisAlignedBox( + minimum = Vector(minW + skirt_size, minH, minD + skirt_size + disallowed_area_size), + maximum = Vector(maxW - skirt_size, maxH, maxD - skirt_size - disallowed_area_size) + ) + + Application.getInstance().getController().getScene()._maximum_bounds = scale_to_max_bounds From 886dac57817dc570483863332e66c746fb06bd17 Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Thu, 25 Jun 2015 13:54:36 +0200 Subject: [PATCH 115/117] Store the disallowed areas as polygons and use those to test for intersection This prevents issues when trying to print larger things Contributes to Asana issue 37107676459484 --- cura/CuraApplication.py | 15 +++------------ cura/PlatformPhysics.py | 13 +++++++++---- 2 files changed, 12 insertions(+), 16 deletions(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 56a43dfc24..2d388ce78c 100644 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -18,6 +18,7 @@ from UM.Preferences import Preferences from UM.Message import Message from UM.PluginRegistry import PluginRegistry from UM.JobQueue import JobQueue +from UM.Math.Polygon import Polygon from UM.Scene.BoxRenderer import BoxRenderer from UM.Scene.Selection import Selection @@ -466,23 +467,13 @@ class CuraApplication(QtApplication): disallowed_areas = machine.getSettingValueByKey("machine_disallowed_areas") areas = [] if disallowed_areas: - for area in disallowed_areas: - polygon = [] - polygon.append(Vector(area[0][0], 0.2, area[0][1])) - polygon.append(Vector(area[1][0], 0.2, area[1][1])) - polygon.append(Vector(area[2][0], 0.2, area[2][1])) - polygon.append(Vector(area[3][0], 0.2, area[3][1])) - areas.append(polygon) + areas.append(Polygon(numpy.array(area, numpy.float32))) + self._volume.setDisallowedAreas(areas) self._volume.rebuild() - if self.getController().getTool("ScaleTool"): - bbox = self._volume.getBoundingBox() - bbox.setBottom(0.0) - self.getController().getTool("ScaleTool").setMaximumBounds(bbox) - offset = machine.getSettingValueByKey("machine_platform_offset") if offset: self._platform.setPosition(Vector(offset[0], offset[1], offset[2])) diff --git a/cura/PlatformPhysics.py b/cura/PlatformPhysics.py index 444fe2d3cc..9dcd15ad54 100644 --- a/cura/PlatformPhysics.py +++ b/cura/PlatformPhysics.py @@ -94,14 +94,19 @@ class PlatformPhysics: move_vector.setX(overlap[0] * 1.1) move_vector.setZ(overlap[1] * 1.1) + if hasattr(node, "_convex_hull"): + # Check for collisions between disallowed areas and the object + for area in self._build_volume.getDisallowedAreas(): + overlap = node._convex_hull.intersectsPolygon(area) + if overlap is None: + continue + + node._outside_buildarea = True + if move_vector != Vector(): op = PlatformPhysicsOperation.PlatformPhysicsOperation(node, move_vector) op.push() - if node.getBoundingBox().intersectsBox(self._build_volume.getBoundingBox()) == AxisAlignedBox.IntersectionResult.FullIntersection: - op = ScaleToBoundsOperation(node, self._build_volume.getBoundingBox()) - op.push() - def _onToolOperationStarted(self, tool): self._enabled = False From 22302534d57855bdfe1dcfd96589d447f98b63bb Mon Sep 17 00:00:00 2001 From: Tim Kuipers Date: Wed, 1 Jul 2015 17:17:20 +0200 Subject: [PATCH 116/117] added travel_compensate_overlapping_walls_enabled setting --- resources/settings/fdmprinter.json | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/resources/settings/fdmprinter.json b/resources/settings/fdmprinter.json index 40f3849349..200b09c489 100644 --- a/resources/settings/fdmprinter.json +++ b/resources/settings/fdmprinter.json @@ -261,6 +261,13 @@ } } }, + "travel_compensate_overlapping_walls_enabled": { + "label": "Compensate Wall Overlaps", + "description": "Compensate the flow for parts of a wall being laid down where there already is a piece of a wall. These overlaps occur in thin pieces in a model. Gcode generation might be slowed down considerably.", + "type": "boolean", + "default": false, + "visible": false + }, "fill_perimeter_gaps":{ "label": "Fill Gaps Between Walls", "description": "Fill the gaps created by walls where they would otherwise be overlapping. This will also fill thin walls. Optionally only the gaps occurring within the top and bottom skin can be filled.", From b573a71195fd6a9a10677841e030e85301725630 Mon Sep 17 00:00:00 2001 From: Tim Kuipers Date: Fri, 3 Jul 2015 13:23:47 +0200 Subject: [PATCH 117/117] bugfixes for raft problems: unregistered settings --- resources/settings/fdmprinter.json | 261 +++++++++++++++++++++-------- 1 file changed, 195 insertions(+), 66 deletions(-) diff --git a/resources/settings/fdmprinter.json b/resources/settings/fdmprinter.json index a238603aa6..fc0ea6fa7c 100644 --- a/resources/settings/fdmprinter.json +++ b/resources/settings/fdmprinter.json @@ -1062,72 +1062,6 @@ "value": "Raft" } }, - "raft_line_spacing": { - "label": "Raft Line Spacing", - "description": "The distance between the raft lines. The first 2 layers of the raft have this amount of spacing between the raft lines.", - "unit": "mm", - "type": "float", - "default": 5.0, - "active_if": { - "setting": "adhesion_type", - "value": "Raft" - } - }, - "raft_base_thickness": { - "label": "Raft Base Thickness", - "description": "Layer thickness of the first raft layer. This should be a thick layer which sticks firmly to the printer bed.", - "unit": "mm", - "type": "float", - "default": 0.3, - "active_if": { - "setting": "adhesion_type", - "value": "Raft" - } - }, - "raft_base_linewidth": { - "label": "Raft Base Line Width", - "description": "Width of the lines in the first raft layer. These should be thick lines to assist in bed adhesion.", - "unit": "mm", - "type": "float", - "default": 1.0, - "active_if": { - "setting": "adhesion_type", - "value": "Raft" - } - }, - "raft_base_speed": { - "label": "Raft Base Print Speed", - "description": "The speed at which the first raft layer is printed. This should be printed quite slowly, as the amount of material coming out of the nozzle is quite high.", - "unit": "mm/s", - "type": "float", - "default": 15.0, - "active_if": { - "setting": "adhesion_type", - "value": "Raft" - } - }, - "raft_interface_thickness": { - "label": "Raft Interface Thickness", - "description": "Thickness of the 2nd raft layer.", - "unit": "mm", - "type": "float", - "default": 0.27, - "active_if": { - "setting": "adhesion_type", - "value": "Raft" - } - }, - "raft_interface_linewidth": { - "label": "Raft Interface Line Width", - "description": "Width of the 2nd raft layer lines. These lines should be thinner than the first layer, but strong enough to attach the object to.", - "unit": "mm", - "type": "float", - "default": 0.4, - "active_if": { - "setting": "adhesion_type", - "value": "Raft" - } - }, "raft_airgap": { "label": "Raft Air-gap", "description": "The gap between the final raft layer and the first layer of the object. Only the first layer is raised by this amount to lower the bonding between the raft layer and the object. Makes it easier to peel off the raft.", @@ -1148,6 +1082,201 @@ "setting": "adhesion_type", "value": "Raft" } + }, + "raft_surface_thickness": { + "label": "Raft Surface Thickness", + "description": "Layer thickness of the surface raft layers.", + "unit": "mm", + "type": "float", + "default": 0.1, + "active_if": { + "setting": "adhesion_type", + "value": "Raft" + } + }, + "raft_surface_line_width": { + "label": "Raft Surface Line Width", + "description": "Width of the lines in the surface raft layers. These can be thin lines so that the top of the raft becomes smooth.", + "unit": "mm", + "type": "float", + "default": 0.3, + "active_if": { + "setting": "adhesion_type", + "value": "Raft" + } + }, + "raft_surface_line_spacing": { + "label": "Raft Surface Spacing", + "description": "The distance between the raft lines for the surface raft layers. The spacing of the interface should be equal to the line width, so that the surface is solid.", + "unit": "mm", + "type": "float", + "default": 0.3, + "active_if": { + "setting": "adhesion_type", + "value": "Raft" + }, + "inherit_function": "raft_surface_line_width" + }, + "raft_interface_thickness": { + "label": "Raft Interface Thickness", + "description": "Layer thickness of the interface raft layer.", + "unit": "mm", + "type": "float", + "default": 0.27, + "active_if": { + "setting": "adhesion_type", + "value": "Raft" + } + }, + "raft_interface_line_width": { + "label": "Raft Interface Line Width", + "description": "Width of the lines in the interface raft layer. Making the second layer extrude more causes the lines to stick to the bed.", + "unit": "mm", + "type": "float", + "default": 1.0, + "active_if": { + "setting": "adhesion_type", + "value": "Raft" + } + }, + "raft_interface_line_spacing": { + "label": "Raft Interface Spacing", + "description": "The distance between the raft lines for the interface raft layer. The spacing of the interface should be quite wide, while being dense enough to support the surface raft layers.", + "unit": "mm", + "type": "float", + "default": 2.0, + "active_if": { + "setting": "adhesion_type", + "value": "Raft" + } + }, + "raft_base_thickness": { + "label": "Raft Base Thickness", + "description": "Layer thickness of the base raft layer. This should be a thick layer which sticks firmly to the printer bed.", + "unit": "mm", + "type": "float", + "default": 0.3, + "active_if": { + "setting": "adhesion_type", + "value": "Raft" + } + }, + "raft_base_line_width": { + "label": "Raft Base Line Width", + "description": "Width of the lines in the base raft layer. These should be thick lines to assist in bed adhesion.", + "unit": "mm", + "type": "float", + "default": 1.0, + "active_if": { + "setting": "adhesion_type", + "value": "Raft" + } + }, + "raft_base_line_spacing": { + "label": "Raft Line Spacing", + "description": "The distance between the raft lines for the base raft layer. Wide spacing makes for easy removal of the raft from the build plate.", + "unit": "mm", + "type": "float", + "default": 5.0, + "active_if": { + "setting": "adhesion_type", + "value": "Raft" + } + }, + "raft_speed": { + "label": "Raft Print Speed", + "description": "The speed at which the raft is printed.", + "unit": "mm/s", + "type": "float", + "default": 30.0, + "active_if": { + "setting": "adhesion_type", + "value": "Raft" + }, + "inherit_function": "0.6 * speed_print", + "children": { + "raft_surface_speed": { + "label": "Raft Surface Print Speed", + "description": "The speed at which the surface raft layers are printed. This should be printed a bit slower, so that the nozzle can slowly smooth out adjacent surface lines.", + "unit": "mm/s", + "type": "float", + "default": 30.0, + "active_if": { + "setting": "adhesion_type", + "value": "Raft" + }, + "inherit_function": "parent_value" + }, + "raft_interface_speed": { + "label": "Raft Interface Print Speed", + "description": "The speed at which the interface raft layer is printed. This should be printed quite slowly, as the amount of material coming out of the nozzle is quite high.", + "unit": "mm/s", + "type": "float", + "default": 15.0, + "active_if": { + "setting": "adhesion_type", + "value": "Raft" + }, + "inherit_function": "0.5 * parent_value" + }, + "raft_base_speed": { + "label": "Raft Base Print Speed", + "description": "The speed at which the base raft layer is printed. This should be printed quite slowly, as the amount of material coming out of the nozzle is quite high.", + "unit": "mm/s", + "type": "float", + "default": 15.0, + "active_if": { + "setting": "adhesion_type", + "value": "Raft" + }, + "inherit_function": "0.5 * parent_value" + } + } + }, + "raft_fan_speed": { + "label": "Raft Fan Speed", + "description": "The fan speed for the raft.", + "unit": "%", + "type": "float", + "min_value": 0.0, + "max_value": 100.0, + "default": 100.0, + "visible": false, + "children": { + "raft_surface_fan_speed": { + "label": "Raft Surface Fan Speed", + "description": "The fan speed for the surface raft layers.", + "unit": "%", + "type": "float", + "min_value": 0.0, + "max_value": 100.0, + "default": 100.0, + "visible": false, + "inherit": true + }, + "raft_interface_fan_speed": { + "label": "Raft Interface Fan Speed", + "description": "The fan speed for the interface raft layer.", + "unit": "%", + "type": "float", + "min_value": 0.0, + "max_value": 100.0, + "default": 100.0, + "visible": false, + "inherit": true + }, + "raft_base_fan_speed": { + "label": "Raft Base Fan Speed", + "description": "The fan speed for the base raft layer.", + "unit": "%", + "type": "float", + "min_value": 0.0, + "max_value": 100.0, + "default": 100.0, + "visible": false, + "inherit": true + } + } } } },