From 585c3e4f06b458088e5e47259c5e11751625b898 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Fri, 1 Dec 2017 16:45:23 +0100 Subject: [PATCH 01/62] CURA-4660 Fix some mismatches between quality profiles, AA0.25 variant and UM3 definitions. --- .../um3_aa0.25_ABS_Normal_Quality.inst.cfg | 1 - .../um3_aa0.25_CPE_Normal_Quality.inst.cfg | 1 - .../um3_aa0.25_Nylon_Normal_Quality.inst.cfg | 5 ++--- .../um3_aa0.25_PC_Normal_Quality.inst.cfg | 3 +++ .../um3_aa0.25_PLA_Normal_Quality.inst.cfg | 2 +- .../um3_aa0.25_PP_Normal_Quality.inst.cfg | 1 - resources/variants/ultimaker3_aa0.25.inst.cfg | 14 ++++++++++++-- 7 files changed, 18 insertions(+), 9 deletions(-) diff --git a/resources/quality/ultimaker3/um3_aa0.25_ABS_Normal_Quality.inst.cfg b/resources/quality/ultimaker3/um3_aa0.25_ABS_Normal_Quality.inst.cfg index 5d798d556e..fc7d4d12d2 100644 --- a/resources/quality/ultimaker3/um3_aa0.25_ABS_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.25_ABS_Normal_Quality.inst.cfg @@ -14,7 +14,6 @@ setting_version = 4 cool_fan_speed = 40 infill_overlap = 15 material_final_print_temperature = =material_print_temperature - 5 -prime_tower_enable = True prime_tower_purge_volume = 0.6 prime_tower_size = 12 prime_tower_wall_thickness = 0.9 diff --git a/resources/quality/ultimaker3/um3_aa0.25_CPE_Normal_Quality.inst.cfg b/resources/quality/ultimaker3/um3_aa0.25_CPE_Normal_Quality.inst.cfg index a68b43fa2a..83e0c549c4 100644 --- a/resources/quality/ultimaker3/um3_aa0.25_CPE_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.25_CPE_Normal_Quality.inst.cfg @@ -11,7 +11,6 @@ weight = 0 setting_version = 4 [values] -infill_overlap = =10 if infill_sparse_density < 95 and infill_pattern != 'concentric' else 0 prime_tower_size = 12 prime_tower_wall_thickness = 0.9 retraction_extrusion_window = 0.5 diff --git a/resources/quality/ultimaker3/um3_aa0.25_Nylon_Normal_Quality.inst.cfg b/resources/quality/ultimaker3/um3_aa0.25_Nylon_Normal_Quality.inst.cfg index ff16fff8f1..58ddc32101 100644 --- a/resources/quality/ultimaker3/um3_aa0.25_Nylon_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.25_Nylon_Normal_Quality.inst.cfg @@ -14,9 +14,8 @@ setting_version = 4 cool_min_layer_time_fan_speed_max = 20 cool_min_speed = 12 infill_line_width = =round(line_width * 0.5 / 0.4, 2) -infill_overlap = =10 if infill_sparse_density < 95 and infill_pattern != 'concentric' else 0 machine_nozzle_cool_down_speed = 0.9 -machine_nozzle_heat_up_speed = 2.0 +machine_nozzle_heat_up_speed = 1.4 ooze_shield_angle = 40 raft_acceleration = =acceleration_layer_0 raft_airgap = =round(layer_height_0 * 0.85, 2) @@ -24,7 +23,7 @@ raft_interface_thickness = =round(machine_nozzle_size * 0.3 / 0.4, 3) raft_jerk = =jerk_layer_0 raft_margin = 10 raft_surface_thickness = =round(machine_nozzle_size * 0.2 / 0.4, 2) -retraction_min_travel = =line_width * 2 +retraction_min_travel = 5 skin_overlap = 50 speed_print = 70 speed_topbottom = =math.ceil(speed_print * 30 / 70) diff --git a/resources/quality/ultimaker3/um3_aa0.25_PC_Normal_Quality.inst.cfg b/resources/quality/ultimaker3/um3_aa0.25_PC_Normal_Quality.inst.cfg index 43b8f95677..7c9fa32949 100644 --- a/resources/quality/ultimaker3/um3_aa0.25_PC_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.25_PC_Normal_Quality.inst.cfg @@ -28,6 +28,9 @@ machine_min_cool_heat_time_window = 15 multiple_mesh_overlap = 0 ooze_shield_angle = 40 prime_tower_enable = True +prime_tower_wipe_enabled = True +raft_airgap = 0.25 +raft_interface_thickness = =max(layer_height * 1.5, 0.225) retraction_count_max = 80 retraction_hop = 2 retraction_hop_enabled = True diff --git a/resources/quality/ultimaker3/um3_aa0.25_PLA_Normal_Quality.inst.cfg b/resources/quality/ultimaker3/um3_aa0.25_PLA_Normal_Quality.inst.cfg index c1a93e12b0..1bee4b8f18 100644 --- a/resources/quality/ultimaker3/um3_aa0.25_PLA_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.25_PLA_Normal_Quality.inst.cfg @@ -17,7 +17,7 @@ cool_min_speed = 10 infill_overlap = 10 infill_pattern = grid machine_nozzle_cool_down_speed = 0.9 -machine_nozzle_heat_up_speed = 2.0 +machine_nozzle_heat_up_speed = 1.4 material_final_print_temperature = =max(-273.15, material_print_temperature - 15) material_initial_print_temperature = =max(-273.15, material_print_temperature - 10) material_print_temperature = 190 diff --git a/resources/quality/ultimaker3/um3_aa0.25_PP_Normal_Quality.inst.cfg b/resources/quality/ultimaker3/um3_aa0.25_PP_Normal_Quality.inst.cfg index 7f138f979d..58e7fdc688 100644 --- a/resources/quality/ultimaker3/um3_aa0.25_PP_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.25_PP_Normal_Quality.inst.cfg @@ -57,4 +57,3 @@ travel_avoid_distance = 3 wall_0_inset = 0 wall_line_width_x = =line_width wall_thickness = =line_width * 3 - diff --git a/resources/variants/ultimaker3_aa0.25.inst.cfg b/resources/variants/ultimaker3_aa0.25.inst.cfg index ebb584f674..e6f852c02c 100644 --- a/resources/variants/ultimaker3_aa0.25.inst.cfg +++ b/resources/variants/ultimaker3_aa0.25.inst.cfg @@ -11,7 +11,6 @@ setting_version = 4 [values] brim_width = 7 infill_line_width = 0.23 -infill_overlap = 0 layer_height_0 = 0.17 line_width = 0.23 machine_nozzle_cool_down_speed = 0.85 @@ -21,10 +20,18 @@ machine_nozzle_size = 0.25 machine_nozzle_tip_outer_diameter = 0.65 material_final_print_temperature = =material_print_temperature - 10 material_initial_print_temperature = =material_print_temperature - 5 +raft_airgap = 0.3 +raft_base_thickness = =resolveOrValue('layer_height_0') * 1.2 +raft_interface_line_spacing = =raft_interface_line_width + 0.2 +raft_interface_line_width = =line_width * 2 raft_interface_thickness = =layer_height * 1.5 +raft_jerk = =jerk_print +raft_margin = 15 +raft_surface_layers = 2 retraction_count_max = 25 retraction_extrusion_window = 1 retraction_min_travel = 0.7 +retraction_prime_speed = =retraction_speed skin_overlap = 15 speed_layer_0 = 20 speed_print = 55 @@ -32,9 +39,12 @@ speed_topbottom = 20 speed_wall = =math.ceil(speed_print * 30 / 55) support_angle = 60 support_bottom_distance = =support_z_distance / 2 +support_pattern = zigzag support_top_distance = =support_z_distance +support_use_towers = True support_z_distance = =layer_height * 2 +switch_extruder_prime_speed = =switch_extruder_retraction_speeds +switch_extruder_retraction_amount = =machine_heat_zone_length top_bottom_thickness = 1.2 wall_line_width_x = 0.23 wall_thickness = 1.3 - From 033a4615ebbd7f8adaead4c641f661997f7796d2 Mon Sep 17 00:00:00 2001 From: Mark Burton Date: Fri, 1 Dec 2017 17:47:08 +0000 Subject: [PATCH 02/62] Call resetLayerData() before adding LayerDataDecorator to scene node rather than afterwards. This ensures that the layer data is reset before the scene is rendered. --- plugins/CuraEngineBackend/ProcessSlicedLayersJob.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/CuraEngineBackend/ProcessSlicedLayersJob.py b/plugins/CuraEngineBackend/ProcessSlicedLayersJob.py index 37ab451d16..7a43b43fd0 100644 --- a/plugins/CuraEngineBackend/ProcessSlicedLayersJob.py +++ b/plugins/CuraEngineBackend/ProcessSlicedLayersJob.py @@ -208,6 +208,10 @@ class ProcessSlicedLayersJob(Job): self._progress_message.hide() return + view = Application.getInstance().getController().getActiveView() + if view.getPluginId() == "SimulationView": + view.resetLayerData() + # Add LayerDataDecorator to scene node to indicate that the node has layer data decorator = LayerDataDecorator.LayerDataDecorator() decorator.setLayerData(layer_mesh) @@ -226,10 +230,6 @@ class ProcessSlicedLayersJob(Job): if self._progress_message: self._progress_message.setProgress(100) - view = Application.getInstance().getController().getActiveView() - if view.getPluginId() == "SimulationView": - view.resetLayerData() - if self._progress_message: self._progress_message.hide() From 184bfc9dfd825276f340c206c8bbea18a7c56468 Mon Sep 17 00:00:00 2001 From: Andreea Scorojitu Date: Mon, 4 Dec 2017 09:34:30 +0100 Subject: [PATCH 03/62] Update_ChangeLog_3.1_CURA-4656 --- plugins/ChangeLogPlugin/ChangeLog.txt | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/plugins/ChangeLogPlugin/ChangeLog.txt b/plugins/ChangeLogPlugin/ChangeLog.txt index 63ecb37564..0f86b4a21a 100755 --- a/plugins/ChangeLogPlugin/ChangeLog.txt +++ b/plugins/ChangeLogPlugin/ChangeLog.txt @@ -11,8 +11,20 @@ The existing Layer View has been updated in order to see a live simulation of al *Quick camera controls New buttons have been added to the interface that can quickly reposition the camera view of the buildplate. -*Increased processing speeds and performance -A 5-10% speed increase when calculating normals, loading models and slicing. +*Lock model on platform +The move tool has a new option to lock a selected model to the platform. + +*Faster profile switching speed +Duplicating and removing a profile could take Ultimaker Cura quite some time, it now happens instantly. + +*Faster printer selection +Removing a printer from the library is now instant. No more unresponsive screens. + +*Faster processing speed +A 5 - 10 % speed increase when calculating normals, loading models, and slicing. + +*Feedrate visualization +Feedrate visualization has been added to the Layer view. Using this gives the user an idea of the print speeds per model part, allowing for better control over prints. *Jogging It allows the printhead to be moved with on-screen controls. Contributed by fieldOfView. @@ -52,8 +64,8 @@ If profile settings have been modified in recommended mode under custom mode, a - Fix for Ultimaker Cura engine crashes on certain models - Fix for Uninstalling previous versions of Cura on Windows platforms - Fix for displaying visible settings -- Fix for loading legacy profiles -- Fix for importing custom single extrusion profile +- Fix for importing legacy .ini files +- Prevent skipping user agreement dialog by pressing escape [3.0.4] *Bug fixes From c3d9c1b3d8058876e328369a768c8619531e6938 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Mon, 4 Dec 2017 09:49:03 +0100 Subject: [PATCH 04/62] CURA-4667 Fix simulation view render race. Change the place where the layer data is reset. --- plugins/CuraEngineBackend/ProcessSlicedLayersJob.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/plugins/CuraEngineBackend/ProcessSlicedLayersJob.py b/plugins/CuraEngineBackend/ProcessSlicedLayersJob.py index 7a43b43fd0..26a8269183 100644 --- a/plugins/CuraEngineBackend/ProcessSlicedLayersJob.py +++ b/plugins/CuraEngineBackend/ProcessSlicedLayersJob.py @@ -61,7 +61,9 @@ class ProcessSlicedLayersJob(Job): def run(self): start_time = time() - if Application.getInstance().getController().getActiveView().getPluginId() == "SimulationView": + view = Application.getInstance().getController().getActiveView() + if view.getPluginId() == "SimulationView": + view.resetLayerData() self._progress_message.show() Job.yieldThread() if self._abort_requested: @@ -208,10 +210,6 @@ class ProcessSlicedLayersJob(Job): self._progress_message.hide() return - view = Application.getInstance().getController().getActiveView() - if view.getPluginId() == "SimulationView": - view.resetLayerData() - # Add LayerDataDecorator to scene node to indicate that the node has layer data decorator = LayerDataDecorator.LayerDataDecorator() decorator.setLayerData(layer_mesh) From ad4a27db3a36768f588754ed6d1d6d61a4586463 Mon Sep 17 00:00:00 2001 From: Mark Burton Date: Fri, 1 Dec 2017 17:47:08 +0000 Subject: [PATCH 05/62] Call resetLayerData() before adding LayerDataDecorator to scene node rather than afterwards. This ensures that the layer data is reset before the scene is rendered. --- plugins/CuraEngineBackend/ProcessSlicedLayersJob.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/CuraEngineBackend/ProcessSlicedLayersJob.py b/plugins/CuraEngineBackend/ProcessSlicedLayersJob.py index 37ab451d16..7a43b43fd0 100644 --- a/plugins/CuraEngineBackend/ProcessSlicedLayersJob.py +++ b/plugins/CuraEngineBackend/ProcessSlicedLayersJob.py @@ -208,6 +208,10 @@ class ProcessSlicedLayersJob(Job): self._progress_message.hide() return + view = Application.getInstance().getController().getActiveView() + if view.getPluginId() == "SimulationView": + view.resetLayerData() + # Add LayerDataDecorator to scene node to indicate that the node has layer data decorator = LayerDataDecorator.LayerDataDecorator() decorator.setLayerData(layer_mesh) @@ -226,10 +230,6 @@ class ProcessSlicedLayersJob(Job): if self._progress_message: self._progress_message.setProgress(100) - view = Application.getInstance().getController().getActiveView() - if view.getPluginId() == "SimulationView": - view.resetLayerData() - if self._progress_message: self._progress_message.hide() From 288aba0ec4125bce1144903663c053e985596790 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Mon, 4 Dec 2017 09:49:03 +0100 Subject: [PATCH 06/62] CURA-4667 Fix simulation view render race. Change the place where the layer data is reset. --- plugins/CuraEngineBackend/ProcessSlicedLayersJob.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/plugins/CuraEngineBackend/ProcessSlicedLayersJob.py b/plugins/CuraEngineBackend/ProcessSlicedLayersJob.py index 7a43b43fd0..26a8269183 100644 --- a/plugins/CuraEngineBackend/ProcessSlicedLayersJob.py +++ b/plugins/CuraEngineBackend/ProcessSlicedLayersJob.py @@ -61,7 +61,9 @@ class ProcessSlicedLayersJob(Job): def run(self): start_time = time() - if Application.getInstance().getController().getActiveView().getPluginId() == "SimulationView": + view = Application.getInstance().getController().getActiveView() + if view.getPluginId() == "SimulationView": + view.resetLayerData() self._progress_message.show() Job.yieldThread() if self._abort_requested: @@ -208,10 +210,6 @@ class ProcessSlicedLayersJob(Job): self._progress_message.hide() return - view = Application.getInstance().getController().getActiveView() - if view.getPluginId() == "SimulationView": - view.resetLayerData() - # Add LayerDataDecorator to scene node to indicate that the node has layer data decorator = LayerDataDecorator.LayerDataDecorator() decorator.setLayerData(layer_mesh) From d69e397716120d542a74734f6c7ec100ac663beb Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Mon, 4 Dec 2017 10:31:21 +0100 Subject: [PATCH 07/62] Add new plugins to ignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index f67add62cf..7284597fa9 100644 --- a/.gitignore +++ b/.gitignore @@ -45,6 +45,7 @@ plugins/cura-big-flame-graph plugins/cura-siemensnx-plugin plugins/CuraVariSlicePlugin plugins/CuraLiveScriptingPlugin +plugins/CuraPrintProfileCreator #Build stuff CMakeCache.txt From 1934bdfb99ad6c407f360f12221e710a0edb524f Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Mon, 4 Dec 2017 10:52:40 +0100 Subject: [PATCH 08/62] Add sidebar view object --- cura/SidebarView.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 cura/SidebarView.py diff --git a/cura/SidebarView.py b/cura/SidebarView.py new file mode 100644 index 0000000000..f5a9caba94 --- /dev/null +++ b/cura/SidebarView.py @@ -0,0 +1,13 @@ +# Copyright (c) 2017 Ultimaker B.V. + +from UM.PluginObject import PluginObject + + +# Abstract class for sidebar view objects. +# By default the sidebar is Cura's settings, slicing and printing overview. +# The last plugin to claim the sidebar QML target will be displayed. +class SidebarView(PluginObject): + + def __init__(self): + super().__init__() + print("sidebar view hello") From db2c3525c48411a9611dff17d334728556c9ca27 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Mon, 4 Dec 2017 11:06:10 +0100 Subject: [PATCH 09/62] Sidebar view and controller scaffolding --- cura/Sidebar/SidebarController.py | 48 +++++++++++++++++++++++++++++++ cura/{ => Sidebar}/SidebarView.py | 0 cura/Sidebar/__init__.py | 0 3 files changed, 48 insertions(+) create mode 100644 cura/Sidebar/SidebarController.py rename cura/{ => Sidebar}/SidebarView.py (100%) create mode 100644 cura/Sidebar/__init__.py diff --git a/cura/Sidebar/SidebarController.py b/cura/Sidebar/SidebarController.py new file mode 100644 index 0000000000..79e8d55c56 --- /dev/null +++ b/cura/Sidebar/SidebarController.py @@ -0,0 +1,48 @@ +# Copyright (c) 2017 Ultimaker B.V. +from UM.Logger import Logger +from UM.PluginRegistry import PluginRegistry +from UM.Signal import Signal +from .SidebarView import SidebarView +from typing import Optional + +# The sidebar controller manages available sidebar components and decides which one to display. +# The cura.qml file uses this controller to repeat over the sidebars and show the active index. +class SidebarController: + + def __init__(self, application): + self._application = application + self._sidebar_views = {} + self._active_sidebar_view = None + + PluginRegistry.addType("sidebar_view", self.addSidebarView) + + ## Emitted when the list of views changes. + sidebarViewsChanged = Signal() + + ## Emitted when the active view changes. + activeSidebarViewChanged = Signal() + + ## Get the active application instance. + def getApplication(self): + return self._application + + ## Add a sidebar view to the registry. + # It get's a unique name based on the plugin ID. + def addSidebarView(self, sidebar_view: SidebarView): + name = sidebar_view.getPluginId() + if name not in self._sidebar_views: + self._sidebar_views[name] = sidebar_view + self.sidebarViewsChanged.emit() + + ## Get a registered sidebar view by name. + # The name is the ID of the plugin that registered the view. + def getSidebarView(self, name: str) -> Optional[SidebarView]: + try: + return self._sidebar_views[name] + except KeyError: + Logger.log("e", "Unable to find %s in sidebar view list", name) + return None + + ## Change the active sidebar view to one of the registered views. + def setActiveSidebarView(self, name: str): + print("setting active sidebar view") diff --git a/cura/SidebarView.py b/cura/Sidebar/SidebarView.py similarity index 100% rename from cura/SidebarView.py rename to cura/Sidebar/SidebarView.py diff --git a/cura/Sidebar/__init__.py b/cura/Sidebar/__init__.py new file mode 100644 index 0000000000..e69de29bb2 From 0d9f49413e72c116f21969d970fe3a1875db9a4b Mon Sep 17 00:00:00 2001 From: Andrew Donaldson Date: Mon, 4 Dec 2017 21:16:30 +1100 Subject: [PATCH 10/62] Add Fedora to test for nvidia driver work around. --- cura_app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cura_app.py b/cura_app.py index d725bc1200..313b161a7d 100755 --- a/cura_app.py +++ b/cura_app.py @@ -12,7 +12,7 @@ from UM.Platform import Platform #WORKAROUND: GITHUB-88 GITHUB-385 GITHUB-612 if Platform.isLinux(): # Needed for platform.linux_distribution, which is not available on Windows and OSX # For Ubuntu: https://bugs.launchpad.net/ubuntu/+source/python-qt4/+bug/941826 - if platform.linux_distribution()[0] in ("debian", "Ubuntu", "LinuxMint"): # TODO: Needs a "if X11_GFX == 'nvidia'" here. The workaround is only needed on Ubuntu+NVidia drivers. Other drivers are not affected, but fine with this fix. + if platform.linux_distribution()[0] in ("debian", "Ubuntu", "LinuxMint", "Fedora"): # TODO: Needs a "if X11_GFX == 'nvidia'" here. The workaround is only needed on Ubuntu+NVidia drivers. Other drivers are not affected, but fine with this fix. import ctypes from ctypes.util import find_library libGL = find_library("GL") From b453e0a4e78fb18d4e41f873b787e0e995df9755 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Mon, 4 Dec 2017 11:27:37 +0100 Subject: [PATCH 11/62] Allow minimum layer time to be higher than the fan speed min/max threshold Previously that wasn't possible because the division that calculated the slope of the fan speed ramping up towards the minimum layer time couldn't deal with negative values. Now it deals with that properly and in the limit the regular fan speed will be used. Fixes #2328 and CURA-4272. --- resources/definitions/fdmprinter.def.json | 1 - 1 file changed, 1 deletion(-) diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json index 484e857aef..ed8bf59b97 100644 --- a/resources/definitions/fdmprinter.def.json +++ b/resources/definitions/fdmprinter.def.json @@ -3379,7 +3379,6 @@ "unit": "s", "type": "float", "default_value": 10, - "minimum_value": "cool_min_layer_time", "maximum_value_warning": "600", "settable_per_mesh": false, "settable_per_extruder": true From f2b4cbe182fe01fdd974958f28e35f37952a1b28 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Mon, 4 Dec 2017 11:37:49 +0100 Subject: [PATCH 12/62] Register sidebar controller in application, start with default sidebar view --- cura/CuraApplication.py | 19 +++++++++++++++++++ cura/Settings/SettingsSidebarView.py | 10 ++++++++++ cura/Sidebar/SidebarViewModel.py | 16 ++++++++++++++++ 3 files changed, 45 insertions(+) create mode 100644 cura/Settings/SettingsSidebarView.py create mode 100644 cura/Sidebar/SidebarViewModel.py diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 9427e15552..7f8fef05ad 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -33,9 +33,11 @@ from UM.Operations.RemoveSceneNodeOperation import RemoveSceneNodeOperation from UM.Operations.GroupedOperation import GroupedOperation from UM.Operations.SetTransformOperation import SetTransformOperation from cura.Arrange import Arrange +from cura.Settings.SettingsSidebarView import SettingsSidebarView from cura.ShapeArray import ShapeArray from cura.ConvexHullDecorator import ConvexHullDecorator from cura.SetParentOperation import SetParentOperation +from cura.Sidebar.SidebarController import SidebarController from cura.SliceableObjectDecorator import SliceableObjectDecorator from cura.BlockSlicingDecorator import BlockSlicingDecorator @@ -204,6 +206,14 @@ class CuraApplication(QtApplication): self._setting_inheritance_manager = None self._simple_mode_settings_manager = None + ## As of Cura 3.2, the sidebar is controlled by a controller. + # This functionality was added to allow plugins registering custom sidebar views. + self._sidebar_controller = SidebarController(self) + + ## Register the default settings sidebar manually + settings_sidebar_view = SettingsSidebarView() + self._sidebar_controller.addSidebarView(settings_sidebar_view) + self._additional_components = {} # Components to add to certain areas in the interface super().__init__(name = "cura", version = CuraVersion, buildtype = CuraBuildType, @@ -775,6 +785,14 @@ class CuraApplication(QtApplication): def getPrintInformation(self): return self._print_information + ## Get the SidebarController of this application. + # A sidebar controller is created if it wasn't yet. + # \returns SidebarControllers \type{SidebarController} + def getSidebarController(self) -> SidebarController: + if self._sidebar_controller is None: + self._sidebar_controller = SidebarController(self) + return self._sidebar_controller + ## Registers objects for the QML engine to use. # # \param engine The QML engine. @@ -800,6 +818,7 @@ class CuraApplication(QtApplication): qmlRegisterType(MachineNameValidator, "Cura", 1, 0, "MachineNameValidator") qmlRegisterType(UserChangesModel, "Cura", 1, 1, "UserChangesModel") qmlRegisterSingletonType(ContainerManager, "Cura", 1, 0, "ContainerManager", ContainerManager.createContainerManager) + qmlRegisterSingletonType(SidebarController, "Cura", 1, 0, "SidebarController", self.getSidebarController) # As of Qt5.7, it is necessary to get rid of any ".." in the path for the singleton to work. actions_url = QUrl.fromLocalFile(os.path.abspath(Resources.getPath(CuraApplication.ResourceTypes.QmlFiles, "Actions.qml"))) diff --git a/cura/Settings/SettingsSidebarView.py b/cura/Settings/SettingsSidebarView.py new file mode 100644 index 0000000000..813f3fef2f --- /dev/null +++ b/cura/Settings/SettingsSidebarView.py @@ -0,0 +1,10 @@ +# Copyright (c) 2017 Ultimaker B.V. + +from PyQt5.QtCore import QObject + +from cura.Sidebar.SidebarView import SidebarView + +class SettingsSidebarView(QObject, SidebarView): + + def __init__(self): + super().__init__() diff --git a/cura/Sidebar/SidebarViewModel.py b/cura/Sidebar/SidebarViewModel.py new file mode 100644 index 0000000000..49e64060bc --- /dev/null +++ b/cura/Sidebar/SidebarViewModel.py @@ -0,0 +1,16 @@ +# Copyright (c) 2017 Ultimaker B.V. +from PyQt5.QtCore import Qt +from UM.Qt.ListModel import ListModel +from UM.Application import Application +from UM.PluginRegistry import PluginRegistry + +## The SidebarViewModel is the default sidebar view in Cura with all the print settings and print button. +class SidebarViewModel(ListModel): + IdRole = Qt.UserRole + 1 + NameRole = Qt.UserRole + 2 + ActiveRole = Qt.UserRole + 3 + + def __init__(self, parent = None): + super().__init__(parent) + + \ No newline at end of file From 713055e320266d2bfa456e545c475cf8ba4849c7 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Mon, 4 Dec 2017 12:37:29 +0100 Subject: [PATCH 13/62] Get meta data per sidebar view in sidebar view model --- cura/Settings/SettingsSidebarView.py | 13 +++++++-- cura/Sidebar/SidebarController.py | 6 ++++- cura/Sidebar/SidebarViewModel.py | 40 +++++++++++++++++++++++++++- 3 files changed, 55 insertions(+), 4 deletions(-) diff --git a/cura/Settings/SettingsSidebarView.py b/cura/Settings/SettingsSidebarView.py index 813f3fef2f..abd3968ca9 100644 --- a/cura/Settings/SettingsSidebarView.py +++ b/cura/Settings/SettingsSidebarView.py @@ -1,10 +1,19 @@ # Copyright (c) 2017 Ultimaker B.V. - from PyQt5.QtCore import QObject - +from UM.i18n import i18nCatalog from cura.Sidebar.SidebarView import SidebarView +i18n_catalog = i18nCatalog("cura") class SettingsSidebarView(QObject, SidebarView): def __init__(self): super().__init__() + + ## As the default sidebar is not a plugin, we have a get meta data method here to allow the sidebar view model to get the needed data. + def getMetaData(self): + return { + "sidebar_view": { + "name": i18n_catalog.i18nc("", "Print settings"), + "weight": 1 + } + } diff --git a/cura/Sidebar/SidebarController.py b/cura/Sidebar/SidebarController.py index 79e8d55c56..cb0e4a1e8e 100644 --- a/cura/Sidebar/SidebarController.py +++ b/cura/Sidebar/SidebarController.py @@ -3,7 +3,7 @@ from UM.Logger import Logger from UM.PluginRegistry import PluginRegistry from UM.Signal import Signal from .SidebarView import SidebarView -from typing import Optional +from typing import Optional, Dict # The sidebar controller manages available sidebar components and decides which one to display. # The cura.qml file uses this controller to repeat over the sidebars and show the active index. @@ -46,3 +46,7 @@ class SidebarController: ## Change the active sidebar view to one of the registered views. def setActiveSidebarView(self, name: str): print("setting active sidebar view") + + ## Get all sidebar views registered in this controller. + def getAllSidebarViews(self) -> Dict[SidebarView]: + return self._sidebar_views diff --git a/cura/Sidebar/SidebarViewModel.py b/cura/Sidebar/SidebarViewModel.py index 49e64060bc..638800247d 100644 --- a/cura/Sidebar/SidebarViewModel.py +++ b/cura/Sidebar/SidebarViewModel.py @@ -13,4 +13,42 @@ class SidebarViewModel(ListModel): def __init__(self, parent = None): super().__init__(parent) - \ No newline at end of file + self._controller = Application.getInstance().getSidebarController() + + # register Qt list roles + self.addRoleName(self.IdRole, "id") + self.addRoleName(self.NameRole, "name") + self.addRoleName(self.ActiveRole, "active") + + ## Update the model when new views are added or another view is made the active view. + def _onSidebarViewsChanged(self): + items = [] + sidebar_views = self._controller.getAllSidebarViews() + current_view = self._controller.getActiveSidebarView() + + for sidebar_view_id, sidebar_view in sidebar_views.items(): + plugin_metadata = PluginRegistry.getInstance().getMetaData(sidebar_view_id) + if plugin_metadata: + # Check if the registered view came from a plugin and extract the metadata if so. + sidebar_view_metadata = plugin_metadata.get("sidebar_view", {}) + else: + # Get the meta data directly from the plugin + sidebar_view_metadata = sidebar_view.getMetaData() + + # Skip view modes that are marked as not visible + if "visible" in sidebar_view_metadata and not sidebar_view_metadata["visible"]: + continue + + name = sidebar_view_metadata.get("name", id) + weight = sidebar_view_metadata.get("weight", 0) + + items.append({ + "id": sidebar_view_id, + "name": name, + "active": sidebar_view_id == current_view.getPluginId(), + "weight": weight + }) + + # Sort the views by weight + items.sort(key=lambda t: t["weight"]) + self.setItems(items) From 4ef39ca31374ee77670d33a0096b57f473951210 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Mon, 4 Dec 2017 12:47:08 +0100 Subject: [PATCH 14/62] Add sidebar views menu item and expose model to QML --- cura/CuraApplication.py | 2 ++ cura/Settings/SettingsSidebarView.py | 4 ++++ cura/Sidebar/SidebarController.py | 1 + cura/Sidebar/SidebarViewModel.py | 5 ++++- resources/qml/Menus/ViewMenu.qml | 33 ++++++++++++++++++++++------ 5 files changed, 37 insertions(+), 8 deletions(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 7f8fef05ad..a2efa887c0 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -38,6 +38,7 @@ from cura.ShapeArray import ShapeArray from cura.ConvexHullDecorator import ConvexHullDecorator from cura.SetParentOperation import SetParentOperation from cura.Sidebar.SidebarController import SidebarController +from cura.Sidebar.SidebarViewModel import SidebarViewModel from cura.SliceableObjectDecorator import SliceableObjectDecorator from cura.BlockSlicingDecorator import BlockSlicingDecorator @@ -819,6 +820,7 @@ class CuraApplication(QtApplication): qmlRegisterType(UserChangesModel, "Cura", 1, 1, "UserChangesModel") qmlRegisterSingletonType(ContainerManager, "Cura", 1, 0, "ContainerManager", ContainerManager.createContainerManager) qmlRegisterSingletonType(SidebarController, "Cura", 1, 0, "SidebarController", self.getSidebarController) + qmlRegisterType(SidebarViewModel, "Cura", 1, 0, "SidebarViewModel") # As of Qt5.7, it is necessary to get rid of any ".." in the path for the singleton to work. actions_url = QUrl.fromLocalFile(os.path.abspath(Resources.getPath(CuraApplication.ResourceTypes.QmlFiles, "Actions.qml"))) diff --git a/cura/Settings/SettingsSidebarView.py b/cura/Settings/SettingsSidebarView.py index abd3968ca9..648e056044 100644 --- a/cura/Settings/SettingsSidebarView.py +++ b/cura/Settings/SettingsSidebarView.py @@ -9,6 +9,10 @@ class SettingsSidebarView(QObject, SidebarView): def __init__(self): super().__init__() + ## As the default sidebar is not a plugin, we have a get plugin ID method to allow the sidebar view model to get the needed data. + def getPluginId(self): + return "default" + ## As the default sidebar is not a plugin, we have a get meta data method here to allow the sidebar view model to get the needed data. def getMetaData(self): return { diff --git a/cura/Sidebar/SidebarController.py b/cura/Sidebar/SidebarController.py index cb0e4a1e8e..1212091b2d 100644 --- a/cura/Sidebar/SidebarController.py +++ b/cura/Sidebar/SidebarController.py @@ -46,6 +46,7 @@ class SidebarController: ## Change the active sidebar view to one of the registered views. def setActiveSidebarView(self, name: str): print("setting active sidebar view") + self.activeSidebarViewChanged.emit() ## Get all sidebar views registered in this controller. def getAllSidebarViews(self) -> Dict[SidebarView]: diff --git a/cura/Sidebar/SidebarViewModel.py b/cura/Sidebar/SidebarViewModel.py index 638800247d..4ed1ca60cb 100644 --- a/cura/Sidebar/SidebarViewModel.py +++ b/cura/Sidebar/SidebarViewModel.py @@ -13,7 +13,10 @@ class SidebarViewModel(ListModel): def __init__(self, parent = None): super().__init__(parent) + # connect views changed signals self._controller = Application.getInstance().getSidebarController() + self._controller.sidebarViewsChanged.connect(self._onSidebarViewsChanged) + self._controller.activeSidebarViewChanged.connect(self._onSidebarViewsChanged) # register Qt list roles self.addRoleName(self.IdRole, "id") @@ -39,7 +42,7 @@ class SidebarViewModel(ListModel): if "visible" in sidebar_view_metadata and not sidebar_view_metadata["visible"]: continue - name = sidebar_view_metadata.get("name", id) + name = sidebar_view_metadata.get("name", sidebar_view_id) weight = sidebar_view_metadata.get("weight", 0) items.append({ diff --git a/resources/qml/Menus/ViewMenu.qml b/resources/qml/Menus/ViewMenu.qml index bb5999edb9..654de3391a 100644 --- a/resources/qml/Menus/ViewMenu.qml +++ b/resources/qml/Menus/ViewMenu.qml @@ -12,21 +12,40 @@ Menu title: catalog.i18nc("@title:menu menubar:toplevel", "&View"); id: menu enabled: !PrintInformation.preSliced + + // main views Instantiator { - model: UM.ViewModel { } + model: UM.ViewModel{} MenuItem { - text: model.name; - checkable: true; - checked: model.active; - exclusiveGroup: group; - onTriggered: UM.Controller.setActiveView(model.id); + text: model.name + checkable: true + checked: model.active + exclusiveGroup: group + onTriggered: UM.Controller.setActiveView(model.id) } onObjectAdded: menu.insertItem(index, object) onObjectRemoved: menu.removeItem(object) } - ExclusiveGroup { id: group; } + ExclusiveGroup { id: group } + + // sidebar views + Instantiator + { + model: Cura.SidebarViewModel{} + MenuItem + { + text: model.name + checkable: true + checked: model.active + exclusiveGroup: sidebarGroup + onTriggered: Cura.SidebarController.setActiveSidebarView(model.id) + } + onObjectAdded: menu.insertItem(index, object) + onObjectRemoved: menu.removeItem(object) + } + ExclusiveGroup { id: sidebarGroup } MenuSeparator {} MenuItem { action: Cura.Actions.homeCamera; } From e8f1073af85c8a5fad8d80896f1954c5b9809144 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Mon, 4 Dec 2017 12:48:07 +0100 Subject: [PATCH 15/62] Add menu seperator --- resources/qml/Menus/ViewMenu.qml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/resources/qml/Menus/ViewMenu.qml b/resources/qml/Menus/ViewMenu.qml index 654de3391a..b5e4c5765f 100644 --- a/resources/qml/Menus/ViewMenu.qml +++ b/resources/qml/Menus/ViewMenu.qml @@ -30,6 +30,8 @@ Menu } ExclusiveGroup { id: group } + MenuSeparator {} + // sidebar views Instantiator { From 5eeb98bbcf41011bc9f49591aa4727e75803f511 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Mon, 4 Dec 2017 13:20:16 +0100 Subject: [PATCH 16/62] Move sidebar controller init --- cura/CuraApplication.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index a2efa887c0..6f5746b49d 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -207,14 +207,6 @@ class CuraApplication(QtApplication): self._setting_inheritance_manager = None self._simple_mode_settings_manager = None - ## As of Cura 3.2, the sidebar is controlled by a controller. - # This functionality was added to allow plugins registering custom sidebar views. - self._sidebar_controller = SidebarController(self) - - ## Register the default settings sidebar manually - settings_sidebar_view = SettingsSidebarView() - self._sidebar_controller.addSidebarView(settings_sidebar_view) - self._additional_components = {} # Components to add to certain areas in the interface super().__init__(name = "cura", version = CuraVersion, buildtype = CuraBuildType, @@ -225,6 +217,14 @@ class CuraApplication(QtApplication): self.setWindowIcon(QIcon(Resources.getPath(Resources.Images, "cura-icon.png"))) + ## As of Cura 3.2, the sidebar is controlled by a controller. + # This functionality was added to allow plugins registering custom sidebar views. + self._sidebar_controller = SidebarController(self) + + ## Register the default settings sidebar manually + settings_sidebar_view = SettingsSidebarView() + self._sidebar_controller.addSidebarView(settings_sidebar_view) + self.setRequiredPlugins([ "CuraEngineBackend", "UserAgreement", From 6315cbae4e2cf83bdccf5cacb29d0b48d949d409 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A1udio=20Sampaio=20=28Patola=29?= Date: Mon, 4 Dec 2017 12:48:19 -0200 Subject: [PATCH 17/62] updated brazilian portuguese translations --- resources/i18n/pt_BR/cura.po | 172 ++++++++++--------- resources/i18n/pt_BR/fdmextruder.def.json.po | 2 +- resources/i18n/pt_BR/fdmprinter.def.json.po | 58 ++++--- 3 files changed, 120 insertions(+), 112 deletions(-) diff --git a/resources/i18n/pt_BR/cura.po b/resources/i18n/pt_BR/cura.po index 82cbda8362..175c00c2e0 100644 --- a/resources/i18n/pt_BR/cura.po +++ b/resources/i18n/pt_BR/cura.po @@ -8,7 +8,7 @@ msgstr "" "Project-Id-Version: Cura 3.0\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2017-11-21 16:58+0100\n" -"PO-Revision-Date: 2017-10-05 12:20-0300\n" +"PO-Revision-Date: 2017-12-04 10:20-0300\n" "Last-Translator: Cláudio Sampaio \n" "Language-Team: Cláudio Sampaio and CoderSquirrel \n" "Language: pt_BR\n" @@ -92,7 +92,7 @@ msgstr "Arquivo enviado ao Doodle3D Connect" #: /home/ruben/Projects/Cura/plugins/Doodle3D-cura-plugin/Doodle3D/D3DCloudPrintOutputDevicePlugin.py:214 msgctxt "@action:button" msgid "Open Connect..." -msgstr "" +msgstr "Abrir Connect..." #: /home/ruben/Projects/Cura/plugins/Doodle3D-cura-plugin/Doodle3D/D3DCloudPrintOutputDevicePlugin.py:214 msgctxt "@info:tooltip" @@ -142,7 +142,7 @@ msgstr "Incapaz de iniciar novo trabalho porque a impressora está ocupada ou n #: /home/ruben/Projects/Cura/plugins/USBPrinting/USBPrinterOutputDevice.py:154 msgctxt "@info:title" msgid "Printer Unavailable" -msgstr "" +msgstr "Impressora Não Disponível" #: /home/ruben/Projects/Cura/plugins/USBPrinting/USBPrinterOutputDevice.py:457 msgctxt "@info:status" @@ -594,7 +594,7 @@ msgstr "Conectar pela rede" #, python-brace-format msgctxt "@info Don't translate {machine_name}, since it gets replaced by a printer name!" msgid "New features are available for your {machine_name}! It is recommended to update the firmware on your printer." -msgstr "" +msgstr "Novos recursos estão disponível para sua {machine_name}! Recomenda-se atualizar o firmware da impressora." #: /home/ruben/Projects/Cura/plugins/FirmwareUpdateChecker/FirmwareUpdateCheckerJob.py:65 #, python-format @@ -605,7 +605,7 @@ msgstr "Novo firmware de %s disponível" #: /home/ruben/Projects/Cura/plugins/FirmwareUpdateChecker/FirmwareUpdateCheckerJob.py:66 msgctxt "@action:button" msgid "How to update" -msgstr "" +msgstr "Como atualizar" #: /home/ruben/Projects/Cura/plugins/FirmwareUpdateChecker/FirmwareUpdateCheckerJob.py:77 msgctxt "@info" @@ -640,7 +640,7 @@ msgstr "Erro ao iniciar %s!" #: /home/ruben/Projects/Cura/plugins/SimulationView/__init__.py:14 msgctxt "@item:inlistbox" msgid "Simulation view" -msgstr "" +msgstr "Visão simulada" #: /home/ruben/Projects/Cura/plugins/SimulationView/SimulationView.py:100 msgctxt "@info:status" @@ -650,7 +650,7 @@ msgstr "O Cura não mostra as camadas corretamente quando Impressão em Arame es #: /home/ruben/Projects/Cura/plugins/SimulationView/SimulationView.py:101 msgctxt "@info:title" msgid "Simulation View" -msgstr "" +msgstr "Visão Simulada" #: /home/ruben/Projects/Cura/plugins/PostProcessingPlugin/PostProcessingPlugin.py:26 msgid "Modify G-Code" @@ -731,7 +731,7 @@ msgstr "Incapaz de fatiar com os ajustes atuais. Os seguintes ajustes têm erros #, python-brace-format msgctxt "@info:status" msgid "Unable to slice due to some per-model settings. The following settings have errors on one or more models: {error_labels}" -msgstr "" +msgstr "Incapaz de fatiar devido a alguns ajustes por modelo. Os seguintes ajustes têm erros em um ou mais dos modelos: {error_labels}" #: /home/ruben/Projects/Cura/plugins/CuraEngineBackend/CuraEngineBackend.py:326 msgctxt "@info:status" @@ -766,25 +766,25 @@ msgstr "Configurar ajustes por Modelo" #: /home/ruben/Projects/Cura/plugins/cura-siemensnx-plugin/Installer.py:23 msgid "Install" -msgstr "" +msgstr "Instalar" #: /home/ruben/Projects/Cura/plugins/cura-siemensnx-plugin/Installer.py:43 msgid "Failed to copy Siemens NX plugins files. Please check your UGII_USER_DIR. It is not set to a directory." -msgstr "" +msgstr "Erro ao copiar arquivos de plugins Siemens NX. Por favor verifique seu UGII_USER_DIR. Ele não está configurado para um diretório." #: /home/ruben/Projects/Cura/plugins/cura-siemensnx-plugin/Installer.py:50 #: /home/ruben/Projects/Cura/plugins/cura-siemensnx-plugin/Installer.py:59 #: /home/ruben/Projects/Cura/plugins/cura-siemensnx-plugin/Installer.py:81 msgid "Successfully installed Siemens NX Cura plugin." -msgstr "" +msgstr "Plugin Siemens NX do Cura instalado com sucesso." #: /home/ruben/Projects/Cura/plugins/cura-siemensnx-plugin/Installer.py:65 msgid "Failed to copy Siemens NX plugins files. Please check your UGII_USER_DIR." -msgstr "" +msgstr "Erro ao copiar arquivos de plugins Siemens NX. Por favor, verifique seu UGII_USER_DIR." #: /home/ruben/Projects/Cura/plugins/cura-siemensnx-plugin/Installer.py:85 msgid "Failed to install Siemens NX plugin. Could not set environment variable UGII_USER_DIR for Siemens NX." -msgstr "" +msgstr "Erro ao instalar arquivos de plugins Siemens NX. Não foi possível ajustar a variável de ambiente UGII_USER_DIR para o Simenes NX." #: /home/ruben/Projects/Cura/plugins/3MFReader/WorkspaceDialog.py:167 #: /home/ruben/Projects/Cura/resources/qml/Sidebar.qml:585 @@ -947,7 +947,7 @@ msgstr "Outros" #: /home/ruben/Projects/Cura/cura/PrintInformation.py:199 msgctxt "@label unknown material" msgid "Unknown" -msgstr "" +msgstr "Desconhecido" #: /home/ruben/Projects/Cura/cura/PrintInformation.py:284 #, python-brace-format @@ -1012,12 +1012,12 @@ msgstr "Material Personalizado" #: /home/ruben/Projects/Cura/cura/Settings/ExtrudersModel.py:182 msgctxt "@menuitem" msgid "Global" -msgstr "" +msgstr "Global" #: /home/ruben/Projects/Cura/cura/Settings/ExtrudersModel.py:229 msgctxt "@menuitem" msgid "Not overridden" -msgstr "" +msgstr "Não sobrepujado" #: /home/ruben/Projects/Cura/cura/Settings/MachineManager.py:117 msgctxt "@info:status" @@ -1066,7 +1066,7 @@ msgstr "Perfil exportado para {0}" #: /home/ruben/Projects/Cura/cura/Settings/CuraContainerRegistry.py:157 msgctxt "@info:title" msgid "Export succeeded" -msgstr "" +msgstr "Exportação concluída" #: /home/ruben/Projects/Cura/cura/Settings/CuraContainerRegistry.py:183 #: /home/ruben/Projects/Cura/cura/Settings/CuraContainerRegistry.py:205 @@ -1139,84 +1139,87 @@ msgid "" "

Please use the \"Send report\" button to post a bug report automatically to our servers

\n" " " msgstr "" +"

Uma exceção fatal aocorreu. Por favor nos envie este Relatório de Erros para consertarmos o problema

\n" +"

Por favor use o botão \"Enviar relatório\" para postar um relato de bug automaticamente em nossos servidores

\n" +" " #: /home/ruben/Projects/Cura/cura/CrashHandler.py:101 msgctxt "@title:groupbox" msgid "System information" -msgstr "" +msgstr "Informação do Sistema" #: /home/ruben/Projects/Cura/cura/CrashHandler.py:109 msgctxt "@label unknown version of Cura" msgid "Unknown" -msgstr "" +msgstr "Desconhecida" #: /home/ruben/Projects/Cura/cura/CrashHandler.py:111 #, python-brace-format msgctxt "@label Cura version" msgid "Cura version: {version}
" -msgstr "" +msgstr "Versão do Cura: {version}
" #: /home/ruben/Projects/Cura/cura/CrashHandler.py:112 #, python-brace-format msgctxt "@label Platform" msgid "Platform: {platform}
" -msgstr "" +msgstr "Plataforma: {platform}
" #: /home/ruben/Projects/Cura/cura/CrashHandler.py:113 #, python-brace-format msgctxt "@label Qt version" msgid "Qt version: {qt}
" -msgstr "" +msgstr "Versão da Qt: {qt}
" #: /home/ruben/Projects/Cura/cura/CrashHandler.py:114 #, python-brace-format msgctxt "@label PyQt version" msgid "PyQt version: {pyqt}
" -msgstr "" +msgstr "Versão da PyQt: {pyqt}
" #: /home/ruben/Projects/Cura/cura/CrashHandler.py:115 #, python-brace-format msgctxt "@label OpenGL" msgid "OpenGL: {opengl}
" -msgstr "" +msgstr "OpenGL: {opengl}
" #: /home/ruben/Projects/Cura/cura/CrashHandler.py:130 #, python-brace-format msgctxt "@label OpenGL version" msgid "
  • OpenGL Version: {version}
  • " -msgstr "" +msgstr "
  • Versão da OpenGL: {version}
  • " #: /home/ruben/Projects/Cura/cura/CrashHandler.py:131 #, python-brace-format msgctxt "@label OpenGL vendor" msgid "
  • OpenGL Vendor: {vendor}
  • " -msgstr "" +msgstr "
  • Fornecedor da OpenGL: {vendor}
  • " #: /home/ruben/Projects/Cura/cura/CrashHandler.py:132 #, python-brace-format msgctxt "@label OpenGL renderer" msgid "
  • OpenGL Renderer: {renderer}
  • " -msgstr "" +msgstr "
  • Renderizador da OpenGL: {renderer}
  • " #: /home/ruben/Projects/Cura/cura/CrashHandler.py:141 msgctxt "@title:groupbox" msgid "Exception traceback" -msgstr "" +msgstr "Traceback de exceção" #: /home/ruben/Projects/Cura/cura/CrashHandler.py:208 msgctxt "@title:groupbox" msgid "Logs" -msgstr "" +msgstr "Registros" #: /home/ruben/Projects/Cura/cura/CrashHandler.py:231 msgctxt "@title:groupbox" msgid "User description" -msgstr "" +msgstr "Descrição do usuário" #: /home/ruben/Projects/Cura/cura/CrashHandler.py:246 msgctxt "@action:button" msgid "Send report" -msgstr "" +msgstr "Enviar relatório" #: /home/ruben/Projects/Cura/cura/CuraApplication.py:256 msgctxt "@info:progress" @@ -1254,7 +1257,7 @@ msgstr "Não é possível abrir nenhum outro arquivo se G-Code estiver sendo car #: /home/ruben/Projects/Cura/cura/CuraApplication.py:1416 msgctxt "@info:status" msgid "The selected model was too small to load." -msgstr "" +msgstr "O modelo selecionado é pequenos demais para carregar." #: /home/ruben/Projects/Cura/plugins/MachineSettingsAction/MachineSettingsAction.qml:59 msgctxt "@title" @@ -1632,7 +1635,7 @@ msgstr "%1 não está configurada para hospedar um grupo de impressora Ultimaker #: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/OpenPanelButton.qml:14 msgctxt "@info:tooltip" msgid "Opens the print jobs page with your default web browser." -msgstr "" +msgstr "Abre a página de trabalhos de impressão com seu navegador default." #: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/OpenPanelButton.qml:15 #: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/ClusterControlItem.qml:131 @@ -1667,7 +1670,7 @@ msgstr "A conexão à impressora foi perdida" #: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/PrinterInfoBlock.qml:257 msgctxt "@label:status" msgid "Disabled" -msgstr "" +msgstr "Desabilitado" #: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/PrinterInfoBlock.qml:273 msgctxt "@label:status" @@ -1682,12 +1685,12 @@ msgstr "Finalizado" #: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/PrinterInfoBlock.qml:290 msgctxt "@label:status" msgid "Paused" -msgstr "" +msgstr "Pausado" #: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/PrinterInfoBlock.qml:292 msgctxt "@label:status" msgid "Resuming" -msgstr "" +msgstr "Continuando" #: /home/ruben/Projects/Cura/plugins/UM3NetworkPrinting/PrinterInfoBlock.qml:294 msgctxt "@label:status" @@ -1829,12 +1832,12 @@ msgstr "Tipo de Linha" #: /home/ruben/Projects/Cura/plugins/SimulationView/SimulationView.qml:104 msgctxt "@label:listbox" msgid "Feedrate" -msgstr "" +msgstr "Taxa de alimentação" #: /home/ruben/Projects/Cura/plugins/SimulationView/SimulationView.qml:108 msgctxt "@label:listbox" msgid "Layer thickness" -msgstr "" +msgstr "Largura de camada" #: /home/ruben/Projects/Cura/plugins/SimulationView/SimulationView.qml:148 msgctxt "@label" @@ -1884,12 +1887,12 @@ msgstr "Parede Interna" #: /home/ruben/Projects/Cura/plugins/SimulationView/SimulationView.qml:378 msgctxt "@label" msgid "min" -msgstr "" +msgstr "mín" #: /home/ruben/Projects/Cura/plugins/SimulationView/SimulationView.qml:420 msgctxt "@label" msgid "max" -msgstr "" +msgstr "máx" #: /home/ruben/Projects/Cura/plugins/PostProcessingPlugin/PostProcessingPlugin.qml:18 msgctxt "@title:window" @@ -2127,7 +2130,7 @@ msgstr "%1 de %2" #: /home/ruben/Projects/Cura/plugins/3MFReader/WorkspaceDialog.qml:368 msgctxt "@action:warning" msgid "Loading a project will clear all models on the build plate." -msgstr "" +msgstr "Carregar um projeto limpará todos os modelos da mesa de impressão." #: /home/ruben/Projects/Cura/plugins/3MFReader/WorkspaceDialog.qml:386 msgctxt "@action:button" @@ -2171,6 +2174,9 @@ msgid "" "You need to accept this license to install this plugin.\n" "Do you agree with the terms below?" msgstr "" +"Este plugin contém uma licença.\n" +"Você precisa aceitar esta licença para instalar este plugin.\n" +"Você concorda com os termos abaixo?" #: /home/ruben/Projects/Cura/plugins/PluginBrowser/PluginBrowser.qml:242 msgctxt "@action:button" @@ -2185,7 +2191,7 @@ msgstr "Recusar" #: /home/ruben/Projects/Cura/plugins/UserAgreementPlugin/UserAgreement.qml:16 msgctxt "@title:window" msgid "User Agreement" -msgstr "" +msgstr "Termos de Acordo do Usuário" #: /home/ruben/Projects/Cura/plugins/UltimakerMachineActions/UM2UpgradeSelectionMachineAction.qml:25 #: /home/ruben/Projects/Cura/plugins/UltimakerMachineActions/UMOUpgradeSelectionMachineAction.qml:25 @@ -3068,7 +3074,7 @@ msgstr "Sobre o Cura" #: /home/ruben/Projects/Cura/resources/qml/AboutDialog.qml:43 msgctxt "@label" msgid "version: %1" -msgstr "" +msgstr "versão: %1" #: /home/ruben/Projects/Cura/resources/qml/AboutDialog.qml:56 msgctxt "@label" @@ -3167,7 +3173,7 @@ msgstr "Biblioteca de recorte de polígonos" #: /home/ruben/Projects/Cura/resources/qml/AboutDialog.qml:135 msgctxt "@Label" msgid "Python HTTP library" -msgstr "" +msgstr "Biblioteca de HTTP Python" #: /home/ruben/Projects/Cura/resources/qml/AboutDialog.qml:137 msgctxt "@label" @@ -3187,7 +3193,7 @@ msgstr "Perfil:" #: /home/ruben/Projects/Cura/resources/qml/Settings/SettingView.qml:66 msgctxt "@" msgid "No Profile Available" -msgstr "" +msgstr "Nenhum Perfil Disponível" #: /home/ruben/Projects/Cura/resources/qml/Settings/SettingView.qml:104 msgctxt "@tooltip" @@ -3298,44 +3304,44 @@ msgstr "" #: /home/ruben/Projects/Cura/resources/qml/Sidebar.qml:336 msgctxt "@label Hours and minutes" msgid "00h 00min" -msgstr "" +msgstr "00h 00min" #: /home/ruben/Projects/Cura/resources/qml/Sidebar.qml:354 msgctxt "@tooltip" msgid "Time specification
    " -msgstr "" +msgstr "Especificação de tempo
    " #: /home/ruben/Projects/Cura/resources/qml/Sidebar.qml:429 msgctxt "@label" msgid "Cost specification" -msgstr "" +msgstr "Especificação de custo" #: /home/ruben/Projects/Cura/resources/qml/Sidebar.qml:434 #: /home/ruben/Projects/Cura/resources/qml/Sidebar.qml:445 msgctxt "@label m for meter" msgid "%1m" -msgstr "" +msgstr "%1m" #: /home/ruben/Projects/Cura/resources/qml/Sidebar.qml:435 #: /home/ruben/Projects/Cura/resources/qml/Sidebar.qml:446 msgctxt "@label g for grams" msgid "%1g" -msgstr "" +msgstr "%1g" #: /home/ruben/Projects/Cura/resources/qml/Sidebar.qml:444 msgctxt "@label" msgid "Total:" -msgstr "" +msgstr "Total:" #: /home/ruben/Projects/Cura/resources/qml/Sidebar.qml:498 msgctxt "@label Print estimates: m for meters, g for grams, %4 is currency and %3 is print cost" msgid "%1m / ~ %2g / ~ %4 %3" -msgstr "" +msgstr "%1m / ~ %2g / ~ %4 %3" #: /home/ruben/Projects/Cura/resources/qml/Sidebar.qml:503 msgctxt "@label Print estimates: m for meters, g for grams" msgid "%1m / ~ %2g" -msgstr "" +msgstr "%1m / ~ %2g" #: /home/ruben/Projects/Cura/resources/qml/Sidebar.qml:586 msgctxt "@tooltip" @@ -3460,27 +3466,27 @@ msgstr "Aquecer a mesa antes de imprimir. Você pode continuar ajustando sua imp #: /home/ruben/Projects/Cura/resources/qml/PrintMonitor.qml:703 msgctxt "@label" msgid "Printer control" -msgstr "" +msgstr "Controle da Impressora" #: /home/ruben/Projects/Cura/resources/qml/PrintMonitor.qml:717 msgctxt "@label" msgid "Jog Position" -msgstr "" +msgstr "Posição de Trote" #: /home/ruben/Projects/Cura/resources/qml/PrintMonitor.qml:735 msgctxt "@label" msgid "X/Y" -msgstr "" +msgstr "X/Y" #: /home/ruben/Projects/Cura/resources/qml/PrintMonitor.qml:842 msgctxt "@label" msgid "Z" -msgstr "" +msgstr "Z" #: /home/ruben/Projects/Cura/resources/qml/PrintMonitor.qml:907 msgctxt "@label" msgid "Jog Distance" -msgstr "" +msgstr "Distância de Trote" #: /home/ruben/Projects/Cura/resources/qml/PrintMonitor.qml:1018 msgctxt "@label" @@ -3525,7 +3531,7 @@ msgstr "&Sair" #: /home/ruben/Projects/Cura/resources/qml/Actions.qml:107 msgctxt "@action:inmenu menubar:view" msgid "&Reset camera position" -msgstr "" +msgstr "&Recompor posições de câmera" #: /home/ruben/Projects/Cura/resources/qml/Actions.qml:114 msgctxt "@action:inmenu" @@ -3767,7 +3773,7 @@ msgstr "Importar todos como modelos" #: /home/ruben/Projects/Cura/resources/qml/Cura.qml:19 msgctxt "@title:window" msgid "Ultimaker Cura" -msgstr "" +msgstr "Ultimaker Cura" #: /home/ruben/Projects/Cura/resources/qml/Cura.qml:81 msgctxt "@title:menu menubar:toplevel" @@ -3924,7 +3930,7 @@ msgstr "Altura de Camada" #: /home/ruben/Projects/Cura/resources/qml/SidebarSimple.qml:323 msgctxt "@tooltip" msgid "A custom profile is currently active. To enable the quality slider, choose a default quality profile in Custom tab" -msgstr "" +msgstr "Um perfil personalizado está atualmente ativo. Para habilitar o controle deslizante de qualidade, escolha um perfil de qualidade default na aba Personalizado" #: /home/ruben/Projects/Cura/resources/qml/SidebarSimple.qml:340 msgctxt "@label" @@ -3944,7 +3950,7 @@ msgstr "Mais Rápido" #: /home/ruben/Projects/Cura/resources/qml/SidebarSimple.qml:388 msgctxt "@tooltip" msgid "You have modified some profile settings. If you want to change these go to custom mode." -msgstr "" +msgstr "Você modificou alguns ajustes de perfil. Se você quiser alterá-los, use o modo personalizado." #: /home/ruben/Projects/Cura/resources/qml/SidebarSimple.qml:413 msgctxt "@label" @@ -4036,7 +4042,7 @@ msgstr "Material" #: /home/ruben/Projects/Cura/resources/qml/SidebarHeader.qml:349 msgctxt "@label" msgid "Check compatibility" -msgstr "" +msgstr "Verificar compatibilidade" #: /home/ruben/Projects/Cura/resources/qml/SidebarHeader.qml:369 msgctxt "@tooltip" @@ -4176,12 +4182,12 @@ msgstr "Integração ao SolidWorks" #: SimulationView/plugin.json msgctxt "description" msgid "Provides the Simulation view." -msgstr "" +msgstr "Provê a Visão Simulada." #: SimulationView/plugin.json msgctxt "name" msgid "Simulation View" -msgstr "" +msgstr "Visão Simulada" #: PostProcessingPlugin/plugin.json msgctxt "description" @@ -4266,12 +4272,12 @@ msgstr "Atualização de Versão de 2.7 para 3.0" #: VersionUpgrade/VersionUpgrade30to31/plugin.json msgctxt "description" msgid "Upgrades configurations from Cura 3.0 to Cura 3.1." -msgstr "" +msgstr "Atualiza configurações do Cura 3.0 para o Cura 3.1." #: VersionUpgrade/VersionUpgrade30to31/plugin.json msgctxt "name" msgid "Version Upgrade 3.0 to 3.1" -msgstr "" +msgstr "Atualização de Versão 3.0 para 3.1" #: VersionUpgrade/VersionUpgrade26to27/plugin.json msgctxt "description" @@ -4336,12 +4342,12 @@ msgstr "Ferramenta de Ajustes Por Modelo" #: cura-siemensnx-plugin/plugin.json msgctxt "description" msgid "Helps you to install an 'export to Cura' button in Siemens NX." -msgstr "" +msgstr "Auxilia na instalação de um botão 'exportar para o Cura' no Siemens NX." #: cura-siemensnx-plugin/plugin.json msgctxt "name" msgid "Siemens NX Integration" -msgstr "" +msgstr "Integração ao Siemens NX" #: 3MFReader/plugin.json msgctxt "description" @@ -4406,12 +4412,12 @@ msgstr "Gerador de 3MF" #: UserAgreementPlugin/plugin.json msgctxt "description" msgid "Ask the user once if he/she agrees with our license" -msgstr "" +msgstr "Pergunta ao usuário uma única vez sobre concordância com a licença" #: UserAgreementPlugin/plugin.json msgctxt "name" msgid "UserAgreement" -msgstr "" +msgstr "Acordo de Usuário" #: UltimakerMachineActions/plugin.json msgctxt "description" @@ -4453,13 +4459,13 @@ msgstr "Leitor de Perfis do Cura" #~ msgid "To ensure that your {machine_name} is equipped with the latest features it is recommended to update the firmware regularly. This can be done on the {machine_name} (when connected to the network) or via USB." #~ msgstr "Para assegurar que sua {machine_name} esteja equipada com os recursos mais recentes, é recomendado atualizar o firmware regularmente. Isto pode ser feito na {machine_name} (quando conectado pela rede) ou via USB." -msgctxt "@item:inlistbox" -msgid "Layer view" -msgstr "Visão de Camadas" +#~ msgctxt "@item:inlistbox" +#~ msgid "Layer view" +#~ msgstr "Visão de Camadas" -msgctxt "@info:title" -msgid "Layer View" -msgstr "Visão de Camadas" +#~ msgctxt "@info:title" +#~ msgid "Layer View" +#~ msgstr "Visão de Camadas" #~ msgctxt "@menuitem" #~ msgid "Browse plugins" @@ -4561,9 +4567,9 @@ msgstr "Visão de Camadas" #~ msgid "Provides the Layer view." #~ msgstr "Provê a visão de Camadas." -msgctxt "name" -msgid "Layer View" -msgstr "Visão de Camadas" +#~ msgctxt "name" +#~ msgid "Layer View" +#~ msgstr "Visão de Camadas" #~ msgctxt "@item:inlistbox" #~ msgid "X-Ray" @@ -4884,9 +4890,9 @@ msgstr "Visão de Camadas" #~ msgid "Provides support for importing profiles from g-code files." #~ msgstr "Provê suporte para importar perfis de arquivos G-Code." -msgctxt "@label" -msgid "Layer View" -msgstr "Visão de Camadas" +#~ msgctxt "@label" +#~ msgid "Layer View" +#~ msgstr "Visão de Camadas" #~ msgctxt "@info:whatsthis" #~ msgid "Provides the Layer view." diff --git a/resources/i18n/pt_BR/fdmextruder.def.json.po b/resources/i18n/pt_BR/fdmextruder.def.json.po index 816a6ccb4a..1f222f9847 100644 --- a/resources/i18n/pt_BR/fdmextruder.def.json.po +++ b/resources/i18n/pt_BR/fdmextruder.def.json.po @@ -8,7 +8,7 @@ msgstr "" "Project-Id-Version: Cura 3.0\n" "Report-Msgid-Bugs-To: http://github.com/ultimaker/uranium\n" "POT-Creation-Date: 2017-11-21 16:58+0000\n" -"PO-Revision-Date: 2017-10-06 08:00-0300\n" +"PO-Revision-Date: 2017-12-04 09:00-0300\n" "Last-Translator: Cláudio Sampaio \n" "Language-Team: Cláudio Sampaio and CoderSquirrel \n" "Language: pt_BR\n" diff --git a/resources/i18n/pt_BR/fdmprinter.def.json.po b/resources/i18n/pt_BR/fdmprinter.def.json.po index d0e806e768..4aeea1d2f9 100644 --- a/resources/i18n/pt_BR/fdmprinter.def.json.po +++ b/resources/i18n/pt_BR/fdmprinter.def.json.po @@ -8,7 +8,7 @@ msgstr "" "Project-Id-Version: Cura 3.0\n" "Report-Msgid-Bugs-To: http://github.com/ultimaker/uranium\n" "POT-Creation-Date: 2017-11-21 16:58+0000\n" -"PO-Revision-Date: 2017-10-06 10:00-0300\n" +"PO-Revision-Date: 2017-12-04 10:20-0300\n" "Last-Translator: Cláudio Sampaio \n" "Language-Team: Cláudio Sampaio and CoderSquirrel \n" "Language: pt_BR\n" @@ -613,27 +613,27 @@ msgstr "A altura da camada inicial em mm. Uma camada inicial mais espessa faz a #: fdmprinter.def.json msgctxt "slicing_tolerance label" msgid "Slicing Tolerance" -msgstr "" +msgstr "Tolerância de Fatiamento" #: fdmprinter.def.json msgctxt "slicing_tolerance description" msgid "How to slice layers with diagonal surfaces. The areas of a layer can be generated based on where the middle of the layer intersects the surface (Middle). Alternatively each layer can have the areas which fall inside of the volume throughout the height of the layer (Exclusive) or a layer has the areas which fall inside anywhere within the layer (Inclusive). Exclusive retains the most details, Inclusive makes for the best fit and Middle takes the least time to process." -msgstr "" +msgstr "Como fatiar camadas com superfícies diagonais. As áreas de uma camada podem ser geradas baseadas em onde o meio da camada interseciona a superfície (Meio). Alternativamente, cada camada pode ter as áreas que se encontram dentro do volume por toda a altura da camada (Exclusivo) ou a camada pode abranger todas as áreas que tenham qualquer penetração dentro do volume (Inclusivo). Exclusivo retém mais detalhes, Inclusivo é melhor para encaixes e Meio toma menos tempo para processar." #: fdmprinter.def.json msgctxt "slicing_tolerance option middle" msgid "Middle" -msgstr "" +msgstr "Meio" #: fdmprinter.def.json msgctxt "slicing_tolerance option exclusive" msgid "Exclusive" -msgstr "" +msgstr "Exclusivo" #: fdmprinter.def.json msgctxt "slicing_tolerance option inclusive" msgid "Inclusive" -msgstr "" +msgstr "Inclusivo" #: fdmprinter.def.json msgctxt "line_width label" @@ -808,7 +808,7 @@ msgstr "O carro extrusor usado para imprimir a parede externa. Este ajuste é us #: fdmprinter.def.json msgctxt "wall_x_extruder_nr label" msgid "Inner Wall Extruder" -msgstr "" +msgstr "Extrusor da Parede Interior" #: fdmprinter.def.json msgctxt "wall_x_extruder_nr description" @@ -1388,7 +1388,7 @@ msgstr "Padrão de Preenchimento" #: fdmprinter.def.json msgctxt "infill_pattern description" msgid "The pattern of the infill material of the print. The line and zig zag infill swap direction on alternate layers, reducing material cost. The grid, triangle, tri-hexagon, cubic, octet, quarter cubic, cross and concentric patterns are fully printed every layer. Cubic, quarter cubic and octet infill change with every layer to provide a more equal distribution of strength over each direction." -msgstr "" +msgstr "O padrão do material de preenchimento da impressão. Preenchimento de Linhas e Ziguezague trocam direções em camadas alternadas, reduzindo custo do material. Os padrões de Grade, Triângulo, Tri-Hexágono, Cúbico, Octeto, Quarto Cúbico, Cruzado e Concêntrico são totalmente impressos em cada camada. Os preenchimentos Cúbico, Quarto Cúbico e Octeto mudam em cada camada para prover uma distribuição mais uniforme de forças em cada direção." #: fdmprinter.def.json msgctxt "infill_pattern option grid" @@ -1408,7 +1408,7 @@ msgstr "Triângulos" #: fdmprinter.def.json msgctxt "infill_pattern option trihexagon" msgid "Tri-Hexagon" -msgstr "" +msgstr "Tri-Hexágono" #: fdmprinter.def.json msgctxt "infill_pattern option cubic" @@ -1463,7 +1463,7 @@ msgstr "Conectar Linhas de Preenchimento" #: fdmprinter.def.json msgctxt "zig_zaggify_infill description" msgid "Connect the ends where the infill pattern meets the inner wall using a line which follows the shape of the inner wall. Enabling this setting can make the infill adhere to the walls better and reduce the effects of infill on the quality of vertical surfaces. Disabling this setting reduces the amount of material used." -msgstr "" +msgstr "Conecta as extremidades onde o padrão de preenchimento toca a parede interna usando uma linha que segue a forma da parede interna. Habilitar este ajuste pode fazer o preenchimento aderir melhor às paredes e reduzir o efeito do preenchimento na qualidade de superfícies verticais. Desabilitar este ajuda diminui a quantidade de material usado." #: fdmprinter.def.json msgctxt "infill_angles label" @@ -1478,22 +1478,22 @@ msgstr "Uma lista de direções de filetes em números inteiros a usar. Elemento #: fdmprinter.def.json msgctxt "infill_offset_x label" msgid "Infill X Offset" -msgstr "" +msgstr "Deslocamento X do Preenchimento" #: fdmprinter.def.json msgctxt "infill_offset_x description" msgid "The infill pattern is offset this distance along the X axis." -msgstr "" +msgstr "O padrão de preenchimento é corrigido/deslocado nesta distância no eixo X." #: fdmprinter.def.json msgctxt "infill_offset_y label" msgid "Infill Y Offset" -msgstr "" +msgstr "Deslocamento do Preenchimento Y" #: fdmprinter.def.json msgctxt "infill_offset_y description" msgid "The infill pattern is offset this distance along the Y axis." -msgstr "" +msgstr "O padrão de preenchimento é corrigido/deslocado nesta distância no eixo Y." #: fdmprinter.def.json msgctxt "sub_div_rad_add label" @@ -1643,7 +1643,7 @@ msgstr "Distância de Expansão do Contorno" #: fdmprinter.def.json msgctxt "expand_skins_expand_distance description" msgid "The distance the skins are expanded into the infill. Higher values makes the skin attach better to the infill pattern and makes the walls on neighboring layers adhere better to the skin. Lower values save amount of material used." -msgstr "" +msgstr "A distância em que os contornos são expandidos pra dentro do preenchimento. Valores mais altos fazem o contorno aderir melhor ao padrão de preenchimento e faz as paredes de camadas vizinhas aderirem melhor ao contorno. Valores menores diminuem a quantidade de material usado." #: fdmprinter.def.json msgctxt "top_skin_expand_distance label" @@ -1808,22 +1808,22 @@ msgstr "Ajusta o diâmetro do filamento utilizado. Acerte este valor com o diâm #: fdmprinter.def.json msgctxt "material_adhesion_tendency label" msgid "Adhesion Tendency" -msgstr "" +msgstr "Tendência à Aderência" #: fdmprinter.def.json msgctxt "material_adhesion_tendency description" msgid "Surface adhesion tendency." -msgstr "" +msgstr "Tendência de aderência da superfície" #: fdmprinter.def.json msgctxt "material_surface_energy label" msgid "Surface Energy" -msgstr "" +msgstr "Energia de Superfície" #: fdmprinter.def.json msgctxt "material_surface_energy description" msgid "Surface energy." -msgstr "" +msgstr "Energia de superfície." #: fdmprinter.def.json msgctxt "material_flow label" @@ -3556,6 +3556,8 @@ 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 "" +"A distância horizontal entre o skirt a primeira camada da impressão.\n" +"Esta é a distância mínima. Linhas múltiplas de skirt estenderão além desta distância." #: fdmprinter.def.json msgctxt "skirt_brim_minimal_length label" @@ -3615,7 +3617,7 @@ msgstr "Amaciamento do Raft" #: fdmprinter.def.json msgctxt "raft_smoothing description" msgid "This setting controls how much inner corners in the raft outline are rounded. Inward corners are rounded to a semi circle with a radius equal to the value given here. This setting also removes holes in the raft outline which are smaller than such a circle." -msgstr "" +msgstr "Este ajuste controla quanto os cantos internos do contorno do raft são arredondados. Esses cantos internos são convertidos em semicírculos com raio igual ao valor dado aqui. Este ajuste também remove furos no contorno do raft que forem menores que o círculo equivalente." #: fdmprinter.def.json msgctxt "raft_airgap label" @@ -4090,12 +4092,12 @@ msgstr "Normalmente o Cura tenta costurar pequenos furos na malha e remover part #: fdmprinter.def.json msgctxt "meshfix_maximum_resolution label" msgid "Maximum Resolution" -msgstr "" +msgstr "Resolução Máxima" #: fdmprinter.def.json msgctxt "meshfix_maximum_resolution description" msgid "The minimum size of a line segment after slicing. If you increase this, the mesh will have a lower resolution. This may allow the printer to keep up with the speed it has to process g-code and will increase slice speed by removing details of the mesh that it can't process anyway." -msgstr "" +msgstr "O tamanho mínimo de um segmento de linha após o fatiamento. Se você aumentar este valor, a malha terá uma resolução menor. Isto pode permitir que a impressora mantenha a velocidade que precisa para processar o G-Code e aumentará a velocidade de fatiamento ao remover detalhes da malha que não poderia processar de qualquer jeito." #: fdmprinter.def.json msgctxt "multiple_mesh_overlap label" @@ -4130,12 +4132,12 @@ msgstr "Troca quais volumes sobrepondo malhas vão pertencer a cada camada, de m #: fdmprinter.def.json msgctxt "remove_empty_first_layers label" msgid "Remove Empty First Layers" -msgstr "" +msgstr "Remover Camadas Iniciais Vazias" #: fdmprinter.def.json msgctxt "remove_empty_first_layers description" msgid "Remove empty layers beneath the first printed layer if they are present. Disabling this setting can cause empty first layers if the Slicing Tolerance setting is set to Exclusive or Middle." -msgstr "" +msgstr "Remove camadas vazias entre a primeira camada impressa se estiverem presentes. Desabilitar este ajuste pode criar camadas iniciais vazias se a Tolerância de Fatiamento estiver configurada para Exclusivo ou Meio." #: fdmprinter.def.json msgctxt "blackmagic label" @@ -4665,22 +4667,22 @@ msgstr "A distância média entre os pontos aleatórios introduzidos em cada seg #: fdmprinter.def.json msgctxt "flow_rate_max_extrusion_offset label" msgid "Flow rate compensation max extrusion offset" -msgstr "" +msgstr "Deslocamento de extrusão máxima da compensação de taxa de fluxo." #: fdmprinter.def.json msgctxt "flow_rate_max_extrusion_offset description" msgid "The maximum distance in mm to compensate." -msgstr "" +msgstr "A distância máxima em mm a compensar." #: fdmprinter.def.json msgctxt "flow_rate_extrusion_offset_factor label" msgid "Flow rate compensation factor" -msgstr "" +msgstr "Fator de compensaçõ de taxa de fluxo" #: fdmprinter.def.json msgctxt "flow_rate_extrusion_offset_factor description" msgid "The multiplication factor for the flow rate -> distance translation." -msgstr "" +msgstr "O fator de multiplicação para a tradução entre taxa de fluxo -> distância." #: fdmprinter.def.json msgctxt "wireframe_enabled label" From 3c863fc388b0f7e813005f3b50403572d333b9e5 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Mon, 4 Dec 2017 16:28:35 +0100 Subject: [PATCH 18/62] Get default settings view to work as sidebar component --- cura/CuraApplication.py | 37 +++++++++++++++----------- cura/Settings/SettingsSidebarView.py | 16 +++++++---- cura/Sidebar/SidebarController.py | 33 ++++++++++++----------- cura/Sidebar/SidebarControllerProxy.py | 36 +++++++++++++++++++++++++ cura/Sidebar/SidebarView.py | 15 +++++++++-- cura_app.py | 8 +++--- resources/qml/Cura.qml | 21 +++++++++------ resources/qml/SidebarSettings.qml | 21 +++++++++++++++ 8 files changed, 138 insertions(+), 49 deletions(-) create mode 100644 cura/Sidebar/SidebarControllerProxy.py create mode 100644 resources/qml/SidebarSettings.qml diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 6f5746b49d..d7ded98688 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -32,13 +32,11 @@ from UM.Operations.AddSceneNodeOperation import AddSceneNodeOperation from UM.Operations.RemoveSceneNodeOperation import RemoveSceneNodeOperation from UM.Operations.GroupedOperation import GroupedOperation from UM.Operations.SetTransformOperation import SetTransformOperation + from cura.Arrange import Arrange -from cura.Settings.SettingsSidebarView import SettingsSidebarView from cura.ShapeArray import ShapeArray from cura.ConvexHullDecorator import ConvexHullDecorator from cura.SetParentOperation import SetParentOperation -from cura.Sidebar.SidebarController import SidebarController -from cura.Sidebar.SidebarViewModel import SidebarViewModel from cura.SliceableObjectDecorator import SliceableObjectDecorator from cura.BlockSlicingDecorator import BlockSlicingDecorator @@ -78,6 +76,11 @@ from cura.Settings.ContainerManager import ContainerManager from cura.Settings.GlobalStack import GlobalStack from cura.Settings.ExtruderStack import ExtruderStack +from cura.Sidebar.SidebarController import SidebarController +from cura.Sidebar.SidebarControllerProxy import SidebarControllerProxy +from cura.Sidebar.SidebarViewModel import SidebarViewModel +from cura.Settings.SettingsSidebarView import SettingsSidebarView + from PyQt5.QtCore import QUrl, pyqtSignal, pyqtProperty, QEvent, Q_ENUMS from UM.FlameProfiler import pyqtSlot from PyQt5.QtGui import QColor, QIcon @@ -131,6 +134,7 @@ class CuraApplication(QtApplication): stacksValidationFinished = pyqtSignal() # Emitted whenever a validation is finished def __init__(self): + # this list of dir names will be used by UM to detect an old cura directory for dir_name in ["extruders", "machine_instances", "materials", "plugins", "quality", "user", "variants"]: Resources.addExpectedDirNameInData(dir_name) @@ -159,7 +163,6 @@ class CuraApplication(QtApplication): SettingDefinition.addSettingType("extruder", None, str, Validator) SettingDefinition.addSettingType("optional_extruder", None, str, None) - SettingDefinition.addSettingType("[int]", None, str, None) SettingFunction.registerOperator("extruderValues", ExtruderManager.getExtruderValues) @@ -184,7 +187,8 @@ class CuraApplication(QtApplication): ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.DefinitionChangesContainer) ## Initialise the version upgrade manager with Cura's storage paths. - import UM.VersionUpgradeManager #Needs to be here to prevent circular dependencies. + # Needs to be here to prevent circular dependencies. + import UM.VersionUpgradeManager UM.VersionUpgradeManager.VersionUpgradeManager.getInstance().setCurrentVersions( { @@ -323,6 +327,11 @@ class CuraApplication(QtApplication): self._need_to_show_user_agreement = not Preferences.getInstance().getValue("general/accepted_user_agreement") + # Set the active sidebar view based on user preferences + preferences.addPreference("cura/active_sidebar_view", "default") + active_sidebar_view = preferences.getValue("cura/active_sidebar_view") + self._sidebar_controller.setActiveSidebarView(active_sidebar_view) + for key in [ "dialog_load_path", # dialog_save_path is in LocalFileOutputDevicePlugin "dialog_profile_path", @@ -678,7 +687,6 @@ class CuraApplication(QtApplication): controller = self.getController() controller.setActiveView("SolidView") - controller.setCameraTool("CameraTool") controller.setSelectionTool("SelectionTool") @@ -741,6 +749,11 @@ class CuraApplication(QtApplication): self.exec_() + ## Get the SidebarController of this application. + # \returns SidebarControllers \type{SidebarController} + def getSidebarController(self) -> SidebarController: + return self._sidebar_controller + def getMachineManager(self, *args): if self._machine_manager is None: self._machine_manager = MachineManager.createMachineManager() @@ -786,14 +799,6 @@ class CuraApplication(QtApplication): def getPrintInformation(self): return self._print_information - ## Get the SidebarController of this application. - # A sidebar controller is created if it wasn't yet. - # \returns SidebarControllers \type{SidebarController} - def getSidebarController(self) -> SidebarController: - if self._sidebar_controller is None: - self._sidebar_controller = SidebarController(self) - return self._sidebar_controller - ## Registers objects for the QML engine to use. # # \param engine The QML engine. @@ -808,6 +813,7 @@ class CuraApplication(QtApplication): qmlRegisterUncreatableType(CuraApplication, "Cura", 1, 0, "ResourceTypes", "Just an Enum type") + qmlRegisterType(SidebarViewModel, "Cura", 1, 0, "SidebarViewModel") qmlRegisterType(ExtrudersModel, "Cura", 1, 0, "ExtrudersModel") qmlRegisterType(ContainerSettingsModel, "Cura", 1, 0, "ContainerSettingsModel") qmlRegisterSingletonType(ProfilesModel, "Cura", 1, 0, "ProfilesModel", ProfilesModel.createProfilesModel) @@ -819,8 +825,7 @@ class CuraApplication(QtApplication): qmlRegisterType(MachineNameValidator, "Cura", 1, 0, "MachineNameValidator") qmlRegisterType(UserChangesModel, "Cura", 1, 1, "UserChangesModel") qmlRegisterSingletonType(ContainerManager, "Cura", 1, 0, "ContainerManager", ContainerManager.createContainerManager) - qmlRegisterSingletonType(SidebarController, "Cura", 1, 0, "SidebarController", self.getSidebarController) - qmlRegisterType(SidebarViewModel, "Cura", 1, 0, "SidebarViewModel") + qmlRegisterSingletonType(SidebarControllerProxy, "Cura", 1, 0, "SidebarController", SidebarControllerProxy.createSidebarControllerProxy) # As of Qt5.7, it is necessary to get rid of any ".." in the path for the singleton to work. actions_url = QUrl.fromLocalFile(os.path.abspath(Resources.getPath(CuraApplication.ResourceTypes.QmlFiles, "Actions.qml"))) diff --git a/cura/Settings/SettingsSidebarView.py b/cura/Settings/SettingsSidebarView.py index 648e056044..b1cb88182b 100644 --- a/cura/Settings/SettingsSidebarView.py +++ b/cura/Settings/SettingsSidebarView.py @@ -1,23 +1,29 @@ # Copyright (c) 2017 Ultimaker B.V. -from PyQt5.QtCore import QObject +import os.path +from PyQt5.QtCore import QObject, QUrl + from UM.i18n import i18nCatalog from cura.Sidebar.SidebarView import SidebarView i18n_catalog = i18nCatalog("cura") class SettingsSidebarView(QObject, SidebarView): - def __init__(self): - super().__init__() + def __init__(self, parent = None): + super().__init__(parent) ## As the default sidebar is not a plugin, we have a get plugin ID method to allow the sidebar view model to get the needed data. def getPluginId(self): return "default" - ## As the default sidebar is not a plugin, we have a get meta data method here to allow the sidebar view model to get the needed data. + ## As the default sidebar is not a plugin, we have a add meta data method here to allow the sidebar view model to get the needed data. def getMetaData(self): return { "sidebar_view": { "name": i18n_catalog.i18nc("", "Print settings"), - "weight": 1 + "weight": 0 } } + + ## As the default sidebar is not a plugin, we have a get component path method here to allow the sidebar controller to get the needed data. + def getComponentPath(self): + return QUrl("SidebarSettings.qml") diff --git a/cura/Sidebar/SidebarController.py b/cura/Sidebar/SidebarController.py index 1212091b2d..d7789b9fb8 100644 --- a/cura/Sidebar/SidebarController.py +++ b/cura/Sidebar/SidebarController.py @@ -1,9 +1,7 @@ # Copyright (c) 2017 Ultimaker B.V. from UM.Logger import Logger -from UM.PluginRegistry import PluginRegistry from UM.Signal import Signal -from .SidebarView import SidebarView -from typing import Optional, Dict +from UM.PluginRegistry import PluginRegistry # The sidebar controller manages available sidebar components and decides which one to display. # The cura.qml file uses this controller to repeat over the sidebars and show the active index. @@ -14,6 +12,7 @@ class SidebarController: self._sidebar_views = {} self._active_sidebar_view = None + # Register the sidebar_view plugin type so plugins can expose custom sidebar views. PluginRegistry.addType("sidebar_view", self.addSidebarView) ## Emitted when the list of views changes. @@ -26,28 +25,32 @@ class SidebarController: def getApplication(self): return self._application + ## Get all sidebar views registered in this controller. + def getAllSidebarViews(self): + return self._sidebar_views + ## Add a sidebar view to the registry. # It get's a unique name based on the plugin ID. - def addSidebarView(self, sidebar_view: SidebarView): - name = sidebar_view.getPluginId() - if name not in self._sidebar_views: - self._sidebar_views[name] = sidebar_view + def addSidebarView(self, sidebar_view): + sidebar_view_id = sidebar_view.getPluginId() + if sidebar_view_id not in self._sidebar_views: + self._sidebar_views[sidebar_view_id] = sidebar_view self.sidebarViewsChanged.emit() ## Get a registered sidebar view by name. # The name is the ID of the plugin that registered the view. - def getSidebarView(self, name: str) -> Optional[SidebarView]: + def getSidebarView(self, name: str): try: return self._sidebar_views[name] except KeyError: Logger.log("e", "Unable to find %s in sidebar view list", name) return None - ## Change the active sidebar view to one of the registered views. - def setActiveSidebarView(self, name: str): - print("setting active sidebar view") - self.activeSidebarViewChanged.emit() + ## Get the active sidebar view. + def getActiveSidebarView(self): + return self._active_sidebar_view - ## Get all sidebar views registered in this controller. - def getAllSidebarViews(self) -> Dict[SidebarView]: - return self._sidebar_views + ## Change the active sidebar view to one of the registered views. + def setActiveSidebarView(self, sidebar_view_id: str): + self._active_sidebar_view = self._sidebar_views[sidebar_view_id] + self.activeSidebarViewChanged.emit() diff --git a/cura/Sidebar/SidebarControllerProxy.py b/cura/Sidebar/SidebarControllerProxy.py new file mode 100644 index 0000000000..1278d1cea8 --- /dev/null +++ b/cura/Sidebar/SidebarControllerProxy.py @@ -0,0 +1,36 @@ +# Copyright (c) 2017 Ultimaker B.V. +from PyQt5.QtCore import QObject, pyqtSlot, QUrl, pyqtProperty, pyqtSignal +from UM.Application import Application + + +## The sidebar controller proxy acts a proxy between the sidebar controller and the QMl context of the controller. +class SidebarControllerProxy(QObject): + + def __init__(self, parent = None): + super().__init__(parent) + self._controller = Application.getInstance().getSidebarController() + self._controller.activeSidebarViewChanged.connect(self._onActiveSidebarComponentChanged) + + ## Emitted when the active view changes. + activeSidebarViewChanged = pyqtSignal() + + @classmethod + def createSidebarControllerProxy(self, engine, script_engine): + return SidebarControllerProxy() + + @pyqtSlot() + def setActiveView(self, sidebar_view): + self._controller.setActiveSidebarView(sidebar_view) + + @pyqtProperty(QUrl, notify = activeSidebarViewChanged) + def activeComponentPath(self): + if not self._controller.getActiveSidebarView(): + return QUrl() + return self._controller.getActiveSidebarView().getComponentPath() + + @pyqtSlot(QUrl) + def getSidebarComponentPath(self, sidebar_id): + self._controller.getSidebarView(sidebar_id).getComponentPath() + + def _onActiveSidebarComponentChanged(self): + self.activeSidebarViewChanged.emit() diff --git a/cura/Sidebar/SidebarView.py b/cura/Sidebar/SidebarView.py index f5a9caba94..6b78de1f17 100644 --- a/cura/Sidebar/SidebarView.py +++ b/cura/Sidebar/SidebarView.py @@ -1,7 +1,9 @@ # Copyright (c) 2017 Ultimaker B.V. +from PyQt5.QtCore import QUrl +from UM.Logger import Logger from UM.PluginObject import PluginObject - +from UM.PluginRegistry import PluginRegistry # Abstract class for sidebar view objects. # By default the sidebar is Cura's settings, slicing and printing overview. @@ -10,4 +12,13 @@ class SidebarView(PluginObject): def __init__(self): super().__init__() - print("sidebar view hello") + self._view = None + + ## Get the path to the component QML file as QUrl + def getComponentPath(self): + try: + sidebar_component_file_path = PluginRegistry.getInstance().getMetaData(self.getPluginId())["sidebar_view"]["sidebar_component"] + return QUrl.fromLocalFile(sidebar_component_file_path) + except KeyError: + Logger.log("w", "Could not find sidebar component QML file for %s", self.getPluginId()) + return QUrl() diff --git a/cura_app.py b/cura_app.py index d725bc1200..dd795e5aa4 100755 --- a/cura_app.py +++ b/cura_app.py @@ -22,9 +22,10 @@ if Platform.isLinux(): # Needed for platform.linux_distribution, which is not av if Platform.isWindows() and hasattr(sys, "frozen"): try: del os.environ["PYTHONPATH"] - except KeyError: pass + except KeyError: + pass -#WORKAROUND: GITHUB-704 GITHUB-708 +# WORKAROUND: GITHUB-704 GITHUB-708 # It looks like setuptools creates a .pth file in # the default /usr/lib which causes the default site-packages # to be inserted into sys.path before PYTHONPATH. @@ -45,6 +46,7 @@ def exceptHook(hook_type, value, traceback): _crash_handler = CrashHandler(hook_type, value, traceback) _crash_handler.show() + sys.excepthook = exceptHook # Workaround for a race condition on certain systems where there @@ -75,7 +77,7 @@ faulthandler.enable() # Force an instance of CuraContainerRegistry to be created and reused later. cura.Settings.CuraContainerRegistry.CuraContainerRegistry.getInstance() -# This prestart up check is needed to determine if we should start the application at all. +# This pre-start up check is needed to determine if we should start the application at all. if not cura.CuraApplication.CuraApplication.preStartUp(): sys.exit(0) diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index e144048af7..9385b36b60 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -366,19 +366,24 @@ UM.MainWindow onStopMonitoringPrint: base.showPrintMonitor = false } - Sidebar + Loader { - id: sidebar; + id: sidebar anchors { - top: topbar.bottom; - bottom: parent.bottom; - right: parent.right; + top: topbar.bottom + bottom: parent.bottom + right: parent.right } - z: 1 - width: UM.Theme.getSize("sidebar").width; - monitoringPrint: base.showPrintMonitor + + width: UM.Theme.getSize("sidebar").width + + // all sidebar components will have access to the show print monitor flag + property bool showPrintMonitor: base.showPrintMonitor + + // dynamically get the component from the sidebar controller + source: Cura.SidebarController.activeComponentPath } Rectangle diff --git a/resources/qml/SidebarSettings.qml b/resources/qml/SidebarSettings.qml new file mode 100644 index 0000000000..85bee1f096 --- /dev/null +++ b/resources/qml/SidebarSettings.qml @@ -0,0 +1,21 @@ +// Copyright (c) 2017 Ultimaker B.V. +// Cura is released under the terms of the LGPLv3 or higher. + +import QtQuick 2.2 + +Sidebar +{ + id: sidebarSettings + + property bool showPrintMonitor: false + + anchors { + top: parent.top + bottom: parent.bottom + left: parent.left + right: parent.right + } + + width: parent.width + monitoringPrint: showPrintMonitor +} From a3818be46faf92ae43d6eac52fbc988247227036 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Mon, 4 Dec 2017 17:05:38 +0100 Subject: [PATCH 19/62] Reactivate layer view translation The layer view mode was renamed back from Simulation View to Layer View after the string freeze. Luckily we still have this old translation. Contributes to issue CURA-4601. --- resources/i18n/pt_BR/cura.po | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/resources/i18n/pt_BR/cura.po b/resources/i18n/pt_BR/cura.po index 175c00c2e0..e00c6099c4 100644 --- a/resources/i18n/pt_BR/cura.po +++ b/resources/i18n/pt_BR/cura.po @@ -4459,13 +4459,13 @@ msgstr "Leitor de Perfis do Cura" #~ msgid "To ensure that your {machine_name} is equipped with the latest features it is recommended to update the firmware regularly. This can be done on the {machine_name} (when connected to the network) or via USB." #~ msgstr "Para assegurar que sua {machine_name} esteja equipada com os recursos mais recentes, é recomendado atualizar o firmware regularmente. Isto pode ser feito na {machine_name} (quando conectado pela rede) ou via USB." -#~ msgctxt "@item:inlistbox" -#~ msgid "Layer view" -#~ msgstr "Visão de Camadas" +msgctxt "@item:inlistbox" +msgid "Layer view" +msgstr "Visão de Camadas" -#~ msgctxt "@info:title" -#~ msgid "Layer View" -#~ msgstr "Visão de Camadas" +msgctxt "@info:title" +msgid "Layer View" +msgstr "Visão de Camadas" #~ msgctxt "@menuitem" #~ msgid "Browse plugins" @@ -4567,9 +4567,9 @@ msgstr "Leitor de Perfis do Cura" #~ msgid "Provides the Layer view." #~ msgstr "Provê a visão de Camadas." -#~ msgctxt "name" -#~ msgid "Layer View" -#~ msgstr "Visão de Camadas" +msgctxt "name" +msgid "Layer View" +msgstr "Visão de Camadas" #~ msgctxt "@item:inlistbox" #~ msgid "X-Ray" @@ -4890,9 +4890,9 @@ msgstr "Leitor de Perfis do Cura" #~ msgid "Provides support for importing profiles from g-code files." #~ msgstr "Provê suporte para importar perfis de arquivos G-Code." -#~ msgctxt "@label" -#~ msgid "Layer View" -#~ msgstr "Visão de Camadas" +msgctxt "@label" +msgid "Layer View" +msgstr "Visão de Camadas" #~ msgctxt "@info:whatsthis" #~ msgid "Provides the Layer view." From caf56587fe3b96f2142268ca07eba2cfb825ebfa Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Mon, 4 Dec 2017 19:37:03 +0100 Subject: [PATCH 20/62] Implement switching sidebar views --- cura/CuraApplication.py | 34 ++++++++++------- cura/Settings/SettingsSidebarView.py | 11 +++--- cura/Sidebar/SidebarController.py | 7 +++- cura/Sidebar/SidebarControllerProxy.py | 36 +++++++++++------- cura/Sidebar/SidebarView.py | 17 +++++++-- cura/Sidebar/SidebarViewModel.py | 15 ++++---- resources/qml/Cura.qml | 52 +++++++++++++++++++++++--- resources/qml/SidebarSettings.qml | 21 ----------- 8 files changed, 121 insertions(+), 72 deletions(-) delete mode 100644 resources/qml/SidebarSettings.qml diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index d7ded98688..e9e521fb49 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -201,6 +201,10 @@ class CuraApplication(QtApplication): } ) + ## As of Cura 3.2, the sidebar is controlled by a controller. + # This functionality was added to allow plugins registering custom sidebar views. + self._sidebar_controller = SidebarController(self) + self._currently_loading_files = [] self._non_sliceable_extensions = [] @@ -221,14 +225,6 @@ class CuraApplication(QtApplication): self.setWindowIcon(QIcon(Resources.getPath(Resources.Images, "cura-icon.png"))) - ## As of Cura 3.2, the sidebar is controlled by a controller. - # This functionality was added to allow plugins registering custom sidebar views. - self._sidebar_controller = SidebarController(self) - - ## Register the default settings sidebar manually - settings_sidebar_view = SettingsSidebarView() - self._sidebar_controller.addSidebarView(settings_sidebar_view) - self.setRequiredPlugins([ "CuraEngineBackend", "UserAgreement", @@ -327,11 +323,6 @@ class CuraApplication(QtApplication): self._need_to_show_user_agreement = not Preferences.getInstance().getValue("general/accepted_user_agreement") - # Set the active sidebar view based on user preferences - preferences.addPreference("cura/active_sidebar_view", "default") - active_sidebar_view = preferences.getValue("cura/active_sidebar_view") - self._sidebar_controller.setActiveSidebarView(active_sidebar_view) - for key in [ "dialog_load_path", # dialog_save_path is in LocalFileOutputDevicePlugin "dialog_profile_path", @@ -737,6 +728,10 @@ class CuraApplication(QtApplication): if not run_headless: self.initializeEngine() + # Now that the SidebarViewModel has been created we can set the default sidebar view + # TODO: put this in a more elegant place + self._setDefaultSidebarView() + if run_headless or self._engine.rootObjects: self.closeSplash() @@ -1324,6 +1319,19 @@ class CuraApplication(QtApplication): def _addProfileWriter(self, profile_writer): pass + ## Create and register the default sidebar component (settings) + def _setDefaultSidebarView(self): + + # Register the default settings sidebar manually + settings_sidebar_view = SettingsSidebarView() + self._sidebar_controller.addSidebarView(settings_sidebar_view) + + # Set the default sidebar view depending on user preferences. + preferences = Preferences.getInstance() + preferences.addPreference("cura/active_sidebar_view", settings_sidebar_view.getPluginId()) + active_sidebar_view = preferences.getValue("cura/active_sidebar_view") + self._sidebar_controller.setActiveSidebarView(active_sidebar_view) + @pyqtSlot("QSize") def setMinimumWindowSize(self, size): self.getMainWindow().setMinimumSize(size) diff --git a/cura/Settings/SettingsSidebarView.py b/cura/Settings/SettingsSidebarView.py index b1cb88182b..8b861fbaf6 100644 --- a/cura/Settings/SettingsSidebarView.py +++ b/cura/Settings/SettingsSidebarView.py @@ -1,8 +1,8 @@ # Copyright (c) 2017 Ultimaker B.V. -import os.path -from PyQt5.QtCore import QObject, QUrl +from PyQt5.QtCore import QObject from UM.i18n import i18nCatalog + from cura.Sidebar.SidebarView import SidebarView i18n_catalog = i18nCatalog("cura") @@ -19,11 +19,10 @@ class SettingsSidebarView(QObject, SidebarView): def getMetaData(self): return { "sidebar_view": { - "name": i18n_catalog.i18nc("", "Print settings"), + "name": i18n_catalog.i18nc("@item:inmenu", "Print settings"), "weight": 0 } } - ## As the default sidebar is not a plugin, we have a get component path method here to allow the sidebar controller to get the needed data. - def getComponentPath(self): - return QUrl("SidebarSettings.qml") + def getComponent(self): + return None diff --git a/cura/Sidebar/SidebarController.py b/cura/Sidebar/SidebarController.py index d7789b9fb8..420f00a489 100644 --- a/cura/Sidebar/SidebarController.py +++ b/cura/Sidebar/SidebarController.py @@ -1,5 +1,6 @@ # Copyright (c) 2017 Ultimaker B.V. from UM.Logger import Logger +from UM.Preferences import Preferences from UM.Signal import Signal from UM.PluginRegistry import PluginRegistry @@ -52,5 +53,7 @@ class SidebarController: ## Change the active sidebar view to one of the registered views. def setActiveSidebarView(self, sidebar_view_id: str): - self._active_sidebar_view = self._sidebar_views[sidebar_view_id] - self.activeSidebarViewChanged.emit() + if sidebar_view_id in self._sidebar_views: + self._active_sidebar_view = self._sidebar_views[sidebar_view_id] + Preferences.getInstance().setValue("cura/active_sidebar_view", sidebar_view_id) + self.activeSidebarViewChanged.emit() diff --git a/cura/Sidebar/SidebarControllerProxy.py b/cura/Sidebar/SidebarControllerProxy.py index 1278d1cea8..c373dc0a0a 100644 --- a/cura/Sidebar/SidebarControllerProxy.py +++ b/cura/Sidebar/SidebarControllerProxy.py @@ -4,33 +4,41 @@ from UM.Application import Application ## The sidebar controller proxy acts a proxy between the sidebar controller and the QMl context of the controller. +from UM.Logger import Logger + + class SidebarControllerProxy(QObject): def __init__(self, parent = None): super().__init__(parent) self._controller = Application.getInstance().getSidebarController() - self._controller.activeSidebarViewChanged.connect(self._onActiveSidebarComponentChanged) + self._controller.activeSidebarViewChanged.connect(self._onActiveSidebarViewChanged) - ## Emitted when the active view changes. activeSidebarViewChanged = pyqtSignal() @classmethod def createSidebarControllerProxy(self, engine, script_engine): return SidebarControllerProxy() - @pyqtSlot() - def setActiveView(self, sidebar_view): - self._controller.setActiveSidebarView(sidebar_view) + @pyqtProperty(str, notify = activeSidebarViewChanged) + def activeSidebarId(self): + if self._controller.getActiveSidebarView() is not None: + return self._controller.getActiveSidebarView().getPluginId() + else: + return "default" - @pyqtProperty(QUrl, notify = activeSidebarViewChanged) - def activeComponentPath(self): - if not self._controller.getActiveSidebarView(): - return QUrl() - return self._controller.getActiveSidebarView().getComponentPath() + @pyqtSlot(str) + def setActiveSidebarView(self, sidebar_view_id): + Logger.log("d", "Setting active sidebar view to %s", sidebar_view_id) + self._controller.setActiveSidebarView(sidebar_view_id) - @pyqtSlot(QUrl) + @pyqtSlot(str, result = QObject) + def getSidebarComponent(self, sidebar_id): + return self._controller.getSidebarView(sidebar_id).getComponent() + + @pyqtSlot(str, result = QUrl) def getSidebarComponentPath(self, sidebar_id): - self._controller.getSidebarView(sidebar_id).getComponentPath() + return self._controller.getSidebarView(sidebar_id).getComponentPath() - def _onActiveSidebarComponentChanged(self): - self.activeSidebarViewChanged.emit() + def _onActiveSidebarViewChanged(self): + self.activeSidebarViewChanged.emit() \ No newline at end of file diff --git a/cura/Sidebar/SidebarView.py b/cura/Sidebar/SidebarView.py index 6b78de1f17..d5c20e0858 100644 --- a/cura/Sidebar/SidebarView.py +++ b/cura/Sidebar/SidebarView.py @@ -1,6 +1,7 @@ # Copyright (c) 2017 Ultimaker B.V. -from PyQt5.QtCore import QUrl +import os.path +from UM.Application import Application from UM.Logger import Logger from UM.PluginObject import PluginObject from UM.PluginRegistry import PluginRegistry @@ -14,11 +15,21 @@ class SidebarView(PluginObject): super().__init__() self._view = None + def getComponent(self): + if not self._view: + self.createView() + return self._view + + def createView(self): + component_path = self.getComponentPath() + self._view = Application.getInstance().createQmlComponent(component_path, {"manager": self}) + ## Get the path to the component QML file as QUrl def getComponentPath(self): try: + plugin_path = PluginRegistry.getInstance().getPluginPath(self.getPluginId()) sidebar_component_file_path = PluginRegistry.getInstance().getMetaData(self.getPluginId())["sidebar_view"]["sidebar_component"] - return QUrl.fromLocalFile(sidebar_component_file_path) + return os.path.join(plugin_path, sidebar_component_file_path) except KeyError: Logger.log("w", "Could not find sidebar component QML file for %s", self.getPluginId()) - return QUrl() + return "" diff --git a/cura/Sidebar/SidebarViewModel.py b/cura/Sidebar/SidebarViewModel.py index 4ed1ca60cb..de0aae182c 100644 --- a/cura/Sidebar/SidebarViewModel.py +++ b/cura/Sidebar/SidebarViewModel.py @@ -26,17 +26,18 @@ class SidebarViewModel(ListModel): ## Update the model when new views are added or another view is made the active view. def _onSidebarViewsChanged(self): items = [] + current_view_id = None + sidebar_views = self._controller.getAllSidebarViews() current_view = self._controller.getActiveSidebarView() + if current_view: + current_view_id = current_view.getPluginId() for sidebar_view_id, sidebar_view in sidebar_views.items(): - plugin_metadata = PluginRegistry.getInstance().getMetaData(sidebar_view_id) - if plugin_metadata: - # Check if the registered view came from a plugin and extract the metadata if so. - sidebar_view_metadata = plugin_metadata.get("sidebar_view", {}) + if sidebar_view_id != "default": + sidebar_view_metadata = PluginRegistry.getInstance().getMetaData(sidebar_view_id).get("sidebar_view", {}) else: - # Get the meta data directly from the plugin - sidebar_view_metadata = sidebar_view.getMetaData() + sidebar_view_metadata = sidebar_view.getMetaData().get("sidebar_view", {}) # Skip view modes that are marked as not visible if "visible" in sidebar_view_metadata and not sidebar_view_metadata["visible"]: @@ -48,7 +49,7 @@ class SidebarViewModel(ListModel): items.append({ "id": sidebar_view_id, "name": name, - "active": sidebar_view_id == current_view.getPluginId(), + "active": sidebar_view_id == current_view_id, "weight": weight }) diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index 9385b36b60..5f421881c0 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -331,7 +331,7 @@ UM.MainWindow text: catalog.i18nc("@action:button","Open File"); iconSource: UM.Theme.getIcon("load") style: UM.Theme.styles.tool_button - tooltip: ''; + tooltip: "" anchors { top: topbar.bottom; @@ -366,7 +366,7 @@ UM.MainWindow onStopMonitoringPrint: base.showPrintMonitor = false } - Loader + Rectangle { id: sidebar @@ -379,11 +379,30 @@ UM.MainWindow width: UM.Theme.getSize("sidebar").width - // all sidebar components will have access to the show print monitor flag - property bool showPrintMonitor: base.showPrintMonitor + // The sidebarRepeater exposes sidebar views provided by plugins. + // Whenever a plugin sidebar view is active (e.g. not "default"), that sidebar view is shown. + Repeater + { + id: sidebarRepeater - // dynamically get the component from the sidebar controller - source: Cura.SidebarController.activeComponentPath + model: Cura.SidebarViewModel { } + + delegate: Loader + { + id: delegate + asynchronous: true + visible: model.active + + // dynamically get the component from the sidebar controller or set the default sidebar + sourceComponent: { + if (model.id !== "default") { + return Cura.SidebarController.getSidebarComponent(model.id) + } else { + return defaultSidebar + } + } + } + } } Rectangle @@ -434,6 +453,27 @@ UM.MainWindow } } + // This is the default sidebar view. + // It is used as sourceComponent for the default sidebar view. + Component + { + id: defaultSidebar + + Sidebar + { +// anchors { +// top: parent.top +// bottom: parent.bottom +// left: parent.left +// right: parent.right +// } +// +// width: parent.width +// +// monitoringPrint: base.showPrintMonitor + } + } + UM.PreferencesDialog { id: preferences diff --git a/resources/qml/SidebarSettings.qml b/resources/qml/SidebarSettings.qml deleted file mode 100644 index 85bee1f096..0000000000 --- a/resources/qml/SidebarSettings.qml +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright (c) 2017 Ultimaker B.V. -// Cura is released under the terms of the LGPLv3 or higher. - -import QtQuick 2.2 - -Sidebar -{ - id: sidebarSettings - - property bool showPrintMonitor: false - - anchors { - top: parent.top - bottom: parent.bottom - left: parent.left - right: parent.right - } - - width: parent.width - monitoringPrint: showPrintMonitor -} From 5673a834bc16edd0c4afaa79c3258ad7bc63401a Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Mon, 4 Dec 2017 19:48:42 +0100 Subject: [PATCH 21/62] Fix showing default sidebar, cleanup needed --- resources/qml/Cura.qml | 42 +++++++++++++++++++----------------------- 1 file changed, 19 insertions(+), 23 deletions(-) diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index 5f421881c0..c08080f276 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -379,6 +379,25 @@ UM.MainWindow width: UM.Theme.getSize("sidebar").width + // This is the default sidebar view. + // It is hidden when the active sidebar view ID is not default. + Sidebar + { + id: defaultSidebar + + anchors { + top: parent.top + bottom: parent.bottom + left: parent.left + right: parent.right + } + + width: parent.width + z: 1 + monitoringPrint: base.showPrintMonitor + visible: Cura.SidebarController.activeSidebarId == "default" + } + // The sidebarRepeater exposes sidebar views provided by plugins. // Whenever a plugin sidebar view is active (e.g. not "default"), that sidebar view is shown. Repeater @@ -397,8 +416,6 @@ UM.MainWindow sourceComponent: { if (model.id !== "default") { return Cura.SidebarController.getSidebarComponent(model.id) - } else { - return defaultSidebar } } } @@ -453,27 +470,6 @@ UM.MainWindow } } - // This is the default sidebar view. - // It is used as sourceComponent for the default sidebar view. - Component - { - id: defaultSidebar - - Sidebar - { -// anchors { -// top: parent.top -// bottom: parent.bottom -// left: parent.left -// right: parent.right -// } -// -// width: parent.width -// -// monitoringPrint: base.showPrintMonitor - } - } - UM.PreferencesDialog { id: preferences From 6f8ed09d3d43347e4328409995bfb729d36697c5 Mon Sep 17 00:00:00 2001 From: Thomas Karl Pietrowski Date: Tue, 5 Dec 2017 00:14:19 +0100 Subject: [PATCH 22/62] SidebarHeader: Adding missing comment about "extruder row" Just read the code and noticed that. --- resources/qml/SidebarHeader.qml | 1 + 1 file changed, 1 insertion(+) diff --git a/resources/qml/SidebarHeader.qml b/resources/qml/SidebarHeader.qml index 07ce75d738..3e1e85824a 100644 --- a/resources/qml/SidebarHeader.qml +++ b/resources/qml/SidebarHeader.qml @@ -34,6 +34,7 @@ Column width: height } + // Extruder Row Item { id: extruderSelectionRow From e33288b7c8cf7b4f3e739cf2d2dcb086d4e782d0 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Tue, 5 Dec 2017 10:56:59 +0100 Subject: [PATCH 23/62] Move sidebar target for plugins to only middle section --- cura/CuraApplication.py | 11 +---- cura/Settings/SettingsSidebarView.py | 28 ----------- cura/Sidebar/SidebarController.py | 9 +++- cura/Sidebar/SidebarControllerProxy.py | 5 +- cura/Sidebar/SidebarViewModel.py | 16 +++++-- resources/qml/Cura.qml | 24 ---------- resources/qml/Sidebar.qml | 66 +++++++++++++++++--------- 7 files changed, 67 insertions(+), 92 deletions(-) delete mode 100644 cura/Settings/SettingsSidebarView.py diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index e9e521fb49..997ed7782e 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -79,7 +79,6 @@ from cura.Settings.ExtruderStack import ExtruderStack from cura.Sidebar.SidebarController import SidebarController from cura.Sidebar.SidebarControllerProxy import SidebarControllerProxy from cura.Sidebar.SidebarViewModel import SidebarViewModel -from cura.Settings.SettingsSidebarView import SettingsSidebarView from PyQt5.QtCore import QUrl, pyqtSignal, pyqtProperty, QEvent, Q_ENUMS from UM.FlameProfiler import pyqtSlot @@ -1319,16 +1318,10 @@ class CuraApplication(QtApplication): def _addProfileWriter(self, profile_writer): pass - ## Create and register the default sidebar component (settings) + ## Set the default sidebar view to "default" def _setDefaultSidebarView(self): - - # Register the default settings sidebar manually - settings_sidebar_view = SettingsSidebarView() - self._sidebar_controller.addSidebarView(settings_sidebar_view) - - # Set the default sidebar view depending on user preferences. preferences = Preferences.getInstance() - preferences.addPreference("cura/active_sidebar_view", settings_sidebar_view.getPluginId()) + preferences.addPreference("cura/active_sidebar_view", "default") active_sidebar_view = preferences.getValue("cura/active_sidebar_view") self._sidebar_controller.setActiveSidebarView(active_sidebar_view) diff --git a/cura/Settings/SettingsSidebarView.py b/cura/Settings/SettingsSidebarView.py deleted file mode 100644 index 8b861fbaf6..0000000000 --- a/cura/Settings/SettingsSidebarView.py +++ /dev/null @@ -1,28 +0,0 @@ -# Copyright (c) 2017 Ultimaker B.V. -from PyQt5.QtCore import QObject - -from UM.i18n import i18nCatalog - -from cura.Sidebar.SidebarView import SidebarView -i18n_catalog = i18nCatalog("cura") - -class SettingsSidebarView(QObject, SidebarView): - - def __init__(self, parent = None): - super().__init__(parent) - - ## As the default sidebar is not a plugin, we have a get plugin ID method to allow the sidebar view model to get the needed data. - def getPluginId(self): - return "default" - - ## As the default sidebar is not a plugin, we have a add meta data method here to allow the sidebar view model to get the needed data. - def getMetaData(self): - return { - "sidebar_view": { - "name": i18n_catalog.i18nc("@item:inmenu", "Print settings"), - "weight": 0 - } - } - - def getComponent(self): - return None diff --git a/cura/Sidebar/SidebarController.py b/cura/Sidebar/SidebarController.py index 420f00a489..a40ee07157 100644 --- a/cura/Sidebar/SidebarController.py +++ b/cura/Sidebar/SidebarController.py @@ -10,7 +10,7 @@ class SidebarController: def __init__(self, application): self._application = application - self._sidebar_views = {} + self._sidebar_views = {"default": {}} # default is needed for the default settings sidebar self._active_sidebar_view = None # Register the sidebar_view plugin type so plugins can expose custom sidebar views. @@ -51,6 +51,13 @@ class SidebarController: def getActiveSidebarView(self): return self._active_sidebar_view + ## Get the ID of the active sidebar view. + def getActiveSidebarViewId(self): + if self._active_sidebar_view: + if hasattr(self._active_sidebar_view, "getPluginId"): + return self._active_sidebar_view.getPluginId() + return "default" + ## Change the active sidebar view to one of the registered views. def setActiveSidebarView(self, sidebar_view_id: str): if sidebar_view_id in self._sidebar_views: diff --git a/cura/Sidebar/SidebarControllerProxy.py b/cura/Sidebar/SidebarControllerProxy.py index c373dc0a0a..a9b23fa08e 100644 --- a/cura/Sidebar/SidebarControllerProxy.py +++ b/cura/Sidebar/SidebarControllerProxy.py @@ -22,10 +22,7 @@ class SidebarControllerProxy(QObject): @pyqtProperty(str, notify = activeSidebarViewChanged) def activeSidebarId(self): - if self._controller.getActiveSidebarView() is not None: - return self._controller.getActiveSidebarView().getPluginId() - else: - return "default" + return self._controller.getActiveSidebarViewId() @pyqtSlot(str) def setActiveSidebarView(self, sidebar_view_id): diff --git a/cura/Sidebar/SidebarViewModel.py b/cura/Sidebar/SidebarViewModel.py index de0aae182c..f6dcb4455b 100644 --- a/cura/Sidebar/SidebarViewModel.py +++ b/cura/Sidebar/SidebarViewModel.py @@ -34,10 +34,18 @@ class SidebarViewModel(ListModel): current_view_id = current_view.getPluginId() for sidebar_view_id, sidebar_view in sidebar_views.items(): - if sidebar_view_id != "default": - sidebar_view_metadata = PluginRegistry.getInstance().getMetaData(sidebar_view_id).get("sidebar_view", {}) - else: - sidebar_view_metadata = sidebar_view.getMetaData().get("sidebar_view", {}) + + # Override fields for default settings sidebar + if sidebar_view_id == "default": + items.append({ + "id": "default", + "name": "Print settings", + "weight": 0, + "active": current_view_id == "default" + }) + continue + + sidebar_view_metadata = PluginRegistry.getInstance().getMetaData(sidebar_view_id).get("sidebar_view", {}) # Skip view modes that are marked as not visible if "visible" in sidebar_view_metadata and not sidebar_view_metadata["visible"]: diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index c08080f276..3e5527c75c 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -395,30 +395,6 @@ UM.MainWindow width: parent.width z: 1 monitoringPrint: base.showPrintMonitor - visible: Cura.SidebarController.activeSidebarId == "default" - } - - // The sidebarRepeater exposes sidebar views provided by plugins. - // Whenever a plugin sidebar view is active (e.g. not "default"), that sidebar view is shown. - Repeater - { - id: sidebarRepeater - - model: Cura.SidebarViewModel { } - - delegate: Loader - { - id: delegate - asynchronous: true - visible: model.active - - // dynamically get the component from the sidebar controller or set the default sidebar - sourceComponent: { - if (model.id !== "default") { - return Cura.SidebarController.getSidebarComponent(model.id) - } - } - } } } diff --git a/resources/qml/Sidebar.qml b/resources/qml/Sidebar.qml index bed5b4c873..9d0957806d 100644 --- a/resources/qml/Sidebar.qml +++ b/resources/qml/Sidebar.qml @@ -87,25 +87,6 @@ Rectangle } } - SidebarHeader { - id: header - width: parent.width - visible: machineExtruderCount.properties.value > 1 || Cura.MachineManager.hasMaterials || Cura.MachineManager.hasVariants - - onShowTooltip: base.showTooltip(item, location, text) - onHideTooltip: base.hideTooltip() - } - - Rectangle { - id: headerSeparator - width: parent.width - visible: settingsModeSelection.visible && header.visible - height: visible ? UM.Theme.getSize("sidebar_lining").height : 0 - color: UM.Theme.getColor("sidebar_lining") - anchors.top: header.bottom - anchors.topMargin: visible ? UM.Theme.getSize("sidebar_margin").height : 0 - } - onCurrentModeIndexChanged: { UM.Preferences.setValue("cura/active_mode", currentModeIndex); @@ -115,6 +96,24 @@ Rectangle } } + SidebarHeader { + id: header + width: parent.width + visible: (machineExtruderCount.properties.value > 1 || Cura.MachineManager.hasMaterials || Cura.MachineManager.hasVariants) && Cura.SidebarController.activeSidebarId == "default" + onShowTooltip: base.showTooltip(item, location, text) + onHideTooltip: base.hideTooltip() + } + + Rectangle { + id: headerSeparator + width: parent.width + visible: settingsModeSelection.visible && header.visible && Cura.SidebarController.activeSidebarId == "default" + height: visible ? UM.Theme.getSize("sidebar_lining").height : 0 + color: UM.Theme.getColor("sidebar_lining") + anchors.top: header.bottom + anchors.topMargin: visible ? UM.Theme.getSize("sidebar_margin").height : 0 + } + Label { id: settingsModeLabel text: !hideSettings ? catalog.i18nc("@label:listbox", "Print Setup") : catalog.i18nc("@label:listbox","Print Setup disabled\nG-code files cannot be modified"); @@ -125,7 +124,7 @@ Rectangle width: Math.floor(parent.width * 0.45) font: UM.Theme.getFont("large") color: UM.Theme.getColor("text") - visible: !monitoringPrint && !hideView + visible: !monitoringPrint && !hideView && Cura.SidebarController.activeSidebarId == "default" } Rectangle { @@ -147,7 +146,7 @@ Rectangle } } anchors.topMargin: UM.Theme.getSize("sidebar_margin").height - visible: !monitoringPrint && !hideSettings && !hideView + visible: !monitoringPrint && !hideSettings && !hideView && Cura.SidebarController.activeSidebarId == "default" Component{ id: wizardDelegate Button { @@ -228,7 +227,7 @@ Rectangle anchors.topMargin: UM.Theme.getSize("sidebar_margin").height anchors.left: base.left anchors.right: base.right - visible: !monitoringPrint && !hideSettings + visible: !monitoringPrint && !hideSettings && Cura.SidebarController.activeSidebarId == "default" delegate: StackViewDelegate { @@ -259,6 +258,29 @@ Rectangle } } + // The sidebarRepeater exposes sidebar views provided by plugins. + // Whenever a plugin sidebar view is active (e.g. not "default"), that sidebar view is shown. + Repeater + { + id: sidebarRepeater + + model: Cura.SidebarViewModel { } + + delegate: Loader + { + id: delegate + asynchronous: true + visible: model.active + + // dynamically get the component from the sidebar controller or set the default sidebar + sourceComponent: { + if (model.id !== "default") { + return Cura.SidebarController.getSidebarComponent(model.id) + } + } + } + } + Loader { id: controlItem From 030dc74f3f44c548a4af592bede0c3067114518c Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Tue, 5 Dec 2017 12:19:00 +0100 Subject: [PATCH 24/62] Fix now showing any sidebar when in monitor mode --- resources/qml/Cura.qml | 22 +++------------------- resources/qml/Sidebar.qml | 18 +++++++++--------- 2 files changed, 12 insertions(+), 28 deletions(-) diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index 3e5527c75c..bf67efdba3 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -366,7 +366,7 @@ UM.MainWindow onStopMonitoringPrint: base.showPrintMonitor = false } - Rectangle + Sidebar { id: sidebar @@ -378,24 +378,8 @@ UM.MainWindow } width: UM.Theme.getSize("sidebar").width - - // This is the default sidebar view. - // It is hidden when the active sidebar view ID is not default. - Sidebar - { - id: defaultSidebar - - anchors { - top: parent.top - bottom: parent.bottom - left: parent.left - right: parent.right - } - - width: parent.width - z: 1 - monitoringPrint: base.showPrintMonitor - } + z: 1 + monitoringPrint: base.showPrintMonitor } Rectangle diff --git a/resources/qml/Sidebar.qml b/resources/qml/Sidebar.qml index 9d0957806d..4a8a0c6a32 100644 --- a/resources/qml/Sidebar.qml +++ b/resources/qml/Sidebar.qml @@ -87,15 +87,6 @@ Rectangle } } - onCurrentModeIndexChanged: - { - UM.Preferences.setValue("cura/active_mode", currentModeIndex); - if(modesListModel.count > base.currentModeIndex) - { - sidebarContents.push({ "item": modesListModel.get(base.currentModeIndex).item, "replace": true }); - } - } - SidebarHeader { id: header width: parent.width @@ -114,6 +105,15 @@ Rectangle anchors.topMargin: visible ? UM.Theme.getSize("sidebar_margin").height : 0 } + onCurrentModeIndexChanged: + { + UM.Preferences.setValue("cura/active_mode", currentModeIndex); + if(modesListModel.count > base.currentModeIndex) + { + sidebarContents.push({ "item": modesListModel.get(base.currentModeIndex).item, "replace": true }); + } + } + Label { id: settingsModeLabel text: !hideSettings ? catalog.i18nc("@label:listbox", "Print Setup") : catalog.i18nc("@label:listbox","Print Setup disabled\nG-code files cannot be modified"); From a6a360284017592c284efa2149f888411fb936d8 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Tue, 5 Dec 2017 12:27:13 +0100 Subject: [PATCH 25/62] View default view selected in view menu --- cura/Sidebar/SidebarControllerProxy.py | 2 +- cura/Sidebar/SidebarViewModel.py | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/cura/Sidebar/SidebarControllerProxy.py b/cura/Sidebar/SidebarControllerProxy.py index a9b23fa08e..00a898301a 100644 --- a/cura/Sidebar/SidebarControllerProxy.py +++ b/cura/Sidebar/SidebarControllerProxy.py @@ -38,4 +38,4 @@ class SidebarControllerProxy(QObject): return self._controller.getSidebarView(sidebar_id).getComponentPath() def _onActiveSidebarViewChanged(self): - self.activeSidebarViewChanged.emit() \ No newline at end of file + self.activeSidebarViewChanged.emit() diff --git a/cura/Sidebar/SidebarViewModel.py b/cura/Sidebar/SidebarViewModel.py index f6dcb4455b..8000d87cc5 100644 --- a/cura/Sidebar/SidebarViewModel.py +++ b/cura/Sidebar/SidebarViewModel.py @@ -26,11 +26,11 @@ class SidebarViewModel(ListModel): ## Update the model when new views are added or another view is made the active view. def _onSidebarViewsChanged(self): items = [] - current_view_id = None + current_view_id = "default" sidebar_views = self._controller.getAllSidebarViews() current_view = self._controller.getActiveSidebarView() - if current_view: + if current_view and hasattr(current_view, "getPluginId"): current_view_id = current_view.getPluginId() for sidebar_view_id, sidebar_view in sidebar_views.items(): @@ -39,9 +39,9 @@ class SidebarViewModel(ListModel): if sidebar_view_id == "default": items.append({ "id": "default", - "name": "Print settings", - "weight": 0, - "active": current_view_id == "default" + "name": "Print settings sidebar", + "active": sidebar_view_id == current_view_id, + "weight": 0 }) continue @@ -52,7 +52,7 @@ class SidebarViewModel(ListModel): continue name = sidebar_view_metadata.get("name", sidebar_view_id) - weight = sidebar_view_metadata.get("weight", 0) + weight = sidebar_view_metadata.get("weight", 1) items.append({ "id": sidebar_view_id, From 51405ca0f770fbf9e631fa16f43ae5c1e52140f9 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Tue, 5 Dec 2017 13:29:04 +0100 Subject: [PATCH 26/62] tmp --- resources/qml/Sidebar.qml | 9 ---- resources/qml/Sidebar/PrinterOutput.qml | 65 +++++++++++++++++++++++++ resources/qml/Sidebar/Settings.qml | 0 3 files changed, 65 insertions(+), 9 deletions(-) create mode 100644 resources/qml/Sidebar/PrinterOutput.qml create mode 100644 resources/qml/Sidebar/Settings.qml diff --git a/resources/qml/Sidebar.qml b/resources/qml/Sidebar.qml index 4a8a0c6a32..d5af3e939b 100644 --- a/resources/qml/Sidebar.qml +++ b/resources/qml/Sidebar.qml @@ -105,15 +105,6 @@ Rectangle anchors.topMargin: visible ? UM.Theme.getSize("sidebar_margin").height : 0 } - onCurrentModeIndexChanged: - { - UM.Preferences.setValue("cura/active_mode", currentModeIndex); - if(modesListModel.count > base.currentModeIndex) - { - sidebarContents.push({ "item": modesListModel.get(base.currentModeIndex).item, "replace": true }); - } - } - Label { id: settingsModeLabel text: !hideSettings ? catalog.i18nc("@label:listbox", "Print Setup") : catalog.i18nc("@label:listbox","Print Setup disabled\nG-code files cannot be modified"); diff --git a/resources/qml/Sidebar/PrinterOutput.qml b/resources/qml/Sidebar/PrinterOutput.qml new file mode 100644 index 0000000000..5506944154 --- /dev/null +++ b/resources/qml/Sidebar/PrinterOutput.qml @@ -0,0 +1,65 @@ +// Copyright (c) 2017 Ultimaker B.V. +// Cura is released under the terms of the LGPLv3 or higher. + +import QtQuick 2.2 +import QtQuick.Controls 1.1 +import QtQuick.Controls.Styles 1.1 +import QtQuick.Layouts 1.1 + +import UM 1.2 as UM +import Cura 1.0 as Cura + +Item +{ + id: printerOutputSection + + UM.I18nCatalog { + id: catalog + name: "cura" + } + + color: UM.Theme.getColor("sidebar") + + // status + property bool isMonitoring: false + + // printer data + property bool printerConnected: Cura.MachineManager.printerOutputDevices.length != 0 + property bool printerAcceptsCommands: printerConnected && Cura.MachineManager.printerOutputDevices[0].acceptsCommands + property var connectedPrinter: Cura.MachineManager.printerOutputDevices.length >= 1 ? Cura.MachineManager.printerOutputDevices[0] : null + property int backendState: UM.Backend.state + + // print job data + property variant printDuration: PrintInformation.currentPrintTime + property variant printMaterialLengths: PrintInformation.materialLengths + property variant printMaterialWeights: PrintInformation.materialWeights + property variant printMaterialCosts: PrintInformation.materialCosts + property variant printMaterialNames: PrintInformation.materialNames + + // helper function for padding pretty time + function strPadLeft(string, pad, length) { + return (new Array(length + 1).join(pad) + string).slice(-length); + } + + // convert a timestamp to a human readable pretty time + function getPrettyTime (time) { + var hours = Math.floor(time / 3600) + time -= hours * 3600 + var minutes = Math.floor(time / 60) + time -= minutes * 60 + var seconds = Math.floor(time) + var finalTime = strPadLeft(hours, "0", 2) + ":" + strPadLeft(minutes, "0", 2) + ":" + strPadLeft(seconds, "0", 2) + return finalTime + } + + // TODO: change name of this component + MonitorButton + { + id: monitorButton + implicitWidth: base.width + anchors.top: footerSeparator.bottom + anchors.topMargin: UM.Theme.getSize("sidebar_margin").height + anchors.bottom: parent.bottom + visible: monitoringPrint + } +} diff --git a/resources/qml/Sidebar/Settings.qml b/resources/qml/Sidebar/Settings.qml new file mode 100644 index 0000000000..e69de29bb2 From 13c7099e2fc748f2fa50b06eb8296de548657ddf Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Tue, 5 Dec 2017 13:29:12 +0100 Subject: [PATCH 27/62] Don't show slice info message if the preference is set to not send it If the preference was set to not send it but the user never clicked on the message, don't show the message. Fixes #2840. --- plugins/SliceInfoPlugin/SliceInfo.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/SliceInfoPlugin/SliceInfo.py b/plugins/SliceInfoPlugin/SliceInfo.py index 79963a4740..67f977adce 100755 --- a/plugins/SliceInfoPlugin/SliceInfo.py +++ b/plugins/SliceInfoPlugin/SliceInfo.py @@ -39,7 +39,7 @@ class SliceInfo(Extension): Preferences.getInstance().addPreference("info/send_slice_info", True) Preferences.getInstance().addPreference("info/asked_send_slice_info", False) - if not Preferences.getInstance().getValue("info/asked_send_slice_info"): + if not Preferences.getInstance().getValue("info/asked_send_slice_info") and Preferences.getInstance().getValue("info/send_slice_info"): self.send_slice_info_message = Message(catalog.i18nc("@info", "Cura collects anonymised slicing statistics. You can disable this in the preferences."), lifetime = 0, dismissable = False, From 9a538b330aeee88494e452ef4133139a14548db9 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Tue, 5 Dec 2017 14:14:52 +0100 Subject: [PATCH 28/62] Ignore new plugins --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index f67add62cf..7284597fa9 100644 --- a/.gitignore +++ b/.gitignore @@ -45,6 +45,7 @@ plugins/cura-big-flame-graph plugins/cura-siemensnx-plugin plugins/CuraVariSlicePlugin plugins/CuraLiveScriptingPlugin +plugins/CuraPrintProfileCreator #Build stuff CMakeCache.txt From aa727df525f1f73e3030852189323fe7fdb36560 Mon Sep 17 00:00:00 2001 From: Ruben D Date: Wed, 6 Dec 2017 00:53:43 +0100 Subject: [PATCH 29/62] Correct description of SOLIDWORKS macro The original description was factually incorrect. Fixes #2839. --- plugins/ChangeLogPlugin/ChangeLog.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/ChangeLogPlugin/ChangeLog.txt b/plugins/ChangeLogPlugin/ChangeLog.txt index 673f2ad7c6..de895b3e41 100755 --- a/plugins/ChangeLogPlugin/ChangeLog.txt +++ b/plugins/ChangeLogPlugin/ChangeLog.txt @@ -109,7 +109,7 @@ The build plate now shows graduations of 10 mm and 1 mm for easy model positioni Extruder tabs have become buttons and icons have been updated. *Add an "Export to Cura" button in SOLIDWORKS -SOLIDWORKS plugin can now be installed using an automatic installer. +A macro can be added to your SOLIDWORKS installation that loads your model into Ultimaker Cura. *Siemens NX macro When a user updates models in Siemens NX and clicks the button, the updated models replace the models opened in Ultimaker Cura. From ab5449b01e75bfa2e2834b2efcd7875707d10dcd Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Wed, 6 Dec 2017 09:10:19 +0100 Subject: [PATCH 30/62] Add CuraStage --- cura/Stages/CuraStage.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 cura/Stages/CuraStage.py diff --git a/cura/Stages/CuraStage.py b/cura/Stages/CuraStage.py new file mode 100644 index 0000000000..77d0deb21f --- /dev/null +++ b/cura/Stages/CuraStage.py @@ -0,0 +1,12 @@ +from UM.Stage import Stage + +class CuraStage(Stage): + + def __init__(self): + super().__init__() + + def getMainView(self): + return None + + def getSidebarView(self): + return None From 44c66e2ad269e6a01b8fd731c3a7554995ec8222 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Wed, 6 Dec 2017 09:26:37 +0100 Subject: [PATCH 31/62] Get named view in CuraStage --- cura/Stages/CuraStage.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cura/Stages/CuraStage.py b/cura/Stages/CuraStage.py index 77d0deb21f..52793134cf 100644 --- a/cura/Stages/CuraStage.py +++ b/cura/Stages/CuraStage.py @@ -6,7 +6,7 @@ class CuraStage(Stage): super().__init__() def getMainView(self): - return None + return self.getView("main") def getSidebarView(self): - return None + return self.getView("sidebar") From 840e80a1fb99ed5b1890a3864e4c19fd2ebb8b74 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Wed, 6 Dec 2017 09:29:09 +0100 Subject: [PATCH 32/62] Add missing init --- cura/Stages/__init__.py | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 cura/Stages/__init__.py diff --git a/cura/Stages/__init__.py b/cura/Stages/__init__.py new file mode 100644 index 0000000000..2977645166 --- /dev/null +++ b/cura/Stages/__init__.py @@ -0,0 +1,2 @@ +# Copyright (c) 2017 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. From 84ba486d57bf953b169174b7c6161cc9eb17fe53 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Wed, 6 Dec 2017 09:34:09 +0100 Subject: [PATCH 33/62] Add plugins for prepare and monitor stages --- cura/Stages/CuraStage.py | 3 +++ plugins/MonitorStage/__init__.py | 0 plugins/PrepareStage/PrepareStage.py | 11 +++++++++++ plugins/PrepareStage/__init__.py | 18 ++++++++++++++++++ 4 files changed, 32 insertions(+) create mode 100644 plugins/MonitorStage/__init__.py create mode 100644 plugins/PrepareStage/PrepareStage.py create mode 100644 plugins/PrepareStage/__init__.py diff --git a/cura/Stages/CuraStage.py b/cura/Stages/CuraStage.py index 52793134cf..d4def4c00e 100644 --- a/cura/Stages/CuraStage.py +++ b/cura/Stages/CuraStage.py @@ -1,3 +1,6 @@ +# Copyright (c) 2017 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. + from UM.Stage import Stage class CuraStage(Stage): diff --git a/plugins/MonitorStage/__init__.py b/plugins/MonitorStage/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/plugins/PrepareStage/PrepareStage.py b/plugins/PrepareStage/PrepareStage.py new file mode 100644 index 0000000000..bbd2275d2e --- /dev/null +++ b/plugins/PrepareStage/PrepareStage.py @@ -0,0 +1,11 @@ +# Copyright (c) 2017 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. + +from cura.Stages.CuraStage import CuraStage + + +## Stage for preparing model (slicing). +class PrepareStage(CuraStage): + + def __init__(self): + super().__init__() diff --git a/plugins/PrepareStage/__init__.py b/plugins/PrepareStage/__init__.py new file mode 100644 index 0000000000..d74cf27e39 --- /dev/null +++ b/plugins/PrepareStage/__init__.py @@ -0,0 +1,18 @@ +# Copyright (c) 2017 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. + +from . import PrepareStage + +from UM.i18n import i18nCatalog +i18n_catalog = i18nCatalog("cura") + +def getMetaData(): + return { + "stage": { + "name": i18n_catalog.i18nc("@item:inmenu", "Prepare"), + "weight": 0 + } + } + +def register(app): + return { "stage": PrepareStage.PrepareStage() } From a5b99bd8624c8a2db866ab37c1f31563178993c8 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Wed, 6 Dec 2017 09:36:09 +0100 Subject: [PATCH 34/62] Scaffold monitor stage --- plugins/MonitorStage/MonitorStage.py | 11 +++++++++++ plugins/MonitorStage/__init__.py | 19 +++++++++++++++++++ 2 files changed, 30 insertions(+) create mode 100644 plugins/MonitorStage/MonitorStage.py diff --git a/plugins/MonitorStage/MonitorStage.py b/plugins/MonitorStage/MonitorStage.py new file mode 100644 index 0000000000..e9d8f75645 --- /dev/null +++ b/plugins/MonitorStage/MonitorStage.py @@ -0,0 +1,11 @@ +# Copyright (c) 2017 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. + +from cura.Stages.CuraStage import CuraStage + + +## Stage for monitoring a 3D printing while it's printing. +class MonitorStage(CuraStage): + + def __init__(self): + super().__init__() diff --git a/plugins/MonitorStage/__init__.py b/plugins/MonitorStage/__init__.py index e69de29bb2..c1f7cf0f6b 100644 --- a/plugins/MonitorStage/__init__.py +++ b/plugins/MonitorStage/__init__.py @@ -0,0 +1,19 @@ +# Copyright (c) 2017 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. + +from . import MonitorStage + +from UM.i18n import i18nCatalog +i18n_catalog = i18nCatalog("cura") + +def getMetaData(): + return { + "stage": { + "name": i18n_catalog.i18nc("@item:inmenu", "Monitor"), + "weight": 0, + "icon": "" + } + } + +def register(app): + return { "stage": MonitorStage.MonitorStage() } From f1b9a17611a379ff8a15d701b0fa748573ef35fb Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Wed, 6 Dec 2017 09:44:30 +0100 Subject: [PATCH 35/62] Remove sidebar controller implementation in favor of stages --- cura/CuraApplication.py | 26 ---- resources/qml/Sidebar.qml | 33 +---- resources/qml/Sidebar/PrinterOutput.qml | 65 --------- resources/qml/Sidebar/Settings.qml | 0 resources/qml/Topbar.qml | 174 +++++++++++++----------- 5 files changed, 101 insertions(+), 197 deletions(-) delete mode 100644 resources/qml/Sidebar/PrinterOutput.qml delete mode 100644 resources/qml/Sidebar/Settings.qml diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 997ed7782e..5acaecbdd5 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -76,10 +76,6 @@ from cura.Settings.ContainerManager import ContainerManager from cura.Settings.GlobalStack import GlobalStack from cura.Settings.ExtruderStack import ExtruderStack -from cura.Sidebar.SidebarController import SidebarController -from cura.Sidebar.SidebarControllerProxy import SidebarControllerProxy -from cura.Sidebar.SidebarViewModel import SidebarViewModel - from PyQt5.QtCore import QUrl, pyqtSignal, pyqtProperty, QEvent, Q_ENUMS from UM.FlameProfiler import pyqtSlot from PyQt5.QtGui import QColor, QIcon @@ -200,10 +196,6 @@ class CuraApplication(QtApplication): } ) - ## As of Cura 3.2, the sidebar is controlled by a controller. - # This functionality was added to allow plugins registering custom sidebar views. - self._sidebar_controller = SidebarController(self) - self._currently_loading_files = [] self._non_sliceable_extensions = [] @@ -727,10 +719,6 @@ class CuraApplication(QtApplication): if not run_headless: self.initializeEngine() - # Now that the SidebarViewModel has been created we can set the default sidebar view - # TODO: put this in a more elegant place - self._setDefaultSidebarView() - if run_headless or self._engine.rootObjects: self.closeSplash() @@ -743,11 +731,6 @@ class CuraApplication(QtApplication): self.exec_() - ## Get the SidebarController of this application. - # \returns SidebarControllers \type{SidebarController} - def getSidebarController(self) -> SidebarController: - return self._sidebar_controller - def getMachineManager(self, *args): if self._machine_manager is None: self._machine_manager = MachineManager.createMachineManager() @@ -807,7 +790,6 @@ class CuraApplication(QtApplication): qmlRegisterUncreatableType(CuraApplication, "Cura", 1, 0, "ResourceTypes", "Just an Enum type") - qmlRegisterType(SidebarViewModel, "Cura", 1, 0, "SidebarViewModel") qmlRegisterType(ExtrudersModel, "Cura", 1, 0, "ExtrudersModel") qmlRegisterType(ContainerSettingsModel, "Cura", 1, 0, "ContainerSettingsModel") qmlRegisterSingletonType(ProfilesModel, "Cura", 1, 0, "ProfilesModel", ProfilesModel.createProfilesModel) @@ -819,7 +801,6 @@ class CuraApplication(QtApplication): qmlRegisterType(MachineNameValidator, "Cura", 1, 0, "MachineNameValidator") qmlRegisterType(UserChangesModel, "Cura", 1, 1, "UserChangesModel") qmlRegisterSingletonType(ContainerManager, "Cura", 1, 0, "ContainerManager", ContainerManager.createContainerManager) - qmlRegisterSingletonType(SidebarControllerProxy, "Cura", 1, 0, "SidebarController", SidebarControllerProxy.createSidebarControllerProxy) # As of Qt5.7, it is necessary to get rid of any ".." in the path for the singleton to work. actions_url = QUrl.fromLocalFile(os.path.abspath(Resources.getPath(CuraApplication.ResourceTypes.QmlFiles, "Actions.qml"))) @@ -1318,13 +1299,6 @@ class CuraApplication(QtApplication): def _addProfileWriter(self, profile_writer): pass - ## Set the default sidebar view to "default" - def _setDefaultSidebarView(self): - preferences = Preferences.getInstance() - preferences.addPreference("cura/active_sidebar_view", "default") - active_sidebar_view = preferences.getValue("cura/active_sidebar_view") - self._sidebar_controller.setActiveSidebarView(active_sidebar_view) - @pyqtSlot("QSize") def setMinimumWindowSize(self, size): self.getMainWindow().setMinimumSize(size) diff --git a/resources/qml/Sidebar.qml b/resources/qml/Sidebar.qml index d5af3e939b..3bfc803a78 100644 --- a/resources/qml/Sidebar.qml +++ b/resources/qml/Sidebar.qml @@ -90,7 +90,7 @@ Rectangle SidebarHeader { id: header width: parent.width - visible: (machineExtruderCount.properties.value > 1 || Cura.MachineManager.hasMaterials || Cura.MachineManager.hasVariants) && Cura.SidebarController.activeSidebarId == "default" + visible: machineExtruderCount.properties.value > 1 || Cura.MachineManager.hasMaterials || Cura.MachineManager.hasVariantst onShowTooltip: base.showTooltip(item, location, text) onHideTooltip: base.hideTooltip() } @@ -98,7 +98,7 @@ Rectangle Rectangle { id: headerSeparator width: parent.width - visible: settingsModeSelection.visible && header.visible && Cura.SidebarController.activeSidebarId == "default" + visible: settingsModeSelection.visible && header.visible height: visible ? UM.Theme.getSize("sidebar_lining").height : 0 color: UM.Theme.getColor("sidebar_lining") anchors.top: header.bottom @@ -115,7 +115,7 @@ Rectangle width: Math.floor(parent.width * 0.45) font: UM.Theme.getFont("large") color: UM.Theme.getColor("text") - visible: !monitoringPrint && !hideView && Cura.SidebarController.activeSidebarId == "default" + visible: !monitoringPrint && !hideView } Rectangle { @@ -137,7 +137,7 @@ Rectangle } } anchors.topMargin: UM.Theme.getSize("sidebar_margin").height - visible: !monitoringPrint && !hideSettings && !hideView && Cura.SidebarController.activeSidebarId == "default" + visible: !monitoringPrint && !hideSettings && !hideView Component{ id: wizardDelegate Button { @@ -218,7 +218,7 @@ Rectangle anchors.topMargin: UM.Theme.getSize("sidebar_margin").height anchors.left: base.left anchors.right: base.right - visible: !monitoringPrint && !hideSettings && Cura.SidebarController.activeSidebarId == "default" + visible: !monitoringPrint && !hideSettings delegate: StackViewDelegate { @@ -249,29 +249,6 @@ Rectangle } } - // The sidebarRepeater exposes sidebar views provided by plugins. - // Whenever a plugin sidebar view is active (e.g. not "default"), that sidebar view is shown. - Repeater - { - id: sidebarRepeater - - model: Cura.SidebarViewModel { } - - delegate: Loader - { - id: delegate - asynchronous: true - visible: model.active - - // dynamically get the component from the sidebar controller or set the default sidebar - sourceComponent: { - if (model.id !== "default") { - return Cura.SidebarController.getSidebarComponent(model.id) - } - } - } - } - Loader { id: controlItem diff --git a/resources/qml/Sidebar/PrinterOutput.qml b/resources/qml/Sidebar/PrinterOutput.qml deleted file mode 100644 index 5506944154..0000000000 --- a/resources/qml/Sidebar/PrinterOutput.qml +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright (c) 2017 Ultimaker B.V. -// Cura is released under the terms of the LGPLv3 or higher. - -import QtQuick 2.2 -import QtQuick.Controls 1.1 -import QtQuick.Controls.Styles 1.1 -import QtQuick.Layouts 1.1 - -import UM 1.2 as UM -import Cura 1.0 as Cura - -Item -{ - id: printerOutputSection - - UM.I18nCatalog { - id: catalog - name: "cura" - } - - color: UM.Theme.getColor("sidebar") - - // status - property bool isMonitoring: false - - // printer data - property bool printerConnected: Cura.MachineManager.printerOutputDevices.length != 0 - property bool printerAcceptsCommands: printerConnected && Cura.MachineManager.printerOutputDevices[0].acceptsCommands - property var connectedPrinter: Cura.MachineManager.printerOutputDevices.length >= 1 ? Cura.MachineManager.printerOutputDevices[0] : null - property int backendState: UM.Backend.state - - // print job data - property variant printDuration: PrintInformation.currentPrintTime - property variant printMaterialLengths: PrintInformation.materialLengths - property variant printMaterialWeights: PrintInformation.materialWeights - property variant printMaterialCosts: PrintInformation.materialCosts - property variant printMaterialNames: PrintInformation.materialNames - - // helper function for padding pretty time - function strPadLeft(string, pad, length) { - return (new Array(length + 1).join(pad) + string).slice(-length); - } - - // convert a timestamp to a human readable pretty time - function getPrettyTime (time) { - var hours = Math.floor(time / 3600) - time -= hours * 3600 - var minutes = Math.floor(time / 60) - time -= minutes * 60 - var seconds = Math.floor(time) - var finalTime = strPadLeft(hours, "0", 2) + ":" + strPadLeft(minutes, "0", 2) + ":" + strPadLeft(seconds, "0", 2) - return finalTime - } - - // TODO: change name of this component - MonitorButton - { - id: monitorButton - implicitWidth: base.width - anchors.top: footerSeparator.bottom - anchors.topMargin: UM.Theme.getSize("sidebar_margin").height - anchors.bottom: parent.bottom - visible: monitoringPrint - } -} diff --git a/resources/qml/Sidebar/Settings.qml b/resources/qml/Sidebar/Settings.qml deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/resources/qml/Topbar.qml b/resources/qml/Topbar.qml index db9abafb5c..f2d053ce70 100644 --- a/resources/qml/Topbar.qml +++ b/resources/qml/Topbar.qml @@ -67,91 +67,109 @@ Rectangle anchors.rightMargin: UM.Theme.getSize("default_margin").width spacing: UM.Theme.getSize("default_margin").width - Button + // The topbar is dynamically filled with all available stages + Repeater { - id: showSettings - height: UM.Theme.getSize("sidebar_header").height - text: catalog.i18nc("@title:tab", "Prepare") - checkable: true - checked: isChecked() - exclusiveGroup: sidebarHeaderBarGroup - style: UM.Theme.styles.topbar_header_tab + id: stagesMenu - // We use a Qt.binding to re-bind the checkbox state after manually setting it - // https://stackoverflow.com/questions/38798450/qt-5-7-qml-why-are-my-checkbox-property-bindings-disappearing - onClicked: { - base.stopMonitoringPrint() - checked = Qt.binding(isChecked) - } + model: UM.StageModel{} - function isChecked () { - return !base.monitoringPrint - } - - property color overlayColor: "transparent" - property string overlayIconSource: "" - } - - Button - { - id: showMonitor - width: UM.Theme.getSize("topbar_button").width - height: UM.Theme.getSize("sidebar_header").height - text: catalog.i18nc("@title:tab", "Monitor") - checkable: true - checked: isChecked() - exclusiveGroup: sidebarHeaderBarGroup - style: UM.Theme.styles.topbar_header_tab_no_overlay - - // We use a Qt.binding to re-bind the checkbox state after manually setting it - // https://stackoverflow.com/questions/38798450/qt-5-7-qml-why-are-my-checkbox-property-bindings-disappearing - onClicked: { - base.startMonitoringPrint() - checked = Qt.binding(isChecked) - } - - function isChecked () { - return base.monitoringPrint - } - - property string iconSource: + delegate: Button { - if (!printerConnected) - { - return UM.Theme.getIcon("tab_status_unknown"); - } - else if (!printerAcceptsCommands) - { - return UM.Theme.getIcon("tab_status_unknown"); - } - - if (Cura.MachineManager.printerOutputDevices[0].printerState == "maintenance") - { - return UM.Theme.getIcon("tab_status_busy"); - } - - switch (Cura.MachineManager.printerOutputDevices[0].jobState) - { - case "printing": - case "pre_print": - case "pausing": - case "resuming": - return UM.Theme.getIcon("tab_status_busy"); - case "wait_cleanup": - return UM.Theme.getIcon("tab_status_finished"); - case "ready": - case "": - return UM.Theme.getIcon("tab_status_connected") - case "paused": - return UM.Theme.getIcon("tab_status_paused") - case "error": - return UM.Theme.getIcon("tab_status_stopped") - default: - return UM.Theme.getIcon("tab_status_unknown") - } + text: model.name + checkable: true + checked: model.active + exclusiveGroup: sidebarHeaderBarGroup + style: UM.Theme.styles.topbar_header_tab + height: UM.Theme.getSize("sidebar_header").height } } +// Button +// { +// id: showSettings +// height: UM.Theme.getSize("sidebar_header").height +// text: catalog.i18nc("@title:tab", "Prepare") +// checkable: true +// checked: isChecked() +// exclusiveGroup: sidebarHeaderBarGroup +// style: UM.Theme.styles.topbar_header_tab +// +// // We use a Qt.binding to re-bind the checkbox state after manually setting it +// // https://stackoverflow.com/questions/38798450/qt-5-7-qml-why-are-my-checkbox-property-bindings-disappearing +// onClicked: { +// base.stopMonitoringPrint() +// checked = Qt.binding(isChecked) +// } +// +// function isChecked () { +// return !base.monitoringPrint +// } +// +// property color overlayColor: "transparent" +// property string overlayIconSource: "" +// } + +// Button +// { +// id: showMonitor +// width: UM.Theme.getSize("topbar_button").width +// height: UM.Theme.getSize("sidebar_header").height +// text: catalog.i18nc("@title:tab", "Monitor") +// checkable: true +// checked: isChecked() +// exclusiveGroup: sidebarHeaderBarGroup +// style: UM.Theme.styles.topbar_header_tab_no_overlay +// +// // We use a Qt.binding to re-bind the checkbox state after manually setting it +// // https://stackoverflow.com/questions/38798450/qt-5-7-qml-why-are-my-checkbox-property-bindings-disappearing +// onClicked: { +// base.startMonitoringPrint() +// checked = Qt.binding(isChecked) +// } +// +// function isChecked () { +// return base.monitoringPrint +// } +// +// property string iconSource: +// { +// if (!printerConnected) +// { +// return UM.Theme.getIcon("tab_status_unknown"); +// } +// else if (!printerAcceptsCommands) +// { +// return UM.Theme.getIcon("tab_status_unknown"); +// } +// +// if (Cura.MachineManager.printerOutputDevices[0].printerState == "maintenance") +// { +// return UM.Theme.getIcon("tab_status_busy"); +// } +// +// switch (Cura.MachineManager.printerOutputDevices[0].jobState) +// { +// case "printing": +// case "pre_print": +// case "pausing": +// case "resuming": +// return UM.Theme.getIcon("tab_status_busy"); +// case "wait_cleanup": +// return UM.Theme.getIcon("tab_status_finished"); +// case "ready": +// case "": +// return UM.Theme.getIcon("tab_status_connected") +// case "paused": +// return UM.Theme.getIcon("tab_status_paused") +// case "error": +// return UM.Theme.getIcon("tab_status_stopped") +// default: +// return UM.Theme.getIcon("tab_status_unknown") +// } +// } +// } + ExclusiveGroup { id: sidebarHeaderBarGroup } } From 9b4b6b2eaeb986c7592f59cea6e8c8bac1806c0f Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Wed, 6 Dec 2017 09:44:59 +0100 Subject: [PATCH 36/62] Remove sidebar views from views menu --- resources/qml/Menus/ViewMenu.qml | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/resources/qml/Menus/ViewMenu.qml b/resources/qml/Menus/ViewMenu.qml index b5e4c5765f..a610bb0009 100644 --- a/resources/qml/Menus/ViewMenu.qml +++ b/resources/qml/Menus/ViewMenu.qml @@ -30,25 +30,6 @@ Menu } ExclusiveGroup { id: group } - MenuSeparator {} - - // sidebar views - Instantiator - { - model: Cura.SidebarViewModel{} - MenuItem - { - text: model.name - checkable: true - checked: model.active - exclusiveGroup: sidebarGroup - onTriggered: Cura.SidebarController.setActiveSidebarView(model.id) - } - onObjectAdded: menu.insertItem(index, object) - onObjectRemoved: menu.removeItem(object) - } - ExclusiveGroup { id: sidebarGroup } - MenuSeparator {} MenuItem { action: Cura.Actions.homeCamera; } } From 0e1c9146cfa62209d09b71424bc8831e5f2a6ab6 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Wed, 6 Dec 2017 09:58:50 +0100 Subject: [PATCH 37/62] Implement stage model in top bar --- cura/CuraApplication.py | 7 +++++-- plugins/MonitorStage/__init__.py | 2 +- plugins/MonitorStage/plugin.json | 8 ++++++++ plugins/PrepareStage/plugin.json | 8 ++++++++ resources/qml/Topbar.qml | 1 + 5 files changed, 23 insertions(+), 3 deletions(-) create mode 100644 plugins/MonitorStage/plugin.json create mode 100644 plugins/PrepareStage/plugin.json diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 5acaecbdd5..c83ed04c82 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -229,7 +229,9 @@ class CuraApplication(QtApplication): "TranslateTool", "FileLogger", "XmlMaterialProfile", - "PluginBrowser" + "PluginBrowser", + "PrepareStage", + "MonitorStage" ]) self._physics = None self._volume = None @@ -668,13 +670,14 @@ class CuraApplication(QtApplication): controller = self.getController() + controller.setActiveStage("PrepareStage") controller.setActiveView("SolidView") controller.setCameraTool("CameraTool") controller.setSelectionTool("SelectionTool") t = controller.getTool("TranslateTool") if t: - t.setEnabledAxis([ToolHandle.XAxis, ToolHandle.YAxis,ToolHandle.ZAxis]) + t.setEnabledAxis([ToolHandle.XAxis, ToolHandle.YAxis, ToolHandle.ZAxis]) Selection.selectionChanged.connect(self.onSelectionChanged) diff --git a/plugins/MonitorStage/__init__.py b/plugins/MonitorStage/__init__.py index c1f7cf0f6b..e18ecba7f9 100644 --- a/plugins/MonitorStage/__init__.py +++ b/plugins/MonitorStage/__init__.py @@ -10,7 +10,7 @@ def getMetaData(): return { "stage": { "name": i18n_catalog.i18nc("@item:inmenu", "Monitor"), - "weight": 0, + "weight": 1, "icon": "" } } diff --git a/plugins/MonitorStage/plugin.json b/plugins/MonitorStage/plugin.json new file mode 100644 index 0000000000..cb3f55a80d --- /dev/null +++ b/plugins/MonitorStage/plugin.json @@ -0,0 +1,8 @@ +{ + "name": "Monitor Stage", + "author": "Ultimaker B.V.", + "version": "1.0.0", + "description": "Provides a monitor stage in Cura.", + "api": 4, + "i18n-catalog": "cura" +} \ No newline at end of file diff --git a/plugins/PrepareStage/plugin.json b/plugins/PrepareStage/plugin.json new file mode 100644 index 0000000000..4fd55e955e --- /dev/null +++ b/plugins/PrepareStage/plugin.json @@ -0,0 +1,8 @@ +{ + "name": "Prepare Stage", + "author": "Ultimaker B.V.", + "version": "1.0.0", + "description": "Provides a prepare stage in Cura.", + "api": 4, + "i18n-catalog": "cura" +} \ No newline at end of file diff --git a/resources/qml/Topbar.qml b/resources/qml/Topbar.qml index f2d053ce70..50e90a9a37 100644 --- a/resources/qml/Topbar.qml +++ b/resources/qml/Topbar.qml @@ -82,6 +82,7 @@ Rectangle exclusiveGroup: sidebarHeaderBarGroup style: UM.Theme.styles.topbar_header_tab height: UM.Theme.getSize("sidebar_header").height + onClicked: UM.Controller.setActiveStage(model.id) } } From c0a502f99c637898a59721907e2c824356d48ead Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Wed, 6 Dec 2017 11:47:31 +0100 Subject: [PATCH 38/62] Check if there is an active machine before doing anything else CURA-4680 --- cura/Settings/MachineManager.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index f1bb8b6648..253c5f793c 100755 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -498,6 +498,11 @@ class MachineManager(QObject): @pyqtProperty("QVariantList", notify=activeVariantChanged) def activeVariantNames(self) -> List[str]: result = [] + + # it can happen when there is no active machine + if self._global_container_stack is None: + return result + active_stacks = ExtruderManager.getInstance().getActiveGlobalAndExtruderStacks() if active_stacks is not None: for stack in active_stacks: @@ -510,6 +515,11 @@ class MachineManager(QObject): @pyqtProperty("QVariantList", notify = activeMaterialChanged) def activeMaterialNames(self) -> List[str]: result = [] + + # it can happen when there is no active machine + if self._global_container_stack is None: + return result + active_stacks = ExtruderManager.getInstance().getActiveGlobalAndExtruderStacks() if active_stacks is not None: for stack in active_stacks: @@ -530,6 +540,11 @@ class MachineManager(QObject): @pyqtProperty("QVariantMap", notify = activeVariantChanged) def allActiveVariantIds(self) -> Dict[str, str]: result = {} + + # it can happen when there is no active machine + if self._global_container_stack is None: + return result + active_stacks = ExtruderManager.getInstance().getActiveExtruderStacks() if active_stacks is not None: #If we have a global stack. for stack in active_stacks: @@ -548,6 +563,11 @@ class MachineManager(QObject): @pyqtProperty("QVariantMap", notify = activeMaterialChanged) def allActiveMaterialIds(self) -> Dict[str, str]: result = {} + + # it can happen when there is no active machine + if self._global_container_stack is None: + return result + active_stacks = ExtruderManager.getInstance().getActiveExtruderStacks() result[self._global_container_stack.getId()] = self._global_container_stack.material.getId() From a57a5aab6b6fcd44a3e1dd65988600e899e1c0ff Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Wed, 6 Dec 2017 12:23:41 +0100 Subject: [PATCH 39/62] Make sidebar view working for active stage --- cura/CuraApplication.py | 1 + cura/Sidebar/SidebarController.py | 66 -- cura/Sidebar/SidebarControllerProxy.py | 41 - cura/Sidebar/SidebarView.py | 35 - cura/Sidebar/SidebarViewModel.py | 66 -- cura/Sidebar/__init__.py | 0 cura/Stages/CuraStage.py | 11 +- plugins/MonitorStage/MonitorStage.py | 6 +- plugins/MonitorStage/__init__.py | 7 +- plugins/PrepareStage/PrepareStage.py | 10 +- plugins/PrepareStage/__init__.py | 4 +- resources/qml/Cura.qml | 6 +- resources/qml/Sidebar.qml | 1133 ++++++++++++------------ resources/qml/Topbar.qml | 31 +- 14 files changed, 605 insertions(+), 812 deletions(-) delete mode 100644 cura/Sidebar/SidebarController.py delete mode 100644 cura/Sidebar/SidebarControllerProxy.py delete mode 100644 cura/Sidebar/SidebarView.py delete mode 100644 cura/Sidebar/SidebarViewModel.py delete mode 100644 cura/Sidebar/__init__.py diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index c83ed04c82..eec6c7f503 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -721,6 +721,7 @@ class CuraApplication(QtApplication): run_headless = self.getCommandLineOption("headless", False) if not run_headless: self.initializeEngine() + controller.setActiveStage("PrepareStage") if run_headless or self._engine.rootObjects: self.closeSplash() diff --git a/cura/Sidebar/SidebarController.py b/cura/Sidebar/SidebarController.py deleted file mode 100644 index a40ee07157..0000000000 --- a/cura/Sidebar/SidebarController.py +++ /dev/null @@ -1,66 +0,0 @@ -# Copyright (c) 2017 Ultimaker B.V. -from UM.Logger import Logger -from UM.Preferences import Preferences -from UM.Signal import Signal -from UM.PluginRegistry import PluginRegistry - -# The sidebar controller manages available sidebar components and decides which one to display. -# The cura.qml file uses this controller to repeat over the sidebars and show the active index. -class SidebarController: - - def __init__(self, application): - self._application = application - self._sidebar_views = {"default": {}} # default is needed for the default settings sidebar - self._active_sidebar_view = None - - # Register the sidebar_view plugin type so plugins can expose custom sidebar views. - PluginRegistry.addType("sidebar_view", self.addSidebarView) - - ## Emitted when the list of views changes. - sidebarViewsChanged = Signal() - - ## Emitted when the active view changes. - activeSidebarViewChanged = Signal() - - ## Get the active application instance. - def getApplication(self): - return self._application - - ## Get all sidebar views registered in this controller. - def getAllSidebarViews(self): - return self._sidebar_views - - ## Add a sidebar view to the registry. - # It get's a unique name based on the plugin ID. - def addSidebarView(self, sidebar_view): - sidebar_view_id = sidebar_view.getPluginId() - if sidebar_view_id not in self._sidebar_views: - self._sidebar_views[sidebar_view_id] = sidebar_view - self.sidebarViewsChanged.emit() - - ## Get a registered sidebar view by name. - # The name is the ID of the plugin that registered the view. - def getSidebarView(self, name: str): - try: - return self._sidebar_views[name] - except KeyError: - Logger.log("e", "Unable to find %s in sidebar view list", name) - return None - - ## Get the active sidebar view. - def getActiveSidebarView(self): - return self._active_sidebar_view - - ## Get the ID of the active sidebar view. - def getActiveSidebarViewId(self): - if self._active_sidebar_view: - if hasattr(self._active_sidebar_view, "getPluginId"): - return self._active_sidebar_view.getPluginId() - return "default" - - ## Change the active sidebar view to one of the registered views. - def setActiveSidebarView(self, sidebar_view_id: str): - if sidebar_view_id in self._sidebar_views: - self._active_sidebar_view = self._sidebar_views[sidebar_view_id] - Preferences.getInstance().setValue("cura/active_sidebar_view", sidebar_view_id) - self.activeSidebarViewChanged.emit() diff --git a/cura/Sidebar/SidebarControllerProxy.py b/cura/Sidebar/SidebarControllerProxy.py deleted file mode 100644 index 00a898301a..0000000000 --- a/cura/Sidebar/SidebarControllerProxy.py +++ /dev/null @@ -1,41 +0,0 @@ -# Copyright (c) 2017 Ultimaker B.V. -from PyQt5.QtCore import QObject, pyqtSlot, QUrl, pyqtProperty, pyqtSignal -from UM.Application import Application - - -## The sidebar controller proxy acts a proxy between the sidebar controller and the QMl context of the controller. -from UM.Logger import Logger - - -class SidebarControllerProxy(QObject): - - def __init__(self, parent = None): - super().__init__(parent) - self._controller = Application.getInstance().getSidebarController() - self._controller.activeSidebarViewChanged.connect(self._onActiveSidebarViewChanged) - - activeSidebarViewChanged = pyqtSignal() - - @classmethod - def createSidebarControllerProxy(self, engine, script_engine): - return SidebarControllerProxy() - - @pyqtProperty(str, notify = activeSidebarViewChanged) - def activeSidebarId(self): - return self._controller.getActiveSidebarViewId() - - @pyqtSlot(str) - def setActiveSidebarView(self, sidebar_view_id): - Logger.log("d", "Setting active sidebar view to %s", sidebar_view_id) - self._controller.setActiveSidebarView(sidebar_view_id) - - @pyqtSlot(str, result = QObject) - def getSidebarComponent(self, sidebar_id): - return self._controller.getSidebarView(sidebar_id).getComponent() - - @pyqtSlot(str, result = QUrl) - def getSidebarComponentPath(self, sidebar_id): - return self._controller.getSidebarView(sidebar_id).getComponentPath() - - def _onActiveSidebarViewChanged(self): - self.activeSidebarViewChanged.emit() diff --git a/cura/Sidebar/SidebarView.py b/cura/Sidebar/SidebarView.py deleted file mode 100644 index d5c20e0858..0000000000 --- a/cura/Sidebar/SidebarView.py +++ /dev/null @@ -1,35 +0,0 @@ -# Copyright (c) 2017 Ultimaker B.V. -import os.path - -from UM.Application import Application -from UM.Logger import Logger -from UM.PluginObject import PluginObject -from UM.PluginRegistry import PluginRegistry - -# Abstract class for sidebar view objects. -# By default the sidebar is Cura's settings, slicing and printing overview. -# The last plugin to claim the sidebar QML target will be displayed. -class SidebarView(PluginObject): - - def __init__(self): - super().__init__() - self._view = None - - def getComponent(self): - if not self._view: - self.createView() - return self._view - - def createView(self): - component_path = self.getComponentPath() - self._view = Application.getInstance().createQmlComponent(component_path, {"manager": self}) - - ## Get the path to the component QML file as QUrl - def getComponentPath(self): - try: - plugin_path = PluginRegistry.getInstance().getPluginPath(self.getPluginId()) - sidebar_component_file_path = PluginRegistry.getInstance().getMetaData(self.getPluginId())["sidebar_view"]["sidebar_component"] - return os.path.join(plugin_path, sidebar_component_file_path) - except KeyError: - Logger.log("w", "Could not find sidebar component QML file for %s", self.getPluginId()) - return "" diff --git a/cura/Sidebar/SidebarViewModel.py b/cura/Sidebar/SidebarViewModel.py deleted file mode 100644 index 8000d87cc5..0000000000 --- a/cura/Sidebar/SidebarViewModel.py +++ /dev/null @@ -1,66 +0,0 @@ -# Copyright (c) 2017 Ultimaker B.V. -from PyQt5.QtCore import Qt -from UM.Qt.ListModel import ListModel -from UM.Application import Application -from UM.PluginRegistry import PluginRegistry - -## The SidebarViewModel is the default sidebar view in Cura with all the print settings and print button. -class SidebarViewModel(ListModel): - IdRole = Qt.UserRole + 1 - NameRole = Qt.UserRole + 2 - ActiveRole = Qt.UserRole + 3 - - def __init__(self, parent = None): - super().__init__(parent) - - # connect views changed signals - self._controller = Application.getInstance().getSidebarController() - self._controller.sidebarViewsChanged.connect(self._onSidebarViewsChanged) - self._controller.activeSidebarViewChanged.connect(self._onSidebarViewsChanged) - - # register Qt list roles - self.addRoleName(self.IdRole, "id") - self.addRoleName(self.NameRole, "name") - self.addRoleName(self.ActiveRole, "active") - - ## Update the model when new views are added or another view is made the active view. - def _onSidebarViewsChanged(self): - items = [] - current_view_id = "default" - - sidebar_views = self._controller.getAllSidebarViews() - current_view = self._controller.getActiveSidebarView() - if current_view and hasattr(current_view, "getPluginId"): - current_view_id = current_view.getPluginId() - - for sidebar_view_id, sidebar_view in sidebar_views.items(): - - # Override fields for default settings sidebar - if sidebar_view_id == "default": - items.append({ - "id": "default", - "name": "Print settings sidebar", - "active": sidebar_view_id == current_view_id, - "weight": 0 - }) - continue - - sidebar_view_metadata = PluginRegistry.getInstance().getMetaData(sidebar_view_id).get("sidebar_view", {}) - - # Skip view modes that are marked as not visible - if "visible" in sidebar_view_metadata and not sidebar_view_metadata["visible"]: - continue - - name = sidebar_view_metadata.get("name", sidebar_view_id) - weight = sidebar_view_metadata.get("weight", 1) - - items.append({ - "id": sidebar_view_id, - "name": name, - "active": sidebar_view_id == current_view_id, - "weight": weight - }) - - # Sort the views by weight - items.sort(key=lambda t: t["weight"]) - self.setItems(items) diff --git a/cura/Sidebar/__init__.py b/cura/Sidebar/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/cura/Stages/CuraStage.py b/cura/Stages/CuraStage.py index d4def4c00e..6d4e56473d 100644 --- a/cura/Stages/CuraStage.py +++ b/cura/Stages/CuraStage.py @@ -1,5 +1,6 @@ # Copyright (c) 2017 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. +from PyQt5.QtCore import pyqtProperty, QObject from UM.Stage import Stage @@ -8,8 +9,10 @@ class CuraStage(Stage): def __init__(self): super().__init__() - def getMainView(self): - return self.getView("main") + @pyqtProperty(QObject, constant = True) + def mainComponent(self): + return self.getDisplayComponent("main") - def getSidebarView(self): - return self.getView("sidebar") + @pyqtProperty(QObject, constant = True) + def sidebarComponent(self): + return self.getDisplayComponent("sidebar") diff --git a/plugins/MonitorStage/MonitorStage.py b/plugins/MonitorStage/MonitorStage.py index e9d8f75645..8b98f2872c 100644 --- a/plugins/MonitorStage/MonitorStage.py +++ b/plugins/MonitorStage/MonitorStage.py @@ -1,6 +1,6 @@ # Copyright (c) 2017 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. - +from UM.Application import Application from cura.Stages.CuraStage import CuraStage @@ -9,3 +9,7 @@ class MonitorStage(CuraStage): def __init__(self): super().__init__() + Application.getInstance().engineCreatedSignal.connect(self._engineCreated) + + def _engineCreated(self): + self.setIconSource(Application.getInstance().getTheme().getIcon("tab_status_connected")) diff --git a/plugins/MonitorStage/__init__.py b/plugins/MonitorStage/__init__.py index e18ecba7f9..884d43a8af 100644 --- a/plugins/MonitorStage/__init__.py +++ b/plugins/MonitorStage/__init__.py @@ -10,10 +10,11 @@ def getMetaData(): return { "stage": { "name": i18n_catalog.i18nc("@item:inmenu", "Monitor"), - "weight": 1, - "icon": "" + "weight": 1 } } def register(app): - return { "stage": MonitorStage.MonitorStage() } + return { + "stage": MonitorStage.MonitorStage() + } diff --git a/plugins/PrepareStage/PrepareStage.py b/plugins/PrepareStage/PrepareStage.py index bbd2275d2e..63086b3e93 100644 --- a/plugins/PrepareStage/PrepareStage.py +++ b/plugins/PrepareStage/PrepareStage.py @@ -1,6 +1,8 @@ # Copyright (c) 2017 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. - +import os.path +from UM.Application import Application +from UM.Resources import Resources from cura.Stages.CuraStage import CuraStage @@ -9,3 +11,9 @@ class PrepareStage(CuraStage): def __init__(self): super().__init__() + Application.getInstance().engineCreatedSignal.connect(self._engineCreated) + + def _engineCreated(self): + sidebar_component_path = os.path.join(Resources.getPath(Application.getInstance().ResourceTypes.QmlFiles), "Sidebar.qml") + sidebar_component = Application.getInstance().createQmlComponent(sidebar_component_path) + self.addDisplayComponent("sidebar", sidebar_component) diff --git a/plugins/PrepareStage/__init__.py b/plugins/PrepareStage/__init__.py index d74cf27e39..f085d788f9 100644 --- a/plugins/PrepareStage/__init__.py +++ b/plugins/PrepareStage/__init__.py @@ -15,4 +15,6 @@ def getMetaData(): } def register(app): - return { "stage": PrepareStage.PrepareStage() } + return { + "stage": PrepareStage.PrepareStage() + } diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index bf67efdba3..3c9ca25b05 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -366,8 +366,7 @@ UM.MainWindow onStopMonitoringPrint: base.showPrintMonitor = false } - Sidebar - { + Loader { id: sidebar anchors @@ -379,7 +378,8 @@ UM.MainWindow width: UM.Theme.getSize("sidebar").width z: 1 - monitoringPrint: base.showPrintMonitor + + sourceComponent: UM.Controller.activeStage.sidebarComponent } Rectangle diff --git a/resources/qml/Sidebar.qml b/resources/qml/Sidebar.qml index 3bfc803a78..7429ef26df 100644 --- a/resources/qml/Sidebar.qml +++ b/resources/qml/Sidebar.qml @@ -10,603 +10,606 @@ import UM 1.2 as UM import Cura 1.0 as Cura import "Menus" -Rectangle +Component { - id: base; - - property int currentModeIndex; - property bool hideSettings: PrintInformation.preSliced - property bool hideView: Cura.MachineManager.activeMachineName == "" - - // Is there an output device for this printer? - property bool printerConnected: Cura.MachineManager.printerOutputDevices.length != 0 - property bool printerAcceptsCommands: printerConnected && Cura.MachineManager.printerOutputDevices[0].acceptsCommands - property var connectedPrinter: Cura.MachineManager.printerOutputDevices.length >= 1 ? Cura.MachineManager.printerOutputDevices[0] : null - property int backendState: UM.Backend.state - - property bool monitoringPrint: false - - property variant printDuration: PrintInformation.currentPrintTime - property variant printMaterialLengths: PrintInformation.materialLengths - property variant printMaterialWeights: PrintInformation.materialWeights - property variant printMaterialCosts: PrintInformation.materialCosts - property variant printMaterialNames: PrintInformation.materialNames - - color: UM.Theme.getColor("sidebar") - UM.I18nCatalog { id: catalog; name:"cura"} - - Timer { - id: tooltipDelayTimer - interval: 500 - repeat: false - property var item - property string text - - onTriggered: - { - base.showTooltip(base, {x: 0, y: item.y}, text); - } - } - - function showTooltip(item, position, text) - { - tooltip.text = text; - position = item.mapToItem(base, position.x - UM.Theme.getSize("default_arrow").width, position.y); - tooltip.show(position); - } - - function hideTooltip() - { - tooltip.hide(); - } - - function strPadLeft(string, pad, length) { - return (new Array(length + 1).join(pad) + string).slice(-length); - } - - function getPrettyTime(time) - { - var hours = Math.floor(time / 3600) - time -= hours * 3600 - var minutes = Math.floor(time / 60); - time -= minutes * 60 - var seconds = Math.floor(time); - - var finalTime = strPadLeft(hours, "0", 2) + ':' + strPadLeft(minutes,'0',2)+ ':' + strPadLeft(seconds,'0',2); - return finalTime; - } - - MouseArea - { - anchors.fill: parent - acceptedButtons: Qt.AllButtons; - - onWheel: - { - wheel.accepted = true; - } - } - - SidebarHeader { - id: header - width: parent.width - visible: machineExtruderCount.properties.value > 1 || Cura.MachineManager.hasMaterials || Cura.MachineManager.hasVariantst - onShowTooltip: base.showTooltip(item, location, text) - onHideTooltip: base.hideTooltip() - } - - Rectangle { - id: headerSeparator - width: parent.width - visible: settingsModeSelection.visible && header.visible - height: visible ? UM.Theme.getSize("sidebar_lining").height : 0 - color: UM.Theme.getColor("sidebar_lining") - anchors.top: header.bottom - anchors.topMargin: visible ? UM.Theme.getSize("sidebar_margin").height : 0 - } - - Label { - id: settingsModeLabel - text: !hideSettings ? catalog.i18nc("@label:listbox", "Print Setup") : catalog.i18nc("@label:listbox","Print Setup disabled\nG-code files cannot be modified"); - anchors.left: parent.left - anchors.leftMargin: UM.Theme.getSize("sidebar_margin").width - anchors.top: headerSeparator.bottom - anchors.topMargin: UM.Theme.getSize("sidebar_margin").height - width: Math.floor(parent.width * 0.45) - font: UM.Theme.getFont("large") - color: UM.Theme.getColor("text") - visible: !monitoringPrint && !hideView - } - - Rectangle { - id: settingsModeSelection - color: "transparent" - width: Math.floor(parent.width * 0.55) - height: UM.Theme.getSize("sidebar_header_mode_toggle").height - anchors.right: parent.right - anchors.rightMargin: UM.Theme.getSize("sidebar_margin").width - anchors.top: - { - if (settingsModeLabel.contentWidth >= parent.width - width - UM.Theme.getSize("sidebar_margin").width * 2) - { - return settingsModeLabel.bottom; - } - else - { - return headerSeparator.bottom; - } - } - anchors.topMargin: UM.Theme.getSize("sidebar_margin").height - visible: !monitoringPrint && !hideSettings && !hideView - Component{ - id: wizardDelegate - Button { - height: settingsModeSelection.height - anchors.left: parent.left - anchors.leftMargin: model.index * Math.floor(settingsModeSelection.width / 2) - anchors.verticalCenter: parent.verticalCenter - width: Math.floor(0.5 * parent.width) - text: model.text - exclusiveGroup: modeMenuGroup; - checkable: true; - checked: base.currentModeIndex == index - onClicked: base.currentModeIndex = index - - onHoveredChanged: { - if (hovered) - { - tooltipDelayTimer.item = settingsModeSelection - tooltipDelayTimer.text = model.tooltipText - tooltipDelayTimer.start(); - } - else - { - tooltipDelayTimer.stop(); - base.hideTooltip(); - } - } - - style: ButtonStyle { - background: Rectangle { - border.width: control.checked ? UM.Theme.getSize("default_lining").width * 2 : UM.Theme.getSize("default_lining").width - border.color: (control.checked || control.pressed) ? UM.Theme.getColor("action_button_active_border") : - control.hovered ? UM.Theme.getColor("action_button_hovered_border") : - UM.Theme.getColor("action_button_border") - color: (control.checked || control.pressed) ? UM.Theme.getColor("action_button_active") : - control.hovered ? UM.Theme.getColor("action_button_hovered") : - UM.Theme.getColor("action_button") - Behavior on color { ColorAnimation { duration: 50; } } - Label { - anchors.left: parent.left - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - anchors.leftMargin: UM.Theme.getSize("default_lining").width * 2 - anchors.rightMargin: UM.Theme.getSize("default_lining").width * 2 - color: (control.checked || control.pressed) ? UM.Theme.getColor("action_button_active_text") : - control.hovered ? UM.Theme.getColor("action_button_hovered_text") : - UM.Theme.getColor("action_button_text") - font: UM.Theme.getFont("default") - text: control.text - horizontalAlignment: Text.AlignHCenter - elide: Text.ElideMiddle - } - } - label: Item { } - } - } - } - ExclusiveGroup { id: modeMenuGroup; } - - ListView - { - id: modesList - property var index: 0 - model: modesListModel - delegate: wizardDelegate - anchors.top: parent.top - anchors.left: parent.left - width: parent.width - } - } - - StackView - { - id: sidebarContents - - anchors.bottom: footerSeparator.top - anchors.top: settingsModeSelection.bottom - anchors.topMargin: UM.Theme.getSize("sidebar_margin").height - anchors.left: base.left - anchors.right: base.right - visible: !monitoringPrint && !hideSettings - - delegate: StackViewDelegate - { - function transitionFinished(properties) - { - properties.exitItem.opacity = 1 - } - - pushTransition: StackViewTransition - { - PropertyAnimation - { - target: enterItem - property: "opacity" - from: 0 - to: 1 - duration: 100 - } - PropertyAnimation - { - target: exitItem - property: "opacity" - from: 1 - to: 0 - duration: 100 - } - } - } - } - - Loader - { - id: controlItem - anchors.bottom: footerSeparator.top - anchors.top: headerSeparator.bottom - anchors.left: base.left - anchors.right: base.right - sourceComponent: - { - if(monitoringPrint && connectedPrinter != null) - { - if(connectedPrinter.controlItem != null) - { - return connectedPrinter.controlItem - } - } - return null - } - } - - Loader - { - anchors.bottom: footerSeparator.top - anchors.top: headerSeparator.bottom - anchors.left: base.left - anchors.right: base.right - source: - { - if(controlItem.sourceComponent == null) - { - if(monitoringPrint) - { - return "PrintMonitor.qml" - } else - { - return "SidebarContents.qml" - } - } - else - { - return "" - } - } - } - Rectangle { - id: footerSeparator - width: parent.width - height: UM.Theme.getSize("sidebar_lining").height - color: UM.Theme.getColor("sidebar_lining") - anchors.bottom: printSpecs.top - anchors.bottomMargin: Math.floor(UM.Theme.getSize("sidebar_margin").height * 2 + UM.Theme.getSize("progressbar").height + UM.Theme.getFont("default_bold").pixelSize) - } + id: base; - Item - { - id: printSpecs - anchors.left: parent.left - anchors.bottom: parent.bottom - anchors.leftMargin: UM.Theme.getSize("sidebar_margin").width - anchors.bottomMargin: UM.Theme.getSize("sidebar_margin").height - height: timeDetails.height + costSpec.height - width: base.width - (saveButton.buttonRowWidth + UM.Theme.getSize("sidebar_margin").width) - visible: !monitoringPrint - clip: true + property int currentModeIndex; + property bool hideSettings: PrintInformation.preSliced + property bool hideView: Cura.MachineManager.activeMachineName == "" - Label - { - id: timeDetails - anchors.left: parent.left - anchors.bottom: costSpec.top - font: UM.Theme.getFont("large") - color: UM.Theme.getColor("text_subtext") - text: (!base.printDuration || !base.printDuration.valid) ? catalog.i18nc("@label Hours and minutes", "00h 00min") : base.printDuration.getDisplayString(UM.DurationFormat.Short) + // Is there an output device for this printer? + property bool printerConnected: Cura.MachineManager.printerOutputDevices.length != 0 + property bool printerAcceptsCommands: printerConnected && Cura.MachineManager.printerOutputDevices[0].acceptsCommands + property var connectedPrinter: Cura.MachineManager.printerOutputDevices.length >= 1 ? Cura.MachineManager.printerOutputDevices[0] : null + property int backendState: UM.Backend.state - MouseArea + property bool monitoringPrint: false + + property variant printDuration: PrintInformation.currentPrintTime + property variant printMaterialLengths: PrintInformation.materialLengths + property variant printMaterialWeights: PrintInformation.materialWeights + property variant printMaterialCosts: PrintInformation.materialCosts + property variant printMaterialNames: PrintInformation.materialNames + + color: UM.Theme.getColor("sidebar") + UM.I18nCatalog { id: catalog; name:"cura"} + + Timer { + id: tooltipDelayTimer + interval: 500 + repeat: false + property var item + property string text + + onTriggered: { - id: timeDetailsMouseArea - anchors.fill: parent - hoverEnabled: true - - onEntered: - { - if(base.printDuration.valid && !base.printDuration.isTotalDurationZero) - { - // All the time information for the different features is achieved - var print_time = PrintInformation.getFeaturePrintTimes(); - var total_seconds = parseInt(base.printDuration.getDisplayString(UM.DurationFormat.Seconds)) - - // A message is created and displayed when the user hover the time label - var tooltip_html = "%1
    ".arg(catalog.i18nc("@tooltip", "Time specification")); - for(var feature in print_time) - { - if(!print_time[feature].isTotalDurationZero) - { - tooltip_html += "" + - "".arg(print_time[feature].getDisplayString(UM.DurationFormat.ISO8601).slice(0,-3)) + - "".arg(Math.round(100 * parseInt(print_time[feature].getDisplayString(UM.DurationFormat.Seconds)) / total_seconds)) + - ""; - } - } - tooltip_html += "
    " + feature + ":  %1  %1%
    "; - - base.showTooltip(parent, Qt.point(-UM.Theme.getSize("sidebar_margin").width, 0), tooltip_html); - } - } - onExited: - { - base.hideTooltip(); - } + base.showTooltip(base, {x: 0, y: item.y}, text); } } - Label + function showTooltip(item, position, text) { - function formatRow(items) + tooltip.text = text; + position = item.mapToItem(base, position.x - UM.Theme.getSize("default_arrow").width, position.y); + tooltip.show(position); + } + + function hideTooltip() + { + tooltip.hide(); + } + + function strPadLeft(string, pad, length) { + return (new Array(length + 1).join(pad) + string).slice(-length); + } + + function getPrettyTime(time) + { + var hours = Math.floor(time / 3600) + time -= hours * 3600 + var minutes = Math.floor(time / 60); + time -= minutes * 60 + var seconds = Math.floor(time); + + var finalTime = strPadLeft(hours, "0", 2) + ':' + strPadLeft(minutes,'0',2)+ ':' + strPadLeft(seconds,'0',2); + return finalTime; + } + + MouseArea + { + anchors.fill: parent + acceptedButtons: Qt.AllButtons; + + onWheel: { - var row_html = ""; - for(var item = 0; item < items.length; item++) - { - if (item == 0) - { - row_html += "%1".arg(items[item]); - } - else - { - row_html += "  %1".arg(items[item]); - } - } - row_html += ""; - return row_html; + wheel.accepted = true; } + } - function getSpecsData() - { - var lengths = []; - var total_length = 0; - var weights = []; - var total_weight = 0; - var costs = []; - var total_cost = 0; - var some_costs_known = false; - var names = []; - if(base.printMaterialLengths) - { - for(var index = 0; index < base.printMaterialLengths.length; index++) - { - if(base.printMaterialLengths[index] > 0) - { - names.push(base.printMaterialNames[index]); - lengths.push(base.printMaterialLengths[index].toFixed(2)); - weights.push(String(Math.floor(base.printMaterialWeights[index]))); - var cost = base.printMaterialCosts[index] == undefined ? 0 : base.printMaterialCosts[index].toFixed(2); - costs.push(cost); - if(cost > 0) - { - some_costs_known = true; - } - - total_length += base.printMaterialLengths[index]; - total_weight += base.printMaterialWeights[index]; - total_cost += base.printMaterialCosts[index]; - } - } - } - if(lengths.length == 0) - { - lengths = ["0.00"]; - weights = ["0"]; - costs = ["0.00"]; - } - - var tooltip_html = "%1
    ".arg(catalog.i18nc("@label", "Cost specification")); - for(var index = 0; index < lengths.length; index++) - { - tooltip_html += formatRow([ - "%1:".arg(names[index]), - catalog.i18nc("@label m for meter", "%1m").arg(lengths[index]), - catalog.i18nc("@label g for grams", "%1g").arg(weights[index]), - "%1 %2".arg(UM.Preferences.getValue("cura/currency")).arg(costs[index]), - ]); - } - if(lengths.length > 1) - { - tooltip_html += formatRow([ - catalog.i18nc("@label", "Total:"), - catalog.i18nc("@label m for meter", "%1m").arg(total_length.toFixed(2)), - catalog.i18nc("@label g for grams", "%1g").arg(Math.round(total_weight)), - "%1 %2".arg(UM.Preferences.getValue("cura/currency")).arg(total_cost.toFixed(2)), - ]); - } - tooltip_html += "
    "; - tooltipText = tooltip_html; - - return tooltipText - } - - id: costSpec - anchors.left: parent.left - anchors.bottom: parent.bottom - font: UM.Theme.getFont("very_small") - color: UM.Theme.getColor("text_subtext") - elide: Text.ElideMiddle + SidebarHeader { + id: header width: parent.width - property string tooltipText - text: + visible: machineExtruderCount.properties.value > 1 || Cura.MachineManager.hasMaterials || Cura.MachineManager.hasVariantst + onShowTooltip: base.showTooltip(item, location, text) + onHideTooltip: base.hideTooltip() + } + + Rectangle { + id: headerSeparator + width: parent.width + visible: settingsModeSelection.visible && header.visible + height: visible ? UM.Theme.getSize("sidebar_lining").height : 0 + color: UM.Theme.getColor("sidebar_lining") + anchors.top: header.bottom + anchors.topMargin: visible ? UM.Theme.getSize("sidebar_margin").height : 0 + } + + Label { + id: settingsModeLabel + text: !hideSettings ? catalog.i18nc("@label:listbox", "Print Setup") : catalog.i18nc("@label:listbox","Print Setup disabled\nG-code files cannot be modified"); + anchors.left: parent.left + anchors.leftMargin: UM.Theme.getSize("sidebar_margin").width + anchors.top: headerSeparator.bottom + anchors.topMargin: UM.Theme.getSize("sidebar_margin").height + width: Math.floor(parent.width * 0.45) + font: UM.Theme.getFont("large") + color: UM.Theme.getColor("text") + visible: !monitoringPrint && !hideView + } + + Rectangle { + id: settingsModeSelection + color: "transparent" + width: Math.floor(parent.width * 0.55) + height: UM.Theme.getSize("sidebar_header_mode_toggle").height + anchors.right: parent.right + anchors.rightMargin: UM.Theme.getSize("sidebar_margin").width + anchors.top: { - var lengths = []; - var weights = []; - var costs = []; - var someCostsKnown = false; - if(base.printMaterialLengths) { - for(var index = 0; index < base.printMaterialLengths.length; index++) - { - if(base.printMaterialLengths[index] > 0) - { - lengths.push(base.printMaterialLengths[index].toFixed(2)); - weights.push(String(Math.floor(base.printMaterialWeights[index]))); - var cost = base.printMaterialCosts[index] == undefined ? 0 : base.printMaterialCosts[index].toFixed(2); - costs.push(cost); - if(cost > 0) - { - someCostsKnown = true; - } - } - } - } - if(lengths.length == 0) + if (settingsModeLabel.contentWidth >= parent.width - width - UM.Theme.getSize("sidebar_margin").width * 2) { - lengths = ["0.00"]; - weights = ["0"]; - costs = ["0.00"]; - } - if(someCostsKnown) - { - return catalog.i18nc("@label Print estimates: m for meters, g for grams, %4 is currency and %3 is print cost", "%1m / ~ %2g / ~ %4 %3").arg(lengths.join(" + ")) - .arg(weights.join(" + ")).arg(costs.join(" + ")).arg(UM.Preferences.getValue("cura/currency")); + return settingsModeLabel.bottom; } else { - return catalog.i18nc("@label Print estimates: m for meters, g for grams", "%1m / ~ %2g").arg(lengths.join(" + ")).arg(weights.join(" + ")); + return headerSeparator.bottom; } } - MouseArea - { - id: costSpecMouseArea - anchors.fill: parent - hoverEnabled: true + anchors.topMargin: UM.Theme.getSize("sidebar_margin").height + visible: !monitoringPrint && !hideSettings && !hideView + Component{ + id: wizardDelegate + Button { + height: settingsModeSelection.height + anchors.left: parent.left + anchors.leftMargin: model.index * Math.floor(settingsModeSelection.width / 2) + anchors.verticalCenter: parent.verticalCenter + width: Math.floor(0.5 * parent.width) + text: model.text + exclusiveGroup: modeMenuGroup; + checkable: true; + checked: base.currentModeIndex == index + onClicked: base.currentModeIndex = index - onEntered: - { + onHoveredChanged: { + if (hovered) + { + tooltipDelayTimer.item = settingsModeSelection + tooltipDelayTimer.text = model.tooltipText + tooltipDelayTimer.start(); + } + else + { + tooltipDelayTimer.stop(); + base.hideTooltip(); + } + } - if(base.printDuration.valid && !base.printDuration.isTotalDurationZero) - { - var show_data = costSpec.getSpecsData() - - base.showTooltip(parent, Qt.point(-UM.Theme.getSize("sidebar_margin").width, 0), show_data); + style: ButtonStyle { + background: Rectangle { + border.width: control.checked ? UM.Theme.getSize("default_lining").width * 2 : UM.Theme.getSize("default_lining").width + border.color: (control.checked || control.pressed) ? UM.Theme.getColor("action_button_active_border") : + control.hovered ? UM.Theme.getColor("action_button_hovered_border") : + UM.Theme.getColor("action_button_border") + color: (control.checked || control.pressed) ? UM.Theme.getColor("action_button_active") : + control.hovered ? UM.Theme.getColor("action_button_hovered") : + UM.Theme.getColor("action_button") + Behavior on color { ColorAnimation { duration: 50; } } + Label { + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + anchors.leftMargin: UM.Theme.getSize("default_lining").width * 2 + anchors.rightMargin: UM.Theme.getSize("default_lining").width * 2 + color: (control.checked || control.pressed) ? UM.Theme.getColor("action_button_active_text") : + control.hovered ? UM.Theme.getColor("action_button_hovered_text") : + UM.Theme.getColor("action_button_text") + font: UM.Theme.getFont("default") + text: control.text + horizontalAlignment: Text.AlignHCenter + elide: Text.ElideMiddle + } + } + label: Item { } } } - onExited: + } + ExclusiveGroup { id: modeMenuGroup; } + + ListView + { + id: modesList + property var index: 0 + model: modesListModel + delegate: wizardDelegate + anchors.top: parent.top + anchors.left: parent.left + width: parent.width + } + } + + StackView + { + id: sidebarContents + + anchors.bottom: footerSeparator.top + anchors.top: settingsModeSelection.bottom + anchors.topMargin: UM.Theme.getSize("sidebar_margin").height + anchors.left: base.left + anchors.right: base.right + visible: !monitoringPrint && !hideSettings + + delegate: StackViewDelegate + { + function transitionFinished(properties) { - base.hideTooltip(); + properties.exitItem.opacity = 1 + } + + pushTransition: StackViewTransition + { + PropertyAnimation + { + target: enterItem + property: "opacity" + from: 0 + to: 1 + duration: 100 + } + PropertyAnimation + { + target: exitItem + property: "opacity" + from: 1 + to: 0 + duration: 100 + } } } } - } - // SaveButton and MonitorButton are actually the bottom footer panels. - // "!monitoringPrint" currently means "show-settings-mode" - SaveButton - { - id: saveButton - implicitWidth: base.width - anchors.top: footerSeparator.bottom - anchors.topMargin: UM.Theme.getSize("sidebar_margin").height - anchors.bottom: parent.bottom - visible: !monitoringPrint - } - - MonitorButton - { - id: monitorButton - implicitWidth: base.width - anchors.top: footerSeparator.bottom - anchors.topMargin: UM.Theme.getSize("sidebar_margin").height - anchors.bottom: parent.bottom - visible: monitoringPrint - } - - - SidebarTooltip - { - id: tooltip; - } - - // Setting mode: Recommended or Custom - ListModel - { - id: modesListModel; - } - - SidebarSimple - { - id: sidebarSimple; - visible: false; - - onShowTooltip: base.showTooltip(item, location, text) - onHideTooltip: base.hideTooltip() - } - - SidebarAdvanced - { - id: sidebarAdvanced; - visible: false; - - onShowTooltip: base.showTooltip(item, location, text) - onHideTooltip: base.hideTooltip() - } - - Component.onCompleted: - { - modesListModel.append({ - text: catalog.i18nc("@title:tab", "Recommended"), - tooltipText: catalog.i18nc("@tooltip", "Recommended Print Setup

    Print with the recommended settings for the selected printer, material and quality."), - item: sidebarSimple - }) - modesListModel.append({ - text: catalog.i18nc("@title:tab", "Custom"), - tooltipText: catalog.i18nc("@tooltip", "Custom Print Setup

    Print with finegrained control over every last bit of the slicing process."), - item: sidebarAdvanced - }) - sidebarContents.push({ "item": modesListModel.get(base.currentModeIndex).item, "immediate": true }); - - var index = Math.floor(UM.Preferences.getValue("cura/active_mode")) - if(index) + Loader { - currentModeIndex = index; + id: controlItem + anchors.bottom: footerSeparator.top + anchors.top: headerSeparator.bottom + anchors.left: base.left + anchors.right: base.right + sourceComponent: + { + if(monitoringPrint && connectedPrinter != null) + { + if(connectedPrinter.controlItem != null) + { + return connectedPrinter.controlItem + } + } + return null + } + } + + Loader + { + anchors.bottom: footerSeparator.top + anchors.top: headerSeparator.bottom + anchors.left: base.left + anchors.right: base.right + source: + { + if(controlItem.sourceComponent == null) + { + if(monitoringPrint) + { + return "PrintMonitor.qml" + } else + { + return "SidebarContents.qml" + } + } + else + { + return "" + } + } + } + + Rectangle + { + id: footerSeparator + width: parent.width + height: UM.Theme.getSize("sidebar_lining").height + color: UM.Theme.getColor("sidebar_lining") + anchors.bottom: printSpecs.top + anchors.bottomMargin: Math.floor(UM.Theme.getSize("sidebar_margin").height * 2 + UM.Theme.getSize("progressbar").height + UM.Theme.getFont("default_bold").pixelSize) + } + + Item + { + id: printSpecs + anchors.left: parent.left + anchors.bottom: parent.bottom + anchors.leftMargin: UM.Theme.getSize("sidebar_margin").width + anchors.bottomMargin: UM.Theme.getSize("sidebar_margin").height + height: timeDetails.height + costSpec.height + width: base.width - (saveButton.buttonRowWidth + UM.Theme.getSize("sidebar_margin").width) + visible: !monitoringPrint + clip: true + + Label + { + id: timeDetails + anchors.left: parent.left + anchors.bottom: costSpec.top + font: UM.Theme.getFont("large") + color: UM.Theme.getColor("text_subtext") + text: (!base.printDuration || !base.printDuration.valid) ? catalog.i18nc("@label Hours and minutes", "00h 00min") : base.printDuration.getDisplayString(UM.DurationFormat.Short) + + MouseArea + { + id: timeDetailsMouseArea + anchors.fill: parent + hoverEnabled: true + + onEntered: + { + if(base.printDuration.valid && !base.printDuration.isTotalDurationZero) + { + // All the time information for the different features is achieved + var print_time = PrintInformation.getFeaturePrintTimes(); + var total_seconds = parseInt(base.printDuration.getDisplayString(UM.DurationFormat.Seconds)) + + // A message is created and displayed when the user hover the time label + var tooltip_html = "%1
    ".arg(catalog.i18nc("@tooltip", "Time specification")); + for(var feature in print_time) + { + if(!print_time[feature].isTotalDurationZero) + { + tooltip_html += "" + + "".arg(print_time[feature].getDisplayString(UM.DurationFormat.ISO8601).slice(0,-3)) + + "".arg(Math.round(100 * parseInt(print_time[feature].getDisplayString(UM.DurationFormat.Seconds)) / total_seconds)) + + ""; + } + } + tooltip_html += "
    " + feature + ":  %1  %1%
    "; + + base.showTooltip(parent, Qt.point(-UM.Theme.getSize("sidebar_margin").width, 0), tooltip_html); + } + } + onExited: + { + base.hideTooltip(); + } + } + } + + Label + { + function formatRow(items) + { + var row_html = ""; + for(var item = 0; item < items.length; item++) + { + if (item == 0) + { + row_html += "%1".arg(items[item]); + } + else + { + row_html += "  %1".arg(items[item]); + } + } + row_html += ""; + return row_html; + } + + function getSpecsData() + { + var lengths = []; + var total_length = 0; + var weights = []; + var total_weight = 0; + var costs = []; + var total_cost = 0; + var some_costs_known = false; + var names = []; + if(base.printMaterialLengths) + { + for(var index = 0; index < base.printMaterialLengths.length; index++) + { + if(base.printMaterialLengths[index] > 0) + { + names.push(base.printMaterialNames[index]); + lengths.push(base.printMaterialLengths[index].toFixed(2)); + weights.push(String(Math.floor(base.printMaterialWeights[index]))); + var cost = base.printMaterialCosts[index] == undefined ? 0 : base.printMaterialCosts[index].toFixed(2); + costs.push(cost); + if(cost > 0) + { + some_costs_known = true; + } + + total_length += base.printMaterialLengths[index]; + total_weight += base.printMaterialWeights[index]; + total_cost += base.printMaterialCosts[index]; + } + } + } + if(lengths.length == 0) + { + lengths = ["0.00"]; + weights = ["0"]; + costs = ["0.00"]; + } + + var tooltip_html = "%1
    ".arg(catalog.i18nc("@label", "Cost specification")); + for(var index = 0; index < lengths.length; index++) + { + tooltip_html += formatRow([ + "%1:".arg(names[index]), + catalog.i18nc("@label m for meter", "%1m").arg(lengths[index]), + catalog.i18nc("@label g for grams", "%1g").arg(weights[index]), + "%1 %2".arg(UM.Preferences.getValue("cura/currency")).arg(costs[index]), + ]); + } + if(lengths.length > 1) + { + tooltip_html += formatRow([ + catalog.i18nc("@label", "Total:"), + catalog.i18nc("@label m for meter", "%1m").arg(total_length.toFixed(2)), + catalog.i18nc("@label g for grams", "%1g").arg(Math.round(total_weight)), + "%1 %2".arg(UM.Preferences.getValue("cura/currency")).arg(total_cost.toFixed(2)), + ]); + } + tooltip_html += "
    "; + tooltipText = tooltip_html; + + return tooltipText + } + + id: costSpec + anchors.left: parent.left + anchors.bottom: parent.bottom + font: UM.Theme.getFont("very_small") + color: UM.Theme.getColor("text_subtext") + elide: Text.ElideMiddle + width: parent.width + property string tooltipText + text: + { + var lengths = []; + var weights = []; + var costs = []; + var someCostsKnown = false; + if(base.printMaterialLengths) { + for(var index = 0; index < base.printMaterialLengths.length; index++) + { + if(base.printMaterialLengths[index] > 0) + { + lengths.push(base.printMaterialLengths[index].toFixed(2)); + weights.push(String(Math.floor(base.printMaterialWeights[index]))); + var cost = base.printMaterialCosts[index] == undefined ? 0 : base.printMaterialCosts[index].toFixed(2); + costs.push(cost); + if(cost > 0) + { + someCostsKnown = true; + } + } + } + } + if(lengths.length == 0) + { + lengths = ["0.00"]; + weights = ["0"]; + costs = ["0.00"]; + } + if(someCostsKnown) + { + return catalog.i18nc("@label Print estimates: m for meters, g for grams, %4 is currency and %3 is print cost", "%1m / ~ %2g / ~ %4 %3").arg(lengths.join(" + ")) + .arg(weights.join(" + ")).arg(costs.join(" + ")).arg(UM.Preferences.getValue("cura/currency")); + } + else + { + return catalog.i18nc("@label Print estimates: m for meters, g for grams", "%1m / ~ %2g").arg(lengths.join(" + ")).arg(weights.join(" + ")); + } + } + MouseArea + { + id: costSpecMouseArea + anchors.fill: parent + hoverEnabled: true + + onEntered: + { + + if(base.printDuration.valid && !base.printDuration.isTotalDurationZero) + { + var show_data = costSpec.getSpecsData() + + base.showTooltip(parent, Qt.point(-UM.Theme.getSize("sidebar_margin").width, 0), show_data); + } + } + onExited: + { + base.hideTooltip(); + } + } + } + } + + // SaveButton and MonitorButton are actually the bottom footer panels. + // "!monitoringPrint" currently means "show-settings-mode" + SaveButton + { + id: saveButton + implicitWidth: base.width + anchors.top: footerSeparator.bottom + anchors.topMargin: UM.Theme.getSize("sidebar_margin").height + anchors.bottom: parent.bottom + visible: !monitoringPrint + } + + MonitorButton + { + id: monitorButton + implicitWidth: base.width + anchors.top: footerSeparator.bottom + anchors.topMargin: UM.Theme.getSize("sidebar_margin").height + anchors.bottom: parent.bottom + visible: monitoringPrint + } + + + SidebarTooltip + { + id: tooltip; + } + + // Setting mode: Recommended or Custom + ListModel + { + id: modesListModel; + } + + SidebarSimple + { + id: sidebarSimple; + visible: false; + + onShowTooltip: base.showTooltip(item, location, text) + onHideTooltip: base.hideTooltip() + } + + SidebarAdvanced + { + id: sidebarAdvanced; + visible: false; + + onShowTooltip: base.showTooltip(item, location, text) + onHideTooltip: base.hideTooltip() + } + + Component.onCompleted: + { + modesListModel.append({ + text: catalog.i18nc("@title:tab", "Recommended"), + tooltipText: catalog.i18nc("@tooltip", "Recommended Print Setup

    Print with the recommended settings for the selected printer, material and quality."), + item: sidebarSimple + }) + modesListModel.append({ + text: catalog.i18nc("@title:tab", "Custom"), + tooltipText: catalog.i18nc("@tooltip", "Custom Print Setup

    Print with finegrained control over every last bit of the slicing process."), + item: sidebarAdvanced + }) + sidebarContents.push({ "item": modesListModel.get(base.currentModeIndex).item, "immediate": true }); + + var index = Math.floor(UM.Preferences.getValue("cura/active_mode")) + if(index) + { + currentModeIndex = index; + } + } + + UM.SettingPropertyProvider + { + id: machineExtruderCount + + containerStackId: Cura.MachineManager.activeMachineId + key: "machine_extruder_count" + watchedProperties: [ "value" ] + storeIndex: 0 + } + + UM.SettingPropertyProvider + { + id: machineHeatedBed + + containerStackId: Cura.MachineManager.activeMachineId + key: "machine_heated_bed" + watchedProperties: [ "value" ] + storeIndex: 0 } } - - UM.SettingPropertyProvider - { - id: machineExtruderCount - - containerStackId: Cura.MachineManager.activeMachineId - key: "machine_extruder_count" - watchedProperties: [ "value" ] - storeIndex: 0 - } - - UM.SettingPropertyProvider - { - id: machineHeatedBed - - containerStackId: Cura.MachineManager.activeMachineId - key: "machine_heated_bed" - watchedProperties: [ "value" ] - storeIndex: 0 - } } diff --git a/resources/qml/Topbar.qml b/resources/qml/Topbar.qml index 50e90a9a37..5817aff13b 100644 --- a/resources/qml/Topbar.qml +++ b/resources/qml/Topbar.qml @@ -6,7 +6,7 @@ import QtQuick.Controls 1.1 import QtQuick.Controls.Styles 1.1 import QtQuick.Layouts 1.1 -import UM 1.2 as UM +import UM 1.4 as UM import Cura 1.0 as Cura import "Menus" @@ -83,34 +83,13 @@ Rectangle style: UM.Theme.styles.topbar_header_tab height: UM.Theme.getSize("sidebar_header").height onClicked: UM.Controller.setActiveStage(model.id) + iconSource: model.stage.iconSource + + property color overlayColor: "transparent" + property string overlayIconSource: "" } } -// Button -// { -// id: showSettings -// height: UM.Theme.getSize("sidebar_header").height -// text: catalog.i18nc("@title:tab", "Prepare") -// checkable: true -// checked: isChecked() -// exclusiveGroup: sidebarHeaderBarGroup -// style: UM.Theme.styles.topbar_header_tab -// -// // We use a Qt.binding to re-bind the checkbox state after manually setting it -// // https://stackoverflow.com/questions/38798450/qt-5-7-qml-why-are-my-checkbox-property-bindings-disappearing -// onClicked: { -// base.stopMonitoringPrint() -// checked = Qt.binding(isChecked) -// } -// -// function isChecked () { -// return !base.monitoringPrint -// } -// -// property color overlayColor: "transparent" -// property string overlayIconSource: "" -// } - // Button // { // id: showMonitor From 2d044a37ae364accfbd86dcf25c026046e0a7228 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Wed, 6 Dec 2017 13:33:05 +0100 Subject: [PATCH 40/62] Sidebar and main view via loader --- plugins/MonitorStage/MonitorStage.py | 7 ++++ resources/qml/Cura.qml | 45 ++++++++++---------------- resources/qml/Settings/SettingView.qml | 2 +- resources/qml/Sidebar.qml | 2 +- resources/qml/SidebarHeader.qml | 8 ++--- resources/qml/Topbar.qml | 45 ++++++++++++-------------- 6 files changed, 50 insertions(+), 59 deletions(-) diff --git a/plugins/MonitorStage/MonitorStage.py b/plugins/MonitorStage/MonitorStage.py index 8b98f2872c..7cd6fe5063 100644 --- a/plugins/MonitorStage/MonitorStage.py +++ b/plugins/MonitorStage/MonitorStage.py @@ -1,6 +1,8 @@ # Copyright (c) 2017 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. +import os.path from UM.Application import Application +from UM.Resources import Resources from cura.Stages.CuraStage import CuraStage @@ -10,6 +12,11 @@ class MonitorStage(CuraStage): def __init__(self): super().__init__() Application.getInstance().engineCreatedSignal.connect(self._engineCreated) + # TODO: connect output device state to icon source def _engineCreated(self): + # Note: currently the sidebar component for prepare and monitor stages is the same, this will change with the printer output device refactor! + sidebar_component_path = os.path.join(Resources.getPath(Application.getInstance().ResourceTypes.QmlFiles), "Sidebar.qml") + sidebar_component = Application.getInstance().createQmlComponent(sidebar_component_path) + self.addDisplayComponent("sidebar", sidebar_component) self.setIconSource(Application.getInstance().getTheme().getIcon("tab_status_connected")) diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index 3c9ca25b05..662de05063 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -358,15 +358,13 @@ UM.MainWindow Topbar { id: topbar - anchors.left:parent.left + anchors.left: parent.left anchors.right: parent.right anchors.top: parent.top - monitoringPrint: base.showPrintMonitor - onStartMonitoringPrint: base.showPrintMonitor = true - onStopMonitoringPrint: base.showPrintMonitor = false } - Loader { + Loader + { id: sidebar anchors @@ -382,39 +380,30 @@ UM.MainWindow sourceComponent: UM.Controller.activeStage.sidebarComponent } - Rectangle + Loader { - id: viewportOverlay + id: main - color: UM.Theme.getColor("viewport_overlay") anchors { top: topbar.bottom bottom: parent.bottom - left:parent.left + left: parent.left right: sidebar.left +// horizontalCenter: parent.horizontalCenter +// verticalCenter: parent.verticalCenter +// horizontalCenterOffset: - UM.Theme.getSize("sidebar").width / 2 +// verticalCenterOffset: UM.Theme.getSize("sidebar_header").height / 2 } - visible: opacity > 0 - opacity: base.showPrintMonitor ? 1 : 0 - MouseArea { - anchors.fill: parent - acceptedButtons: Qt.AllButtons +// MouseArea +// { +// anchors.fill: parent +// acceptedButtons: Qt.AllButtons +// onWheel: wheel.accepted = true +// } - onWheel: wheel.accepted = true - } - } - - Loader - { - sourceComponent: Cura.MachineManager.printerOutputDevices.length > 0 ? Cura.MachineManager.printerOutputDevices[0].monitorItem: null - visible: base.showPrintMonitor - anchors.horizontalCenter: parent.horizontalCenter - anchors.verticalCenter: parent.verticalCenter - anchors.horizontalCenterOffset: - UM.Theme.getSize("sidebar").width / 2 - anchors.verticalCenterOffset: UM.Theme.getSize("sidebar_header").height / 2 - property real maximumWidth: viewportOverlay.width - property real maximumHeight: viewportOverlay.height + sourceComponent: UM.Controller.activeStage.mainComponent } UM.MessageStack diff --git a/resources/qml/Settings/SettingView.qml b/resources/qml/Settings/SettingView.qml index 1ebb5cc40e..2079710315 100644 --- a/resources/qml/Settings/SettingView.qml +++ b/resources/qml/Settings/SettingView.qml @@ -24,7 +24,7 @@ Item { id: globalProfileRow height: UM.Theme.getSize("sidebar_setup").height - visible: !sidebar.monitoringPrint && !sidebar.hideSettings +// visible: !sidebar.monitoringPrint && !sidebar.hideSettings anchors { diff --git a/resources/qml/Sidebar.qml b/resources/qml/Sidebar.qml index 7429ef26df..992ad1f848 100644 --- a/resources/qml/Sidebar.qml +++ b/resources/qml/Sidebar.qml @@ -26,7 +26,7 @@ Component property var connectedPrinter: Cura.MachineManager.printerOutputDevices.length >= 1 ? Cura.MachineManager.printerOutputDevices[0] : null property int backendState: UM.Backend.state - property bool monitoringPrint: false + property bool monitoringPrint: UM.Controller.activeStage.id == "MonitorStage" property variant printDuration: PrintInformation.currentPrintTime property variant printMaterialLengths: PrintInformation.materialLengths diff --git a/resources/qml/SidebarHeader.qml b/resources/qml/SidebarHeader.qml index 3e1e85824a..0f0572a48a 100644 --- a/resources/qml/SidebarHeader.qml +++ b/resources/qml/SidebarHeader.qml @@ -40,7 +40,7 @@ Column id: extruderSelectionRow width: parent.width height: Math.floor(UM.Theme.getSize("sidebar_tabs").height * 2 / 3) - visible: machineExtruderCount.properties.value > 1 && !sidebar.monitoringPrint + visible: machineExtruderCount.properties.value > 1 anchors { @@ -229,7 +229,7 @@ Column { id: materialRow height: UM.Theme.getSize("sidebar_setup").height - visible: Cura.MachineManager.hasMaterials && !sidebar.monitoringPrint && !sidebar.hideSettings + visible: Cura.MachineManager.hasMaterials anchors { @@ -279,7 +279,7 @@ Column { id: variantRow height: UM.Theme.getSize("sidebar_setup").height - visible: Cura.MachineManager.hasVariants && !sidebar.monitoringPrint && !sidebar.hideSettings + visible: Cura.MachineManager.hasVariants anchors { @@ -319,7 +319,7 @@ Column { id: materialInfoRow height: Math.floor(UM.Theme.getSize("sidebar_setup").height / 2) - visible: (Cura.MachineManager.hasVariants || Cura.MachineManager.hasMaterials) && !sidebar.monitoringPrint && !sidebar.hideSettings + visible: Cura.MachineManager.hasVariants || Cura.MachineManager.hasMaterials anchors { diff --git a/resources/qml/Topbar.qml b/resources/qml/Topbar.qml index 5817aff13b..d50cc64608 100644 --- a/resources/qml/Topbar.qml +++ b/resources/qml/Topbar.qml @@ -16,27 +16,22 @@ Rectangle anchors.left: parent.left anchors.right: parent.right height: UM.Theme.getSize("sidebar_header").height - color: base.monitoringPrint ? UM.Theme.getColor("topbar_background_color_monitoring") : UM.Theme.getColor("topbar_background_color") + color: UM.Controller.activeStage.id == "MonitorStage" ? UM.Theme.getColor("topbar_background_color_monitoring") : UM.Theme.getColor("topbar_background_color") property bool printerConnected: Cura.MachineManager.printerOutputDevices.length != 0 property bool printerAcceptsCommands: printerConnected && Cura.MachineManager.printerOutputDevices[0].acceptsCommands - property bool monitoringPrint: false - - // outgoing signal - signal startMonitoringPrint() - signal stopMonitoringPrint() // update monitoring status when event was triggered outside topbar - Component.onCompleted: { - startMonitoringPrint.connect(function () { - base.monitoringPrint = true - UM.Controller.disableModelRendering() - }) - stopMonitoringPrint.connect(function () { - base.monitoringPrint = false - UM.Controller.enableModelRendering() - }) - } +// Component.onCompleted: { +// startMonitoringPrint.connect(function () { +// base.monitoringPrint = true +// UM.Controller.disableModelRendering() +// }) +// stopMonitoringPrint.connect(function () { +// base.monitoringPrint = false +// UM.Controller.enableModelRendering() +// }) +// } UM.I18nCatalog { @@ -79,9 +74,10 @@ Rectangle text: model.name checkable: true checked: model.active - exclusiveGroup: sidebarHeaderBarGroup + exclusiveGroup: topbarMenuGroup style: UM.Theme.styles.topbar_header_tab height: UM.Theme.getSize("sidebar_header").height + width: UM.Theme.getSize("topbar_button").width onClicked: UM.Controller.setActiveStage(model.id) iconSource: model.stage.iconSource @@ -90,6 +86,8 @@ Rectangle } } + ExclusiveGroup { id: topbarMenuGroup } + // Button // { // id: showMonitor @@ -149,8 +147,6 @@ Rectangle // } // } // } - - ExclusiveGroup { id: sidebarHeaderBarGroup } } ToolButton @@ -218,17 +214,16 @@ Rectangle menu: PrinterMenu { } } - //View orientation Item + // View orientation Item Row { id: viewOrientationControl height: 30 - spacing: 2 + visible: UM.Controller.activeStage.id != "MonitorStage" - visible: !base.monitoringPrint - - anchors { + anchors + { verticalCenter: base.verticalCenter right: viewModeButton.right rightMargin: UM.Theme.getSize("default_margin").width + viewModeButton.width @@ -306,7 +301,7 @@ Rectangle } style: UM.Theme.styles.combobox - visible: !base.monitoringPrint + visible: UM.Controller.activeStage.id != "MonitorStage" model: UM.ViewModel { } textRole: "name" From 9561827bdad91604fc70e0c448c9b8c657c83a83 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Wed, 6 Dec 2017 14:12:51 +0100 Subject: [PATCH 41/62] CURA-4680 Checking if there is global stack in the ExtruderManager. Intead of checking for it in all the methods in MachineManager, now the check is done in ExtruderManager when there is no printer in the list. --- cura/Settings/ExtruderManager.py | 8 +++++--- cura/Settings/MachineManager.py | 19 ------------------- 2 files changed, 5 insertions(+), 22 deletions(-) diff --git a/cura/Settings/ExtruderManager.py b/cura/Settings/ExtruderManager.py index 34b283107d..32e3867300 100755 --- a/cura/Settings/ExtruderManager.py +++ b/cura/Settings/ExtruderManager.py @@ -356,14 +356,16 @@ class ExtruderManager(QObject): # \return \type{List[ContainerStack]} a list of def getActiveExtruderStacks(self) -> List["ExtruderStack"]: global_stack = Application.getInstance().getGlobalContainerStack() + if not global_stack: + return None result = [] - machine_extruder_count = global_stack.getProperty("machine_extruder_count", "value") - - if global_stack and global_stack.getId() in self._extruder_trains: + if global_stack.getId() in self._extruder_trains: for extruder in sorted(self._extruder_trains[global_stack.getId()]): result.append(self._extruder_trains[global_stack.getId()][extruder]) + machine_extruder_count = global_stack.getProperty("machine_extruder_count", "value") + return result[:machine_extruder_count] def __globalContainerStackChanged(self) -> None: diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index 253c5f793c..afd038b45d 100755 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -499,10 +499,6 @@ class MachineManager(QObject): def activeVariantNames(self) -> List[str]: result = [] - # it can happen when there is no active machine - if self._global_container_stack is None: - return result - active_stacks = ExtruderManager.getInstance().getActiveGlobalAndExtruderStacks() if active_stacks is not None: for stack in active_stacks: @@ -516,10 +512,6 @@ class MachineManager(QObject): def activeMaterialNames(self) -> List[str]: result = [] - # it can happen when there is no active machine - if self._global_container_stack is None: - return result - active_stacks = ExtruderManager.getInstance().getActiveGlobalAndExtruderStacks() if active_stacks is not None: for stack in active_stacks: @@ -541,10 +533,6 @@ class MachineManager(QObject): def allActiveVariantIds(self) -> Dict[str, str]: result = {} - # it can happen when there is no active machine - if self._global_container_stack is None: - return result - active_stacks = ExtruderManager.getInstance().getActiveExtruderStacks() if active_stacks is not None: #If we have a global stack. for stack in active_stacks: @@ -564,14 +552,7 @@ class MachineManager(QObject): def allActiveMaterialIds(self) -> Dict[str, str]: result = {} - # it can happen when there is no active machine - if self._global_container_stack is None: - return result - active_stacks = ExtruderManager.getInstance().getActiveExtruderStacks() - - result[self._global_container_stack.getId()] = self._global_container_stack.material.getId() - if active_stacks is not None: # If we have extruder stacks for stack in active_stacks: material_container = stack.material From 569e040955f9f924040d18b52f3e30f54fe1a196 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Wed, 6 Dec 2017 14:45:47 +0100 Subject: [PATCH 42/62] Add more complete version info in project files CURA-4683 --- plugins/3MFWriter/ThreeMFWorkspaceWriter.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/plugins/3MFWriter/ThreeMFWorkspaceWriter.py b/plugins/3MFWriter/ThreeMFWorkspaceWriter.py index 9c143f0057..3953bea229 100644 --- a/plugins/3MFWriter/ThreeMFWorkspaceWriter.py +++ b/plugins/3MFWriter/ThreeMFWorkspaceWriter.py @@ -59,7 +59,9 @@ class ThreeMFWorkspaceWriter(WorkspaceWriter): version_file = zipfile.ZipInfo("Cura/version.ini") version_config_parser = configparser.ConfigParser() version_config_parser.add_section("versions") - version_config_parser.set("versions", "cura_version", Application.getStaticVersion()) + version_config_parser.set("versions", "cura_version", Application.getInstance().getVersion()) + version_config_parser.set("versions", "build_type", Application.getInstance().getBuildType()) + version_config_parser.set("versions", "is_debug_mode", Application.getInstance().getIsDebugMode()) version_file_string = StringIO() version_config_parser.write(version_file_string) From 298a659c094bb31e7ad86b4688a40a9a12161dfc Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Wed, 6 Dec 2017 14:48:58 +0100 Subject: [PATCH 43/62] Save is_debug_mode as string CURA-4683 --- plugins/3MFWriter/ThreeMFWorkspaceWriter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/3MFWriter/ThreeMFWorkspaceWriter.py b/plugins/3MFWriter/ThreeMFWorkspaceWriter.py index 3953bea229..4f1ff9494f 100644 --- a/plugins/3MFWriter/ThreeMFWorkspaceWriter.py +++ b/plugins/3MFWriter/ThreeMFWorkspaceWriter.py @@ -61,7 +61,7 @@ class ThreeMFWorkspaceWriter(WorkspaceWriter): version_config_parser.add_section("versions") version_config_parser.set("versions", "cura_version", Application.getInstance().getVersion()) version_config_parser.set("versions", "build_type", Application.getInstance().getBuildType()) - version_config_parser.set("versions", "is_debug_mode", Application.getInstance().getIsDebugMode()) + version_config_parser.set("versions", "is_debug_mode", str(Application.getInstance().getIsDebugMode())) version_file_string = StringIO() version_config_parser.write(version_file_string) From ee643610e5a4de384aa09f5ef5399c94a4972574 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Wed, 6 Dec 2017 17:46:18 +0100 Subject: [PATCH 44/62] Fix sidebar loading and unloading depending on active stage --- cura/CuraApplication.py | 1 - cura/Stages/CuraStage.py | 14 +- plugins/MonitorStage/MonitorStage.py | 7 +- plugins/PrepareStage/PrepareStage.py | 7 +- resources/qml/Cura.qml | 10 +- resources/qml/Sidebar.qml | 1145 +++++++++++++------------- resources/qml/SidebarSimple.qml | 20 +- 7 files changed, 601 insertions(+), 603 deletions(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index eec6c7f503..c9466a0093 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -1,6 +1,5 @@ # Copyright (c) 2017 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. - from PyQt5.QtNetwork import QLocalServer from PyQt5.QtNetwork import QLocalSocket diff --git a/cura/Stages/CuraStage.py b/cura/Stages/CuraStage.py index 6d4e56473d..8b7822ed7a 100644 --- a/cura/Stages/CuraStage.py +++ b/cura/Stages/CuraStage.py @@ -1,18 +1,22 @@ # Copyright (c) 2017 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -from PyQt5.QtCore import pyqtProperty, QObject +from PyQt5.QtCore import pyqtProperty, QUrl, QObject from UM.Stage import Stage class CuraStage(Stage): - def __init__(self): - super().__init__() + def __init__(self, parent = None): + super().__init__(parent) - @pyqtProperty(QObject, constant = True) + @pyqtProperty(str, constant = True) + def stageId(self): + return self.getPluginId() + + @pyqtProperty(QUrl, constant = True) def mainComponent(self): return self.getDisplayComponent("main") - @pyqtProperty(QObject, constant = True) + @pyqtProperty(QUrl, constant = True) def sidebarComponent(self): return self.getDisplayComponent("sidebar") diff --git a/plugins/MonitorStage/MonitorStage.py b/plugins/MonitorStage/MonitorStage.py index 7cd6fe5063..6131e5538b 100644 --- a/plugins/MonitorStage/MonitorStage.py +++ b/plugins/MonitorStage/MonitorStage.py @@ -9,14 +9,13 @@ from cura.Stages.CuraStage import CuraStage ## Stage for monitoring a 3D printing while it's printing. class MonitorStage(CuraStage): - def __init__(self): - super().__init__() + def __init__(self, parent = None): + super().__init__(parent) Application.getInstance().engineCreatedSignal.connect(self._engineCreated) # TODO: connect output device state to icon source def _engineCreated(self): # Note: currently the sidebar component for prepare and monitor stages is the same, this will change with the printer output device refactor! sidebar_component_path = os.path.join(Resources.getPath(Application.getInstance().ResourceTypes.QmlFiles), "Sidebar.qml") - sidebar_component = Application.getInstance().createQmlComponent(sidebar_component_path) - self.addDisplayComponent("sidebar", sidebar_component) + self.addDisplayComponent("sidebar", sidebar_component_path) self.setIconSource(Application.getInstance().getTheme().getIcon("tab_status_connected")) diff --git a/plugins/PrepareStage/PrepareStage.py b/plugins/PrepareStage/PrepareStage.py index 63086b3e93..9d4d632845 100644 --- a/plugins/PrepareStage/PrepareStage.py +++ b/plugins/PrepareStage/PrepareStage.py @@ -9,11 +9,10 @@ from cura.Stages.CuraStage import CuraStage ## Stage for preparing model (slicing). class PrepareStage(CuraStage): - def __init__(self): - super().__init__() + def __init__(self, parent = None): + super().__init__(parent) Application.getInstance().engineCreatedSignal.connect(self._engineCreated) def _engineCreated(self): sidebar_component_path = os.path.join(Resources.getPath(Application.getInstance().ResourceTypes.QmlFiles), "Sidebar.qml") - sidebar_component = Application.getInstance().createQmlComponent(sidebar_component_path) - self.addDisplayComponent("sidebar", sidebar_component) + self.addDisplayComponent("sidebar", sidebar_component_path) diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index 662de05063..445325df90 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -377,7 +377,8 @@ UM.MainWindow width: UM.Theme.getSize("sidebar").width z: 1 - sourceComponent: UM.Controller.activeStage.sidebarComponent + source: UM.Controller.activeStage.sidebarComponent + asynchronous: true } Loader @@ -390,10 +391,6 @@ UM.MainWindow bottom: parent.bottom left: parent.left right: sidebar.left -// horizontalCenter: parent.horizontalCenter -// verticalCenter: parent.verticalCenter -// horizontalCenterOffset: - UM.Theme.getSize("sidebar").width / 2 -// verticalCenterOffset: UM.Theme.getSize("sidebar_header").height / 2 } // MouseArea @@ -403,7 +400,8 @@ UM.MainWindow // onWheel: wheel.accepted = true // } - sourceComponent: UM.Controller.activeStage.mainComponent + source: UM.Controller.activeStage.mainComponent + asynchronous: true } UM.MessageStack diff --git a/resources/qml/Sidebar.qml b/resources/qml/Sidebar.qml index 992ad1f848..a02454d298 100644 --- a/resources/qml/Sidebar.qml +++ b/resources/qml/Sidebar.qml @@ -10,606 +10,601 @@ import UM 1.2 as UM import Cura 1.0 as Cura import "Menus" -Component + +Rectangle { - Rectangle + id: base; + + property int currentModeIndex; + property bool hideSettings: PrintInformation.preSliced + property bool hideView: Cura.MachineManager.activeMachineName == "" + + // Is there an output device for this printer? + property bool printerConnected: Cura.MachineManager.printerOutputDevices.length != 0 + property bool printerAcceptsCommands: printerConnected && Cura.MachineManager.printerOutputDevices[0].acceptsCommands + property var connectedPrinter: Cura.MachineManager.printerOutputDevices.length >= 1 ? Cura.MachineManager.printerOutputDevices[0] : null + property int backendState: UM.Backend.state + + property bool monitoringPrint: UM.Controller.activeStage.stageId == "MonitorStage" + + property variant printDuration: PrintInformation.currentPrintTime + property variant printMaterialLengths: PrintInformation.materialLengths + property variant printMaterialWeights: PrintInformation.materialWeights + property variant printMaterialCosts: PrintInformation.materialCosts + property variant printMaterialNames: PrintInformation.materialNames + + color: UM.Theme.getColor("sidebar") + UM.I18nCatalog { id: catalog; name:"cura"} + + Timer { + id: tooltipDelayTimer + interval: 500 + repeat: false + property var item + property string text + + onTriggered: + { + base.showTooltip(base, {x: 0, y: item.y}, text); + } + } + + function showTooltip(item, position, text) { - id: base; + tooltip.text = text; + position = item.mapToItem(base, position.x - UM.Theme.getSize("default_arrow").width, position.y); + tooltip.show(position); + } - property int currentModeIndex; - property bool hideSettings: PrintInformation.preSliced - property bool hideView: Cura.MachineManager.activeMachineName == "" + function hideTooltip() + { + tooltip.hide(); + } - // Is there an output device for this printer? - property bool printerConnected: Cura.MachineManager.printerOutputDevices.length != 0 - property bool printerAcceptsCommands: printerConnected && Cura.MachineManager.printerOutputDevices[0].acceptsCommands - property var connectedPrinter: Cura.MachineManager.printerOutputDevices.length >= 1 ? Cura.MachineManager.printerOutputDevices[0] : null - property int backendState: UM.Backend.state + function strPadLeft(string, pad, length) { + return (new Array(length + 1).join(pad) + string).slice(-length); + } - property bool monitoringPrint: UM.Controller.activeStage.id == "MonitorStage" + function getPrettyTime(time) + { + var hours = Math.floor(time / 3600) + time -= hours * 3600 + var minutes = Math.floor(time / 60); + time -= minutes * 60 + var seconds = Math.floor(time); - property variant printDuration: PrintInformation.currentPrintTime - property variant printMaterialLengths: PrintInformation.materialLengths - property variant printMaterialWeights: PrintInformation.materialWeights - property variant printMaterialCosts: PrintInformation.materialCosts - property variant printMaterialNames: PrintInformation.materialNames + var finalTime = strPadLeft(hours, "0", 2) + ':' + strPadLeft(minutes,'0',2)+ ':' + strPadLeft(seconds,'0',2); + return finalTime; + } - color: UM.Theme.getColor("sidebar") - UM.I18nCatalog { id: catalog; name:"cura"} + MouseArea + { + anchors.fill: parent + acceptedButtons: Qt.AllButtons; - Timer { - id: tooltipDelayTimer - interval: 500 - repeat: false - property var item - property string text + onWheel: + { + wheel.accepted = true; + } + } - onTriggered: + SidebarHeader { + id: header + width: parent.width + visible: machineExtruderCount.properties.value > 1 || Cura.MachineManager.hasMaterials || Cura.MachineManager.hasVariants + onShowTooltip: base.showTooltip(item, location, text) + onHideTooltip: base.hideTooltip() + } + + Rectangle { + id: headerSeparator + width: parent.width + visible: settingsModeSelection.visible && header.visible + height: visible ? UM.Theme.getSize("sidebar_lining").height : 0 + color: UM.Theme.getColor("sidebar_lining") + anchors.top: header.bottom + anchors.topMargin: visible ? UM.Theme.getSize("sidebar_margin").height : 0 + } + + Label { + id: settingsModeLabel + text: !hideSettings ? catalog.i18nc("@label:listbox", "Print Setup") : catalog.i18nc("@label:listbox","Print Setup disabled\nG-code files cannot be modified"); + anchors.left: parent.left + anchors.leftMargin: UM.Theme.getSize("sidebar_margin").width + anchors.top: headerSeparator.bottom + anchors.topMargin: UM.Theme.getSize("sidebar_margin").height + width: Math.floor(parent.width * 0.45) + font: UM.Theme.getFont("large") + color: UM.Theme.getColor("text") + visible: !monitoringPrint && !hideView + } + + Rectangle { + id: settingsModeSelection + color: "transparent" + width: Math.floor(parent.width * 0.55) + height: UM.Theme.getSize("sidebar_header_mode_toggle").height + anchors.right: parent.right + anchors.rightMargin: UM.Theme.getSize("sidebar_margin").width + anchors.top: + { + if (settingsModeLabel.contentWidth >= parent.width - width - UM.Theme.getSize("sidebar_margin").width * 2) { - base.showTooltip(base, {x: 0, y: item.y}, text); + return settingsModeLabel.bottom; + } + else + { + return headerSeparator.bottom; } } - - function showTooltip(item, position, text) - { - tooltip.text = text; - position = item.mapToItem(base, position.x - UM.Theme.getSize("default_arrow").width, position.y); - tooltip.show(position); - } - - function hideTooltip() - { - tooltip.hide(); - } - - function strPadLeft(string, pad, length) { - return (new Array(length + 1).join(pad) + string).slice(-length); - } - - function getPrettyTime(time) - { - var hours = Math.floor(time / 3600) - time -= hours * 3600 - var minutes = Math.floor(time / 60); - time -= minutes * 60 - var seconds = Math.floor(time); - - var finalTime = strPadLeft(hours, "0", 2) + ':' + strPadLeft(minutes,'0',2)+ ':' + strPadLeft(seconds,'0',2); - return finalTime; - } - - MouseArea - { - anchors.fill: parent - acceptedButtons: Qt.AllButtons; - - onWheel: - { - wheel.accepted = true; - } - } - - SidebarHeader { - id: header - width: parent.width - visible: machineExtruderCount.properties.value > 1 || Cura.MachineManager.hasMaterials || Cura.MachineManager.hasVariantst - onShowTooltip: base.showTooltip(item, location, text) - onHideTooltip: base.hideTooltip() - } - - Rectangle { - id: headerSeparator - width: parent.width - visible: settingsModeSelection.visible && header.visible - height: visible ? UM.Theme.getSize("sidebar_lining").height : 0 - color: UM.Theme.getColor("sidebar_lining") - anchors.top: header.bottom - anchors.topMargin: visible ? UM.Theme.getSize("sidebar_margin").height : 0 - } - - Label { - id: settingsModeLabel - text: !hideSettings ? catalog.i18nc("@label:listbox", "Print Setup") : catalog.i18nc("@label:listbox","Print Setup disabled\nG-code files cannot be modified"); - anchors.left: parent.left - anchors.leftMargin: UM.Theme.getSize("sidebar_margin").width - anchors.top: headerSeparator.bottom - anchors.topMargin: UM.Theme.getSize("sidebar_margin").height - width: Math.floor(parent.width * 0.45) - font: UM.Theme.getFont("large") - color: UM.Theme.getColor("text") - visible: !monitoringPrint && !hideView - } - - Rectangle { - id: settingsModeSelection - color: "transparent" - width: Math.floor(parent.width * 0.55) - height: UM.Theme.getSize("sidebar_header_mode_toggle").height - anchors.right: parent.right - anchors.rightMargin: UM.Theme.getSize("sidebar_margin").width - anchors.top: - { - if (settingsModeLabel.contentWidth >= parent.width - width - UM.Theme.getSize("sidebar_margin").width * 2) - { - return settingsModeLabel.bottom; - } - else - { - return headerSeparator.bottom; - } - } - anchors.topMargin: UM.Theme.getSize("sidebar_margin").height - visible: !monitoringPrint && !hideSettings && !hideView - Component{ - id: wizardDelegate - Button { - height: settingsModeSelection.height - anchors.left: parent.left - anchors.leftMargin: model.index * Math.floor(settingsModeSelection.width / 2) - anchors.verticalCenter: parent.verticalCenter - width: Math.floor(0.5 * parent.width) - text: model.text - exclusiveGroup: modeMenuGroup; - checkable: true; - checked: base.currentModeIndex == index - onClicked: base.currentModeIndex = index - - onHoveredChanged: { - if (hovered) - { - tooltipDelayTimer.item = settingsModeSelection - tooltipDelayTimer.text = model.tooltipText - tooltipDelayTimer.start(); - } - else - { - tooltipDelayTimer.stop(); - base.hideTooltip(); - } - } - - style: ButtonStyle { - background: Rectangle { - border.width: control.checked ? UM.Theme.getSize("default_lining").width * 2 : UM.Theme.getSize("default_lining").width - border.color: (control.checked || control.pressed) ? UM.Theme.getColor("action_button_active_border") : - control.hovered ? UM.Theme.getColor("action_button_hovered_border") : - UM.Theme.getColor("action_button_border") - color: (control.checked || control.pressed) ? UM.Theme.getColor("action_button_active") : - control.hovered ? UM.Theme.getColor("action_button_hovered") : - UM.Theme.getColor("action_button") - Behavior on color { ColorAnimation { duration: 50; } } - Label { - anchors.left: parent.left - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - anchors.leftMargin: UM.Theme.getSize("default_lining").width * 2 - anchors.rightMargin: UM.Theme.getSize("default_lining").width * 2 - color: (control.checked || control.pressed) ? UM.Theme.getColor("action_button_active_text") : - control.hovered ? UM.Theme.getColor("action_button_hovered_text") : - UM.Theme.getColor("action_button_text") - font: UM.Theme.getFont("default") - text: control.text - horizontalAlignment: Text.AlignHCenter - elide: Text.ElideMiddle - } - } - label: Item { } - } - } - } - ExclusiveGroup { id: modeMenuGroup; } - - ListView - { - id: modesList - property var index: 0 - model: modesListModel - delegate: wizardDelegate - anchors.top: parent.top + anchors.topMargin: UM.Theme.getSize("sidebar_margin").height + visible: !monitoringPrint && !hideSettings && !hideView + Component{ + id: wizardDelegate + Button { + height: settingsModeSelection.height anchors.left: parent.left - width: parent.width - } - } + anchors.leftMargin: model.index * Math.floor(settingsModeSelection.width / 2) + anchors.verticalCenter: parent.verticalCenter + width: Math.floor(0.5 * parent.width) + text: model.text + exclusiveGroup: modeMenuGroup; + checkable: true; + checked: base.currentModeIndex == index + onClicked: base.currentModeIndex = index - StackView - { - id: sidebarContents - - anchors.bottom: footerSeparator.top - anchors.top: settingsModeSelection.bottom - anchors.topMargin: UM.Theme.getSize("sidebar_margin").height - anchors.left: base.left - anchors.right: base.right - visible: !monitoringPrint && !hideSettings - - delegate: StackViewDelegate - { - function transitionFinished(properties) - { - properties.exitItem.opacity = 1 - } - - pushTransition: StackViewTransition - { - PropertyAnimation + onHoveredChanged: { + if (hovered) { - target: enterItem - property: "opacity" - from: 0 - to: 1 - duration: 100 - } - PropertyAnimation - { - target: exitItem - property: "opacity" - from: 1 - to: 0 - duration: 100 - } - } - } - } - - Loader - { - id: controlItem - anchors.bottom: footerSeparator.top - anchors.top: headerSeparator.bottom - anchors.left: base.left - anchors.right: base.right - sourceComponent: - { - if(monitoringPrint && connectedPrinter != null) - { - if(connectedPrinter.controlItem != null) - { - return connectedPrinter.controlItem - } - } - return null - } - } - - Loader - { - anchors.bottom: footerSeparator.top - anchors.top: headerSeparator.bottom - anchors.left: base.left - anchors.right: base.right - source: - { - if(controlItem.sourceComponent == null) - { - if(monitoringPrint) - { - return "PrintMonitor.qml" - } else - { - return "SidebarContents.qml" - } - } - else - { - return "" - } - } - } - - Rectangle - { - id: footerSeparator - width: parent.width - height: UM.Theme.getSize("sidebar_lining").height - color: UM.Theme.getColor("sidebar_lining") - anchors.bottom: printSpecs.top - anchors.bottomMargin: Math.floor(UM.Theme.getSize("sidebar_margin").height * 2 + UM.Theme.getSize("progressbar").height + UM.Theme.getFont("default_bold").pixelSize) - } - - Item - { - id: printSpecs - anchors.left: parent.left - anchors.bottom: parent.bottom - anchors.leftMargin: UM.Theme.getSize("sidebar_margin").width - anchors.bottomMargin: UM.Theme.getSize("sidebar_margin").height - height: timeDetails.height + costSpec.height - width: base.width - (saveButton.buttonRowWidth + UM.Theme.getSize("sidebar_margin").width) - visible: !monitoringPrint - clip: true - - Label - { - id: timeDetails - anchors.left: parent.left - anchors.bottom: costSpec.top - font: UM.Theme.getFont("large") - color: UM.Theme.getColor("text_subtext") - text: (!base.printDuration || !base.printDuration.valid) ? catalog.i18nc("@label Hours and minutes", "00h 00min") : base.printDuration.getDisplayString(UM.DurationFormat.Short) - - MouseArea - { - id: timeDetailsMouseArea - anchors.fill: parent - hoverEnabled: true - - onEntered: - { - if(base.printDuration.valid && !base.printDuration.isTotalDurationZero) - { - // All the time information for the different features is achieved - var print_time = PrintInformation.getFeaturePrintTimes(); - var total_seconds = parseInt(base.printDuration.getDisplayString(UM.DurationFormat.Seconds)) - - // A message is created and displayed when the user hover the time label - var tooltip_html = "%1
    ".arg(catalog.i18nc("@tooltip", "Time specification")); - for(var feature in print_time) - { - if(!print_time[feature].isTotalDurationZero) - { - tooltip_html += "" + - "".arg(print_time[feature].getDisplayString(UM.DurationFormat.ISO8601).slice(0,-3)) + - "".arg(Math.round(100 * parseInt(print_time[feature].getDisplayString(UM.DurationFormat.Seconds)) / total_seconds)) + - ""; - } - } - tooltip_html += "
    " + feature + ":  %1  %1%
    "; - - base.showTooltip(parent, Qt.point(-UM.Theme.getSize("sidebar_margin").width, 0), tooltip_html); - } - } - onExited: - { - base.hideTooltip(); - } - } - } - - Label - { - function formatRow(items) - { - var row_html = ""; - for(var item = 0; item < items.length; item++) - { - if (item == 0) - { - row_html += "%1".arg(items[item]); - } - else - { - row_html += "  %1".arg(items[item]); - } - } - row_html += ""; - return row_html; - } - - function getSpecsData() - { - var lengths = []; - var total_length = 0; - var weights = []; - var total_weight = 0; - var costs = []; - var total_cost = 0; - var some_costs_known = false; - var names = []; - if(base.printMaterialLengths) - { - for(var index = 0; index < base.printMaterialLengths.length; index++) - { - if(base.printMaterialLengths[index] > 0) - { - names.push(base.printMaterialNames[index]); - lengths.push(base.printMaterialLengths[index].toFixed(2)); - weights.push(String(Math.floor(base.printMaterialWeights[index]))); - var cost = base.printMaterialCosts[index] == undefined ? 0 : base.printMaterialCosts[index].toFixed(2); - costs.push(cost); - if(cost > 0) - { - some_costs_known = true; - } - - total_length += base.printMaterialLengths[index]; - total_weight += base.printMaterialWeights[index]; - total_cost += base.printMaterialCosts[index]; - } - } - } - if(lengths.length == 0) - { - lengths = ["0.00"]; - weights = ["0"]; - costs = ["0.00"]; - } - - var tooltip_html = "%1
    ".arg(catalog.i18nc("@label", "Cost specification")); - for(var index = 0; index < lengths.length; index++) - { - tooltip_html += formatRow([ - "%1:".arg(names[index]), - catalog.i18nc("@label m for meter", "%1m").arg(lengths[index]), - catalog.i18nc("@label g for grams", "%1g").arg(weights[index]), - "%1 %2".arg(UM.Preferences.getValue("cura/currency")).arg(costs[index]), - ]); - } - if(lengths.length > 1) - { - tooltip_html += formatRow([ - catalog.i18nc("@label", "Total:"), - catalog.i18nc("@label m for meter", "%1m").arg(total_length.toFixed(2)), - catalog.i18nc("@label g for grams", "%1g").arg(Math.round(total_weight)), - "%1 %2".arg(UM.Preferences.getValue("cura/currency")).arg(total_cost.toFixed(2)), - ]); - } - tooltip_html += "
    "; - tooltipText = tooltip_html; - - return tooltipText - } - - id: costSpec - anchors.left: parent.left - anchors.bottom: parent.bottom - font: UM.Theme.getFont("very_small") - color: UM.Theme.getColor("text_subtext") - elide: Text.ElideMiddle - width: parent.width - property string tooltipText - text: - { - var lengths = []; - var weights = []; - var costs = []; - var someCostsKnown = false; - if(base.printMaterialLengths) { - for(var index = 0; index < base.printMaterialLengths.length; index++) - { - if(base.printMaterialLengths[index] > 0) - { - lengths.push(base.printMaterialLengths[index].toFixed(2)); - weights.push(String(Math.floor(base.printMaterialWeights[index]))); - var cost = base.printMaterialCosts[index] == undefined ? 0 : base.printMaterialCosts[index].toFixed(2); - costs.push(cost); - if(cost > 0) - { - someCostsKnown = true; - } - } - } - } - if(lengths.length == 0) - { - lengths = ["0.00"]; - weights = ["0"]; - costs = ["0.00"]; - } - if(someCostsKnown) - { - return catalog.i18nc("@label Print estimates: m for meters, g for grams, %4 is currency and %3 is print cost", "%1m / ~ %2g / ~ %4 %3").arg(lengths.join(" + ")) - .arg(weights.join(" + ")).arg(costs.join(" + ")).arg(UM.Preferences.getValue("cura/currency")); + tooltipDelayTimer.item = settingsModeSelection + tooltipDelayTimer.text = model.tooltipText + tooltipDelayTimer.start(); } else { - return catalog.i18nc("@label Print estimates: m for meters, g for grams", "%1m / ~ %2g").arg(lengths.join(" + ")).arg(weights.join(" + ")); - } - } - MouseArea - { - id: costSpecMouseArea - anchors.fill: parent - hoverEnabled: true - - onEntered: - { - - if(base.printDuration.valid && !base.printDuration.isTotalDurationZero) - { - var show_data = costSpec.getSpecsData() - - base.showTooltip(parent, Qt.point(-UM.Theme.getSize("sidebar_margin").width, 0), show_data); - } - } - onExited: - { + tooltipDelayTimer.stop(); base.hideTooltip(); } } + + style: ButtonStyle { + background: Rectangle { + border.width: control.checked ? UM.Theme.getSize("default_lining").width * 2 : UM.Theme.getSize("default_lining").width + border.color: (control.checked || control.pressed) ? UM.Theme.getColor("action_button_active_border") : + control.hovered ? UM.Theme.getColor("action_button_hovered_border") : + UM.Theme.getColor("action_button_border") + color: (control.checked || control.pressed) ? UM.Theme.getColor("action_button_active") : + control.hovered ? UM.Theme.getColor("action_button_hovered") : + UM.Theme.getColor("action_button") + Behavior on color { ColorAnimation { duration: 50; } } + Label { + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + anchors.leftMargin: UM.Theme.getSize("default_lining").width * 2 + anchors.rightMargin: UM.Theme.getSize("default_lining").width * 2 + color: (control.checked || control.pressed) ? UM.Theme.getColor("action_button_active_text") : + control.hovered ? UM.Theme.getColor("action_button_hovered_text") : + UM.Theme.getColor("action_button_text") + font: UM.Theme.getFont("default") + text: control.text + horizontalAlignment: Text.AlignHCenter + elide: Text.ElideMiddle + } + } + label: Item { } + } } } + ExclusiveGroup { id: modeMenuGroup; } - // SaveButton and MonitorButton are actually the bottom footer panels. - // "!monitoringPrint" currently means "show-settings-mode" - SaveButton + ListView { - id: saveButton - implicitWidth: base.width - anchors.top: footerSeparator.bottom - anchors.topMargin: UM.Theme.getSize("sidebar_margin").height - anchors.bottom: parent.bottom - visible: !monitoringPrint - } - - MonitorButton - { - id: monitorButton - implicitWidth: base.width - anchors.top: footerSeparator.bottom - anchors.topMargin: UM.Theme.getSize("sidebar_margin").height - anchors.bottom: parent.bottom - visible: monitoringPrint - } - - - SidebarTooltip - { - id: tooltip; - } - - // Setting mode: Recommended or Custom - ListModel - { - id: modesListModel; - } - - SidebarSimple - { - id: sidebarSimple; - visible: false; - - onShowTooltip: base.showTooltip(item, location, text) - onHideTooltip: base.hideTooltip() - } - - SidebarAdvanced - { - id: sidebarAdvanced; - visible: false; - - onShowTooltip: base.showTooltip(item, location, text) - onHideTooltip: base.hideTooltip() - } - - Component.onCompleted: - { - modesListModel.append({ - text: catalog.i18nc("@title:tab", "Recommended"), - tooltipText: catalog.i18nc("@tooltip", "Recommended Print Setup

    Print with the recommended settings for the selected printer, material and quality."), - item: sidebarSimple - }) - modesListModel.append({ - text: catalog.i18nc("@title:tab", "Custom"), - tooltipText: catalog.i18nc("@tooltip", "Custom Print Setup

    Print with finegrained control over every last bit of the slicing process."), - item: sidebarAdvanced - }) - sidebarContents.push({ "item": modesListModel.get(base.currentModeIndex).item, "immediate": true }); - - var index = Math.floor(UM.Preferences.getValue("cura/active_mode")) - if(index) - { - currentModeIndex = index; - } - } - - UM.SettingPropertyProvider - { - id: machineExtruderCount - - containerStackId: Cura.MachineManager.activeMachineId - key: "machine_extruder_count" - watchedProperties: [ "value" ] - storeIndex: 0 - } - - UM.SettingPropertyProvider - { - id: machineHeatedBed - - containerStackId: Cura.MachineManager.activeMachineId - key: "machine_heated_bed" - watchedProperties: [ "value" ] - storeIndex: 0 + id: modesList + property var index: 0 + model: modesListModel + delegate: wizardDelegate + anchors.top: parent.top + anchors.left: parent.left + width: parent.width } } + + StackView + { + id: sidebarContents + + anchors.bottom: footerSeparator.top + anchors.top: settingsModeSelection.bottom + anchors.topMargin: UM.Theme.getSize("sidebar_margin").height + anchors.left: base.left + anchors.right: base.right + visible: !monitoringPrint && !hideSettings + + delegate: StackViewDelegate + { + function transitionFinished(properties) + { + properties.exitItem.opacity = 1 + } + + pushTransition: StackViewTransition + { + PropertyAnimation + { + target: enterItem + property: "opacity" + from: 0 + to: 1 + duration: 100 + } + PropertyAnimation + { + target: exitItem + property: "opacity" + from: 1 + to: 0 + duration: 100 + } + } + } + } + + Loader + { + id: controlItem + anchors.bottom: footerSeparator.top + anchors.top: headerSeparator.bottom + anchors.left: base.left + anchors.right: base.right + sourceComponent: + { + if(monitoringPrint && connectedPrinter != null) + { + if(connectedPrinter.controlItem != null) + { + return connectedPrinter.controlItem + } + } + return null + } + } + + Loader + { + anchors.bottom: footerSeparator.top + anchors.top: headerSeparator.bottom + anchors.left: base.left + anchors.right: base.right + source: + { + if(controlItem.sourceComponent == null) + { + if(monitoringPrint) + { + return "PrintMonitor.qml" + } else + { + return "SidebarContents.qml" + } + } + else + { + return "" + } + } + } + + Rectangle + { + id: footerSeparator + width: parent.width + height: UM.Theme.getSize("sidebar_lining").height + color: UM.Theme.getColor("sidebar_lining") + anchors.bottom: printSpecs.top + anchors.bottomMargin: Math.floor(UM.Theme.getSize("sidebar_margin").height * 2 + UM.Theme.getSize("progressbar").height + UM.Theme.getFont("default_bold").pixelSize) + } + + Item + { + id: printSpecs + anchors.left: parent.left + anchors.bottom: parent.bottom + anchors.leftMargin: UM.Theme.getSize("sidebar_margin").width + anchors.bottomMargin: UM.Theme.getSize("sidebar_margin").height + height: timeDetails.height + costSpec.height + width: base.width - (saveButton.buttonRowWidth + UM.Theme.getSize("sidebar_margin").width) + visible: !monitoringPrint + clip: true + + Label + { + id: timeDetails + anchors.left: parent.left + anchors.bottom: costSpec.top + font: UM.Theme.getFont("large") + color: UM.Theme.getColor("text_subtext") + text: (!base.printDuration || !base.printDuration.valid) ? catalog.i18nc("@label Hours and minutes", "00h 00min") : base.printDuration.getDisplayString(UM.DurationFormat.Short) + + MouseArea + { + id: timeDetailsMouseArea + anchors.fill: parent + hoverEnabled: true + + onEntered: + { + if(base.printDuration.valid && !base.printDuration.isTotalDurationZero) + { + // All the time information for the different features is achieved + var print_time = PrintInformation.getFeaturePrintTimes(); + var total_seconds = parseInt(base.printDuration.getDisplayString(UM.DurationFormat.Seconds)) + + // A message is created and displayed when the user hover the time label + var tooltip_html = "%1
    ".arg(catalog.i18nc("@tooltip", "Time specification")); + for(var feature in print_time) + { + if(!print_time[feature].isTotalDurationZero) + { + tooltip_html += "" + + "".arg(print_time[feature].getDisplayString(UM.DurationFormat.ISO8601).slice(0,-3)) + + "".arg(Math.round(100 * parseInt(print_time[feature].getDisplayString(UM.DurationFormat.Seconds)) / total_seconds)) + + ""; + } + } + tooltip_html += "
    " + feature + ":  %1  %1%
    "; + + base.showTooltip(parent, Qt.point(-UM.Theme.getSize("sidebar_margin").width, 0), tooltip_html); + } + } + onExited: + { + base.hideTooltip(); + } + } + } + + Label + { + function formatRow(items) + { + var row_html = ""; + for(var item = 0; item < items.length; item++) + { + if (item == 0) + { + row_html += "%1".arg(items[item]); + } + else + { + row_html += "  %1".arg(items[item]); + } + } + row_html += ""; + return row_html; + } + + function getSpecsData() + { + var lengths = []; + var total_length = 0; + var weights = []; + var total_weight = 0; + var costs = []; + var total_cost = 0; + var some_costs_known = false; + var names = []; + if(base.printMaterialLengths) + { + for(var index = 0; index < base.printMaterialLengths.length; index++) + { + if(base.printMaterialLengths[index] > 0) + { + names.push(base.printMaterialNames[index]); + lengths.push(base.printMaterialLengths[index].toFixed(2)); + weights.push(String(Math.floor(base.printMaterialWeights[index]))); + var cost = base.printMaterialCosts[index] == undefined ? 0 : base.printMaterialCosts[index].toFixed(2); + costs.push(cost); + if(cost > 0) + { + some_costs_known = true; + } + + total_length += base.printMaterialLengths[index]; + total_weight += base.printMaterialWeights[index]; + total_cost += base.printMaterialCosts[index]; + } + } + } + if(lengths.length == 0) + { + lengths = ["0.00"]; + weights = ["0"]; + costs = ["0.00"]; + } + + var tooltip_html = "%1
    ".arg(catalog.i18nc("@label", "Cost specification")); + for(var index = 0; index < lengths.length; index++) + { + tooltip_html += formatRow([ + "%1:".arg(names[index]), + catalog.i18nc("@label m for meter", "%1m").arg(lengths[index]), + catalog.i18nc("@label g for grams", "%1g").arg(weights[index]), + "%1 %2".arg(UM.Preferences.getValue("cura/currency")).arg(costs[index]), + ]); + } + if(lengths.length > 1) + { + tooltip_html += formatRow([ + catalog.i18nc("@label", "Total:"), + catalog.i18nc("@label m for meter", "%1m").arg(total_length.toFixed(2)), + catalog.i18nc("@label g for grams", "%1g").arg(Math.round(total_weight)), + "%1 %2".arg(UM.Preferences.getValue("cura/currency")).arg(total_cost.toFixed(2)), + ]); + } + tooltip_html += "
    "; + tooltipText = tooltip_html; + + return tooltipText + } + + id: costSpec + anchors.left: parent.left + anchors.bottom: parent.bottom + font: UM.Theme.getFont("very_small") + color: UM.Theme.getColor("text_subtext") + elide: Text.ElideMiddle + width: parent.width + property string tooltipText + text: + { + var lengths = []; + var weights = []; + var costs = []; + var someCostsKnown = false; + if(base.printMaterialLengths) { + for(var index = 0; index < base.printMaterialLengths.length; index++) + { + if(base.printMaterialLengths[index] > 0) + { + lengths.push(base.printMaterialLengths[index].toFixed(2)); + weights.push(String(Math.floor(base.printMaterialWeights[index]))); + var cost = base.printMaterialCosts[index] == undefined ? 0 : base.printMaterialCosts[index].toFixed(2); + costs.push(cost); + if(cost > 0) + { + someCostsKnown = true; + } + } + } + } + if(lengths.length == 0) + { + lengths = ["0.00"]; + weights = ["0"]; + costs = ["0.00"]; + } + if(someCostsKnown) + { + return catalog.i18nc("@label Print estimates: m for meters, g for grams, %4 is currency and %3 is print cost", "%1m / ~ %2g / ~ %4 %3").arg(lengths.join(" + ")) + .arg(weights.join(" + ")).arg(costs.join(" + ")).arg(UM.Preferences.getValue("cura/currency")); + } + else + { + return catalog.i18nc("@label Print estimates: m for meters, g for grams", "%1m / ~ %2g").arg(lengths.join(" + ")).arg(weights.join(" + ")); + } + } + MouseArea + { + id: costSpecMouseArea + anchors.fill: parent + hoverEnabled: true + + onEntered: + { + + if(base.printDuration.valid && !base.printDuration.isTotalDurationZero) + { + var show_data = costSpec.getSpecsData() + + base.showTooltip(parent, Qt.point(-UM.Theme.getSize("sidebar_margin").width, 0), show_data); + } + } + onExited: + { + base.hideTooltip(); + } + } + } + } + + // SaveButton and MonitorButton are actually the bottom footer panels. + // "!monitoringPrint" currently means "show-settings-mode" + SaveButton + { + id: saveButton + implicitWidth: base.width + anchors.top: footerSeparator.bottom + anchors.topMargin: UM.Theme.getSize("sidebar_margin").height + anchors.bottom: parent.bottom + visible: !monitoringPrint + } + + MonitorButton + { + id: monitorButton + implicitWidth: base.width + anchors.top: footerSeparator.bottom + anchors.topMargin: UM.Theme.getSize("sidebar_margin").height + anchors.bottom: parent.bottom + visible: monitoringPrint + } + + SidebarTooltip + { + id: tooltip; + } + + // Setting mode: Recommended or Custom + ListModel + { + id: modesListModel; + } + + SidebarSimple + { + id: sidebarSimple; + visible: false; + + onShowTooltip: base.showTooltip(item, location, text) + onHideTooltip: base.hideTooltip() + } + + SidebarAdvanced + { + id: sidebarAdvanced; + visible: false; + + onShowTooltip: base.showTooltip(item, location, text) + onHideTooltip: base.hideTooltip() + } + + Component.onCompleted: + { + modesListModel.append({ + text: catalog.i18nc("@title:tab", "Recommended"), + tooltipText: catalog.i18nc("@tooltip", "Recommended Print Setup

    Print with the recommended settings for the selected printer, material and quality."), + item: sidebarSimple + }) + modesListModel.append({ + text: catalog.i18nc("@title:tab", "Custom"), + tooltipText: catalog.i18nc("@tooltip", "Custom Print Setup

    Print with finegrained control over every last bit of the slicing process."), + item: sidebarAdvanced + }) + sidebarContents.push({ "item": modesListModel.get(base.currentModeIndex).item, "immediate": true }); + + var index = Math.floor(UM.Preferences.getValue("cura/active_mode")) + if(index) + { + currentModeIndex = index; + } + } + + UM.SettingPropertyProvider + { + id: machineExtruderCount + containerStackId: Cura.MachineManager.activeMachineId + key: "machine_extruder_count" + watchedProperties: [ "value" ] + storeIndex: 0 + } + + UM.SettingPropertyProvider + { + id: machineHeatedBed + containerStackId: Cura.MachineManager.activeMachineId + key: "machine_heated_bed" + watchedProperties: [ "value" ] + storeIndex: 0 + } } diff --git a/resources/qml/SidebarSimple.qml b/resources/qml/SidebarSimple.qml index e923963355..4b182c7a72 100644 --- a/resources/qml/SidebarSimple.qml +++ b/resources/qml/SidebarSimple.qml @@ -403,8 +403,6 @@ Item } } - - // // Infill // @@ -568,18 +566,24 @@ Item model: infillModel anchors.fill: parent - property int activeIndex: { + function activeIndex () { for (var i = 0; i < infillModel.count; i++) { var density = parseInt(infillDensity.properties.value) var steps = parseInt(infillSteps.properties.value) var infillModelItem = infillModel.get(i) - if (density >= infillModelItem.percentageMin + // TODO: somehow this print causes this method not to crash QML when the sidebar view gets unloaded (when switching states) + // TODO: That should be fixed :P + print("test", density, steps, infillModelItem) + + if (infillModelItem + && density >= infillModelItem.percentageMin && density <= infillModelItem.percentageMax && steps >= infillModelItem.stepsMin - && steps <= infillModelItem.stepsMax){ - return i - } + && steps <= infillModelItem.stepsMax + ){ + return i + } } return -1 } @@ -587,7 +591,7 @@ Item Rectangle { anchors.fill: parent - visible: infillIconList.activeIndex == index + visible: infillIconList.activeIndex() == index border.width: UM.Theme.getSize("default_lining").width border.color: UM.Theme.getColor("quality_slider_unavailable") From 9702ffb79432f0c7ed7242c9d59bee78c4bd2984 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Wed, 6 Dec 2017 18:09:02 +0100 Subject: [PATCH 45/62] Small fixes to sidebar layout after refactoring --- resources/qml/Settings/SettingView.qml | 2 +- resources/qml/Sidebar.qml | 14 +++++++++++++- resources/qml/SidebarHeader.qml | 8 ++++---- resources/qml/SidebarSimple.qml | 6 +----- 4 files changed, 19 insertions(+), 11 deletions(-) diff --git a/resources/qml/Settings/SettingView.qml b/resources/qml/Settings/SettingView.qml index 2079710315..1ebb5cc40e 100644 --- a/resources/qml/Settings/SettingView.qml +++ b/resources/qml/Settings/SettingView.qml @@ -24,7 +24,7 @@ Item { id: globalProfileRow height: UM.Theme.getSize("sidebar_setup").height -// visible: !sidebar.monitoringPrint && !sidebar.hideSettings + visible: !sidebar.monitoringPrint && !sidebar.hideSettings anchors { diff --git a/resources/qml/Sidebar.qml b/resources/qml/Sidebar.qml index a02454d298..6bc0903059 100644 --- a/resources/qml/Sidebar.qml +++ b/resources/qml/Sidebar.qml @@ -10,7 +10,6 @@ import UM 1.2 as UM import Cura 1.0 as Cura import "Menus" - Rectangle { id: base; @@ -92,6 +91,7 @@ Rectangle id: header width: parent.width visible: machineExtruderCount.properties.value > 1 || Cura.MachineManager.hasMaterials || Cura.MachineManager.hasVariants + onShowTooltip: base.showTooltip(item, location, text) onHideTooltip: base.hideTooltip() } @@ -106,6 +106,15 @@ Rectangle anchors.topMargin: visible ? UM.Theme.getSize("sidebar_margin").height : 0 } + onCurrentModeIndexChanged: + { + UM.Preferences.setValue("cura/active_mode", currentModeIndex); + if(modesListModel.count > base.currentModeIndex) + { + sidebarContents.push({ "item": modesListModel.get(base.currentModeIndex).item, "replace": true }); + } + } + Label { id: settingsModeLabel text: !hideSettings ? catalog.i18nc("@label:listbox", "Print Setup") : catalog.i18nc("@label:listbox","Print Setup disabled\nG-code files cannot be modified"); @@ -540,6 +549,7 @@ Rectangle visible: monitoringPrint } + SidebarTooltip { id: tooltip; @@ -593,6 +603,7 @@ Rectangle UM.SettingPropertyProvider { id: machineExtruderCount + containerStackId: Cura.MachineManager.activeMachineId key: "machine_extruder_count" watchedProperties: [ "value" ] @@ -602,6 +613,7 @@ Rectangle UM.SettingPropertyProvider { id: machineHeatedBed + containerStackId: Cura.MachineManager.activeMachineId key: "machine_heated_bed" watchedProperties: [ "value" ] diff --git a/resources/qml/SidebarHeader.qml b/resources/qml/SidebarHeader.qml index 0f0572a48a..3e1e85824a 100644 --- a/resources/qml/SidebarHeader.qml +++ b/resources/qml/SidebarHeader.qml @@ -40,7 +40,7 @@ Column id: extruderSelectionRow width: parent.width height: Math.floor(UM.Theme.getSize("sidebar_tabs").height * 2 / 3) - visible: machineExtruderCount.properties.value > 1 + visible: machineExtruderCount.properties.value > 1 && !sidebar.monitoringPrint anchors { @@ -229,7 +229,7 @@ Column { id: materialRow height: UM.Theme.getSize("sidebar_setup").height - visible: Cura.MachineManager.hasMaterials + visible: Cura.MachineManager.hasMaterials && !sidebar.monitoringPrint && !sidebar.hideSettings anchors { @@ -279,7 +279,7 @@ Column { id: variantRow height: UM.Theme.getSize("sidebar_setup").height - visible: Cura.MachineManager.hasVariants + visible: Cura.MachineManager.hasVariants && !sidebar.monitoringPrint && !sidebar.hideSettings anchors { @@ -319,7 +319,7 @@ Column { id: materialInfoRow height: Math.floor(UM.Theme.getSize("sidebar_setup").height / 2) - visible: Cura.MachineManager.hasVariants || Cura.MachineManager.hasMaterials + visible: (Cura.MachineManager.hasVariants || Cura.MachineManager.hasMaterials) && !sidebar.monitoringPrint && !sidebar.hideSettings anchors { diff --git a/resources/qml/SidebarSimple.qml b/resources/qml/SidebarSimple.qml index 4b182c7a72..62cf6f9d34 100644 --- a/resources/qml/SidebarSimple.qml +++ b/resources/qml/SidebarSimple.qml @@ -572,11 +572,7 @@ Item var steps = parseInt(infillSteps.properties.value) var infillModelItem = infillModel.get(i) - // TODO: somehow this print causes this method not to crash QML when the sidebar view gets unloaded (when switching states) - // TODO: That should be fixed :P - print("test", density, steps, infillModelItem) - - if (infillModelItem + if (infillModelItem != "undefined" && density >= infillModelItem.percentageMin && density <= infillModelItem.percentageMax && steps >= infillModelItem.stepsMin From 28c5debd61e664a03f72ac27b3f715ade8fcf0fc Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Wed, 6 Dec 2017 18:11:46 +0100 Subject: [PATCH 46/62] Fix hiding topbar buttons in monitor stage --- resources/qml/Topbar.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/qml/Topbar.qml b/resources/qml/Topbar.qml index d50cc64608..550b2dc007 100644 --- a/resources/qml/Topbar.qml +++ b/resources/qml/Topbar.qml @@ -220,7 +220,7 @@ Rectangle id: viewOrientationControl height: 30 spacing: 2 - visible: UM.Controller.activeStage.id != "MonitorStage" + visible: UM.Controller.activeStage.stageId != "MonitorStage" anchors { @@ -301,7 +301,7 @@ Rectangle } style: UM.Theme.styles.combobox - visible: UM.Controller.activeStage.id != "MonitorStage" + visible: UM.Controller.activeStage.stageId != "MonitorStage" model: UM.ViewModel { } textRole: "name" From 548761fcc35d6a68f94a5d0b817eacb8d8152e36 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Wed, 6 Dec 2017 18:15:17 +0100 Subject: [PATCH 47/62] Some cleanup --- plugins/MonitorStage/MonitorStage.py | 37 +++++++++++++++++ resources/qml/Topbar.qml | 62 +--------------------------- 2 files changed, 38 insertions(+), 61 deletions(-) diff --git a/plugins/MonitorStage/MonitorStage.py b/plugins/MonitorStage/MonitorStage.py index 6131e5538b..dd1eca9682 100644 --- a/plugins/MonitorStage/MonitorStage.py +++ b/plugins/MonitorStage/MonitorStage.py @@ -19,3 +19,40 @@ class MonitorStage(CuraStage): sidebar_component_path = os.path.join(Resources.getPath(Application.getInstance().ResourceTypes.QmlFiles), "Sidebar.qml") self.addDisplayComponent("sidebar", sidebar_component_path) self.setIconSource(Application.getInstance().getTheme().getIcon("tab_status_connected")) + +# property string iconSource: +# // { +# // if (!printerConnected) +# // { +# // return UM.Theme.getIcon("tab_status_unknown"); +# // } +# // else if (!printerAcceptsCommands) +# // { +# // return UM.Theme.getIcon("tab_status_unknown"); +# // } +# // +# // if (Cura.MachineManager.printerOutputDevices[0].printerState == "maintenance") +# // { +# // return UM.Theme.getIcon("tab_status_busy"); +# // } +# // +# // switch (Cura.MachineManager.printerOutputDevices[0].jobState) +# // { +# // case "printing": +# // case "pre_print": +# // case "pausing": +# // case "resuming": +# // return UM.Theme.getIcon("tab_status_busy"); +# // case "wait_cleanup": +# // return UM.Theme.getIcon("tab_status_finished"); +# // case "ready": +# // case "": +# // return UM.Theme.getIcon("tab_status_connected") +# // case "paused": +# // return UM.Theme.getIcon("tab_status_paused") +# // case "error": +# // return UM.Theme.getIcon("tab_status_stopped") +# // default: +# // return UM.Theme.getIcon("tab_status_unknown") +# // } +# // } \ No newline at end of file diff --git a/resources/qml/Topbar.qml b/resources/qml/Topbar.qml index 550b2dc007..98f9f94528 100644 --- a/resources/qml/Topbar.qml +++ b/resources/qml/Topbar.qml @@ -16,7 +16,7 @@ Rectangle anchors.left: parent.left anchors.right: parent.right height: UM.Theme.getSize("sidebar_header").height - color: UM.Controller.activeStage.id == "MonitorStage" ? UM.Theme.getColor("topbar_background_color_monitoring") : UM.Theme.getColor("topbar_background_color") + color: UM.Controller.activeStage.stageId == "MonitorStage" ? UM.Theme.getColor("topbar_background_color_monitoring") : UM.Theme.getColor("topbar_background_color") property bool printerConnected: Cura.MachineManager.printerOutputDevices.length != 0 property bool printerAcceptsCommands: printerConnected && Cura.MachineManager.printerOutputDevices[0].acceptsCommands @@ -87,66 +87,6 @@ Rectangle } ExclusiveGroup { id: topbarMenuGroup } - -// Button -// { -// id: showMonitor -// width: UM.Theme.getSize("topbar_button").width -// height: UM.Theme.getSize("sidebar_header").height -// text: catalog.i18nc("@title:tab", "Monitor") -// checkable: true -// checked: isChecked() -// exclusiveGroup: sidebarHeaderBarGroup -// style: UM.Theme.styles.topbar_header_tab_no_overlay -// -// // We use a Qt.binding to re-bind the checkbox state after manually setting it -// // https://stackoverflow.com/questions/38798450/qt-5-7-qml-why-are-my-checkbox-property-bindings-disappearing -// onClicked: { -// base.startMonitoringPrint() -// checked = Qt.binding(isChecked) -// } -// -// function isChecked () { -// return base.monitoringPrint -// } -// -// property string iconSource: -// { -// if (!printerConnected) -// { -// return UM.Theme.getIcon("tab_status_unknown"); -// } -// else if (!printerAcceptsCommands) -// { -// return UM.Theme.getIcon("tab_status_unknown"); -// } -// -// if (Cura.MachineManager.printerOutputDevices[0].printerState == "maintenance") -// { -// return UM.Theme.getIcon("tab_status_busy"); -// } -// -// switch (Cura.MachineManager.printerOutputDevices[0].jobState) -// { -// case "printing": -// case "pre_print": -// case "pausing": -// case "resuming": -// return UM.Theme.getIcon("tab_status_busy"); -// case "wait_cleanup": -// return UM.Theme.getIcon("tab_status_finished"); -// case "ready": -// case "": -// return UM.Theme.getIcon("tab_status_connected") -// case "paused": -// return UM.Theme.getIcon("tab_status_paused") -// case "error": -// return UM.Theme.getIcon("tab_status_stopped") -// default: -// return UM.Theme.getIcon("tab_status_unknown") -// } -// } -// } } ToolButton From 9e3c681d669894c0742a72b00e183a014598428d Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Wed, 6 Dec 2017 18:47:11 +0100 Subject: [PATCH 48/62] Fix top bar tab layout when it has an icon --- resources/qml/Topbar.qml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/resources/qml/Topbar.qml b/resources/qml/Topbar.qml index 98f9f94528..9ad91615ad 100644 --- a/resources/qml/Topbar.qml +++ b/resources/qml/Topbar.qml @@ -75,9 +75,8 @@ Rectangle checkable: true checked: model.active exclusiveGroup: topbarMenuGroup - style: UM.Theme.styles.topbar_header_tab + style: (model.stage.iconSource != "") ? UM.Theme.styles.topbar_header_tab_no_overlay : UM.Theme.styles.topbar_header_tab height: UM.Theme.getSize("sidebar_header").height - width: UM.Theme.getSize("topbar_button").width onClicked: UM.Controller.setActiveStage(model.id) iconSource: model.stage.iconSource From f6b570e299a70ee3f96f2798a8becf16b0901283 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Thu, 7 Dec 2017 09:13:46 +0100 Subject: [PATCH 49/62] Back to QUrl, fix monitor view overlay --- plugins/MonitorStage/MonitorMainView.qml | 16 ++++++++++++++++ plugins/MonitorStage/MonitorStage.py | 22 +++++++++++++++++++--- resources/qml/Cura.qml | 13 +++++++------ 3 files changed, 42 insertions(+), 9 deletions(-) create mode 100644 plugins/MonitorStage/MonitorMainView.qml diff --git a/plugins/MonitorStage/MonitorMainView.qml b/plugins/MonitorStage/MonitorMainView.qml new file mode 100644 index 0000000000..b5008ba795 --- /dev/null +++ b/plugins/MonitorStage/MonitorMainView.qml @@ -0,0 +1,16 @@ +// Copyright (c) 2017 Ultimaker B.V. + +import QtQuick 2.2 +import QtQuick.Controls 1.1 + +import UM 1.3 as UM +import Cura 1.0 as Cura + +Loader +{ + property real maximumWidth: parent.width + property real maximumHeight: parent.height + + sourceComponent: Cura.MachineManager.printerOutputDevices.length > 0 ? Cura.MachineManager.printerOutputDevices[0].monitorItem: null + visible: sourceComponent != null +} diff --git a/plugins/MonitorStage/MonitorStage.py b/plugins/MonitorStage/MonitorStage.py index dd1eca9682..f577016b2e 100644 --- a/plugins/MonitorStage/MonitorStage.py +++ b/plugins/MonitorStage/MonitorStage.py @@ -2,6 +2,7 @@ # Cura is released under the terms of the LGPLv3 or higher. import os.path from UM.Application import Application +from UM.PluginRegistry import PluginRegistry from UM.Resources import Resources from cura.Stages.CuraStage import CuraStage @@ -11,14 +12,29 @@ class MonitorStage(CuraStage): def __init__(self, parent = None): super().__init__(parent) - Application.getInstance().engineCreatedSignal.connect(self._engineCreated) + + # Wait until QML engine is created, otherwise creating the new QML components will fail + Application.getInstance().engineCreatedSignal.connect(self._setComponents) + # TODO: connect output device state to icon source - def _engineCreated(self): + def _setComponents(self): + self._setMainOverlay() + self._setSidebar() + self._setIconSource() + + def _setMainOverlay(self): + main_component_path = os.path.join(PluginRegistry.getInstance().getPluginPath("MonitorStage"), "MonitorMainView.qml") + self.addDisplayComponent("main", main_component_path) + + def _setSidebar(self): # Note: currently the sidebar component for prepare and monitor stages is the same, this will change with the printer output device refactor! sidebar_component_path = os.path.join(Resources.getPath(Application.getInstance().ResourceTypes.QmlFiles), "Sidebar.qml") self.addDisplayComponent("sidebar", sidebar_component_path) - self.setIconSource(Application.getInstance().getTheme().getIcon("tab_status_connected")) + + def _setIconSource(self): + if Application.getInstance().getTheme() is not None: + self.setIconSource(Application.getInstance().getTheme().getIcon("tab_status_connected")) # property string iconSource: # // { diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index 445325df90..3e21bf0468 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -393,12 +393,13 @@ UM.MainWindow right: sidebar.left } -// MouseArea -// { -// anchors.fill: parent -// acceptedButtons: Qt.AllButtons -// onWheel: wheel.accepted = true -// } + MouseArea + { + visible: UM.Controller.activeStage.mainComponent != "" + anchors.fill: parent + acceptedButtons: Qt.AllButtons + onWheel: wheel.accepted = true + } source: UM.Controller.activeStage.mainComponent asynchronous: true From dd92d8d5e093c225f4ccf86b8519551ff8fb548a Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Thu, 7 Dec 2017 09:22:02 +0100 Subject: [PATCH 50/62] Re-implement monitor view overlay when there is no monitor component --- plugins/MonitorStage/MonitorMainView.qml | 33 ++++++++++++++++++++---- plugins/MonitorStage/MonitorStage.py | 2 +- 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/plugins/MonitorStage/MonitorMainView.qml b/plugins/MonitorStage/MonitorMainView.qml index b5008ba795..038403e6d3 100644 --- a/plugins/MonitorStage/MonitorMainView.qml +++ b/plugins/MonitorStage/MonitorMainView.qml @@ -6,11 +6,34 @@ import QtQuick.Controls 1.1 import UM 1.3 as UM import Cura 1.0 as Cura -Loader +Item { - property real maximumWidth: parent.width - property real maximumHeight: parent.height + // We show a nice overlay on the 3D viewer when the current output device has no monitor view + Rectangle + { + id: viewportOverlay - sourceComponent: Cura.MachineManager.printerOutputDevices.length > 0 ? Cura.MachineManager.printerOutputDevices[0].monitorItem: null - visible: sourceComponent != null + color: UM.Theme.getColor("viewport_overlay") + width: parent.width + height: parent.height + visible: monitorViewComponent.sourceComponent == null ? 1 : 0 + + MouseArea + { + anchors.fill: parent + acceptedButtons: Qt.AllButtons + onWheel: wheel.accepted = true + } + } + + Loader + { + id: monitorViewComponent + + property real maximumWidth: parent.width + property real maximumHeight: parent.height + + sourceComponent: Cura.MachineManager.printerOutputDevices.length > 0 ? Cura.MachineManager.printerOutputDevices[0].monitorItem: null + visible: sourceComponent != null + } } diff --git a/plugins/MonitorStage/MonitorStage.py b/plugins/MonitorStage/MonitorStage.py index f577016b2e..f19d1f895e 100644 --- a/plugins/MonitorStage/MonitorStage.py +++ b/plugins/MonitorStage/MonitorStage.py @@ -28,7 +28,7 @@ class MonitorStage(CuraStage): self.addDisplayComponent("main", main_component_path) def _setSidebar(self): - # Note: currently the sidebar component for prepare and monitor stages is the same, this will change with the printer output device refactor! + # TODO: currently the sidebar component for prepare and monitor stages is the same, this will change with the printer output device refactor! sidebar_component_path = os.path.join(Resources.getPath(Application.getInstance().ResourceTypes.QmlFiles), "Sidebar.qml") self.addDisplayComponent("sidebar", sidebar_component_path) From 9d2529c6d4f0ec06e11fcd533f80daf97c796155 Mon Sep 17 00:00:00 2001 From: gitname Date: Thu, 7 Dec 2017 00:54:20 -0800 Subject: [PATCH 51/62] Fix typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ba6a986093..90f17a5463 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ Cura This is the new, shiny 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 the old code started to become a unmaintainable. +We re-worked the whole GUI code at Ultimaker, because the old code started to become unmaintainable. Logging Issues From f91a4db6173bc3e264da53eb8368e78b01104975 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Thu, 7 Dec 2017 10:38:06 +0100 Subject: [PATCH 52/62] Set stage icon for monitor depending on output device state, prevent crash when accessing back-end from unloaded component --- plugins/MonitorStage/MonitorStage.py | 75 ++++++++++++++-------------- resources/qml/Cura.qml | 2 - resources/qml/SaveButton.qml | 2 +- 3 files changed, 38 insertions(+), 41 deletions(-) diff --git a/plugins/MonitorStage/MonitorStage.py b/plugins/MonitorStage/MonitorStage.py index f19d1f895e..0736f49858 100644 --- a/plugins/MonitorStage/MonitorStage.py +++ b/plugins/MonitorStage/MonitorStage.py @@ -16,7 +16,8 @@ class MonitorStage(CuraStage): # Wait until QML engine is created, otherwise creating the new QML components will fail Application.getInstance().engineCreatedSignal.connect(self._setComponents) - # TODO: connect output device state to icon source + # Update the status icon when the output device is changed + Application.getInstance().getOutputDeviceManager().activeDeviceChanged.connect(self._setIconSource) def _setComponents(self): self._setMainOverlay() @@ -34,41 +35,39 @@ class MonitorStage(CuraStage): def _setIconSource(self): if Application.getInstance().getTheme() is not None: - self.setIconSource(Application.getInstance().getTheme().getIcon("tab_status_connected")) + icon_name = self._getActiveOutputDeviceStatusIcon() + self.setIconSource(Application.getInstance().getTheme().getIcon(icon_name)) -# property string iconSource: -# // { -# // if (!printerConnected) -# // { -# // return UM.Theme.getIcon("tab_status_unknown"); -# // } -# // else if (!printerAcceptsCommands) -# // { -# // return UM.Theme.getIcon("tab_status_unknown"); -# // } -# // -# // if (Cura.MachineManager.printerOutputDevices[0].printerState == "maintenance") -# // { -# // return UM.Theme.getIcon("tab_status_busy"); -# // } -# // -# // switch (Cura.MachineManager.printerOutputDevices[0].jobState) -# // { -# // case "printing": -# // case "pre_print": -# // case "pausing": -# // case "resuming": -# // return UM.Theme.getIcon("tab_status_busy"); -# // case "wait_cleanup": -# // return UM.Theme.getIcon("tab_status_finished"); -# // case "ready": -# // case "": -# // return UM.Theme.getIcon("tab_status_connected") -# // case "paused": -# // return UM.Theme.getIcon("tab_status_paused") -# // case "error": -# // return UM.Theme.getIcon("tab_status_stopped") -# // default: -# // return UM.Theme.getIcon("tab_status_unknown") -# // } -# // } \ No newline at end of file + ## Find the correct status icon depending on the active output device state + def _getActiveOutputDeviceStatusIcon(self): + output_device = Application.getInstance().getOutputDeviceManager().getActiveDevice() + + if not output_device: + return "tab_status_unknown" + + if hasattr(output_device, "acceptsCommands") and not output_device.acceptsCommands: + return "tab_status_unknown" + + if not hasattr(output_device, "printerState") or not hasattr(output_device, "jobState"): + return "tab_status_unknown" + + # TODO: refactor to use enum instead of hardcoded strings? + if output_device.printerState == "maintenance": + return "tab_status_busy" + + if output_device.jobState in ["printing", "pre_print", "pausing", "resuming"]: + return "tab_status_busy" + + if output_device.jobState == "wait_cleanup": + return "tab_status_finished" + + if output_device.jobState in ["ready", ""]: + return "tab_status_connected" + + if output_device.jobState == "paused": + return "tab_status_paused" + + if output_device.jobState == "error": + return "tab_status_stopped" + + return "tab_status_unknown" diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index 3e21bf0468..0bab1ee5d7 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -378,7 +378,6 @@ UM.MainWindow z: 1 source: UM.Controller.activeStage.sidebarComponent - asynchronous: true } Loader @@ -402,7 +401,6 @@ UM.MainWindow } source: UM.Controller.activeStage.mainComponent - asynchronous: true } UM.MessageStack diff --git a/resources/qml/SaveButton.qml b/resources/qml/SaveButton.qml index b258ecad43..390b868a9b 100644 --- a/resources/qml/SaveButton.qml +++ b/resources/qml/SaveButton.qml @@ -46,7 +46,7 @@ Item { } function sliceOrStopSlicing() { - if ([1, 5].indexOf(UM.Backend.state) != -1) { + if (backend != "undefined" && [1, 5].indexOf(UM.Backend.state) != -1) { backend.forceSlice(); } else { backend.stopSlicing(); From 71ae78f7b6213884ba126d2d0168b95b1c8e8ee2 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Thu, 7 Dec 2017 10:45:54 +0100 Subject: [PATCH 53/62] Fix monitoring sidebar top margin --- resources/qml/Sidebar.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/qml/Sidebar.qml b/resources/qml/Sidebar.qml index 6bc0903059..8744b17906 100644 --- a/resources/qml/Sidebar.qml +++ b/resources/qml/Sidebar.qml @@ -282,7 +282,7 @@ Rectangle Loader { anchors.bottom: footerSeparator.top - anchors.top: headerSeparator.bottom + anchors.top: monitoringPrint ? base.top : headerSeparator.bottom anchors.left: base.left anchors.right: base.right source: From 7a6d75fd08cbae3c6e55ff09c1c3f4857c939033 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Thu, 7 Dec 2017 11:02:52 +0100 Subject: [PATCH 54/62] Fix sidebar top margin for um3 network monitor controls --- resources/qml/Sidebar.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/qml/Sidebar.qml b/resources/qml/Sidebar.qml index 8744b17906..a459b481db 100644 --- a/resources/qml/Sidebar.qml +++ b/resources/qml/Sidebar.qml @@ -263,7 +263,7 @@ Rectangle { id: controlItem anchors.bottom: footerSeparator.top - anchors.top: headerSeparator.bottom + anchors.top: monitoringPrint ? base.top : headerSeparator.bottom anchors.left: base.left anchors.right: base.right sourceComponent: From faf77b27954491eac9f19122a33241528391c6e1 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Thu, 7 Dec 2017 11:50:01 +0100 Subject: [PATCH 55/62] Cleanup --- resources/qml/Topbar.qml | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/resources/qml/Topbar.qml b/resources/qml/Topbar.qml index 9ad91615ad..99910b24a2 100644 --- a/resources/qml/Topbar.qml +++ b/resources/qml/Topbar.qml @@ -21,18 +21,6 @@ Rectangle property bool printerConnected: Cura.MachineManager.printerOutputDevices.length != 0 property bool printerAcceptsCommands: printerConnected && Cura.MachineManager.printerOutputDevices[0].acceptsCommands - // update monitoring status when event was triggered outside topbar -// Component.onCompleted: { -// startMonitoringPrint.connect(function () { -// base.monitoringPrint = true -// UM.Controller.disableModelRendering() -// }) -// stopMonitoringPrint.connect(function () { -// base.monitoringPrint = false -// UM.Controller.enableModelRendering() -// }) -// } - UM.I18nCatalog { id: catalog From acbef72b68e041ff5502f961464e61116611b973 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Thu, 7 Dec 2017 11:52:15 +0100 Subject: [PATCH 56/62] Cleanup --- resources/qml/Cura.qml | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index 0bab1ee5d7..f5fa922794 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -18,19 +18,6 @@ UM.MainWindow //: Cura application window title title: catalog.i18nc("@title:window","Ultimaker Cura"); viewportRect: Qt.rect(0, 0, (base.width - sidebar.width) / base.width, 1.0) - property bool showPrintMonitor: false - - Connections - { - target: Printer - onShowPrintMonitor: { - if (show) { - topbar.startMonitoringPrint() - } else { - topbar.stopMonitoringPrint() - } - } - } Component.onCompleted: { From 9bf954643c2fc2b71f938980efb68350a2fbcd6e Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Thu, 7 Dec 2017 11:56:52 +0100 Subject: [PATCH 57/62] Use new method of setting active stage when needing to switch to monitor --- cura/CuraApplication.py | 1 - .../UM3NetworkPrinting/NetworkClusterPrinterOutputDevice.py | 2 +- plugins/UM3NetworkPrinting/NetworkPrinterOutputDevice.py | 6 +++--- plugins/USBPrinting/USBPrinterOutputDevice.py | 4 ++-- 4 files changed, 6 insertions(+), 7 deletions(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index c9466a0093..a5ae286b1e 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -391,7 +391,6 @@ class CuraApplication(QtApplication): def needToShowUserAgreement(self): return self._need_to_show_user_agreement - def setNeedToShowUserAgreement(self, set_value = True): self._need_to_show_user_agreement = set_value diff --git a/plugins/UM3NetworkPrinting/NetworkClusterPrinterOutputDevice.py b/plugins/UM3NetworkPrinting/NetworkClusterPrinterOutputDevice.py index 44a2e8b743..853ef72f72 100644 --- a/plugins/UM3NetworkPrinting/NetworkClusterPrinterOutputDevice.py +++ b/plugins/UM3NetworkPrinting/NetworkClusterPrinterOutputDevice.py @@ -698,7 +698,7 @@ class NetworkClusterPrinterOutputDevice(NetworkPrinterOutputDevice.NetworkPrinte if self._reply: self._reply.abort() self._stage = OutputStage.ready - Application.getInstance().showPrintMonitor.emit(False) + Application.getInstance().getController().setActiveStage("PrepareStage") @pyqtSlot(int, result=str) def formatDuration(self, seconds): diff --git a/plugins/UM3NetworkPrinting/NetworkPrinterOutputDevice.py b/plugins/UM3NetworkPrinting/NetworkPrinterOutputDevice.py index d8dd780ed5..3a48bab11b 100755 --- a/plugins/UM3NetworkPrinting/NetworkPrinterOutputDevice.py +++ b/plugins/UM3NetworkPrinting/NetworkPrinterOutputDevice.py @@ -672,7 +672,7 @@ class NetworkPrinterOutputDevice(PrinterOutputDevice): Logger.log("d", "Attempting to perform an action without authentication for printer %s. Auth state is %s", self._key, self._authentication_state) return - Application.getInstance().showPrintMonitor.emit(True) + Application.getInstance().getController().setActiveStage("MonitorStage") self._print_finished = True self.writeStarted.emit(self) self._gcode = getattr(Application.getInstance().getController().getScene(), "gcode_list") @@ -767,7 +767,7 @@ class NetworkPrinterOutputDevice(PrinterOutputDevice): if button == QMessageBox.Yes: self.startPrint() else: - Application.getInstance().showPrintMonitor.emit(False) + Application.getInstance().getController().setActiveStage("PrepareStage") # For some unknown reason Cura on OSX will hang if we do the call back code # immediately without first returning and leaving QML's event system. QTimer.singleShot(100, delayedCallback) @@ -850,7 +850,7 @@ class NetworkPrinterOutputDevice(PrinterOutputDevice): self._write_finished = True # post_reply does not always exist, so make sure we unblock writing if self._post_reply: self._finalizePostReply() - Application.getInstance().showPrintMonitor.emit(False) + Application.getInstance().getController().setActiveStage("PrepareStage") ## Attempt to start a new print. # This function can fail to actually start a print due to not being authenticated or another print already diff --git a/plugins/USBPrinting/USBPrinterOutputDevice.py b/plugins/USBPrinting/USBPrinterOutputDevice.py index 77f45ee6d4..1930f5402b 100644 --- a/plugins/USBPrinting/USBPrinterOutputDevice.py +++ b/plugins/USBPrinting/USBPrinterOutputDevice.py @@ -490,7 +490,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice): self.setJobName(file_name) self._print_estimated_time = int(Application.getInstance().getPrintInformation().currentPrintTime.getDisplayString(DurationFormat.Format.Seconds)) - Application.getInstance().showPrintMonitor.emit(True) + Application.getInstance().getController().setActiveStage("MonitorStage") self.startPrint() def _setEndstopState(self, endstop_key, value): @@ -698,7 +698,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice): self._is_printing = False self._is_paused = False self._updateJobState("ready") - Application.getInstance().showPrintMonitor.emit(False) + Application.getInstance().getController().setActiveStage("PrepareStage") ## Check if the process did not encounter an error yet. def hasError(self): From c6e72abfbe8d8a8a58c7de67070402adb243f1f2 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Thu, 7 Dec 2017 12:00:07 +0100 Subject: [PATCH 58/62] Add switch to monitor support for legacy plugin that emit signal to switch --- resources/qml/Cura.qml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index f5fa922794..aedf5d0fa8 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -18,6 +18,21 @@ UM.MainWindow //: Cura application window title title: catalog.i18nc("@title:window","Ultimaker Cura"); viewportRect: Qt.rect(0, 0, (base.width - sidebar.width) / base.width, 1.0) + property bool showPrintMonitor: false + + // This connection is here to support legacy printer output devices that use the showPrintMonitor signal on Application to switch to the monitor stage + // It should be phased out in newer plugin versions. + Connections + { + target: Printer + onShowPrintMonitor: { + if (show) { + UM.Controller.setActiveStage("MonitorStage") + } else { + UM.Controller.setActiveStage("PrepareStage") + } + } + } Component.onCompleted: { From 21fffcc099f527d74f4290677a49deca6c6a861c Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Fri, 8 Dec 2017 10:40:20 +0100 Subject: [PATCH 59/62] Compare linux distro names in lower cases --- cura_app.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cura_app.py b/cura_app.py index c6fcb5cf5b..7583dd054e 100755 --- a/cura_app.py +++ b/cura_app.py @@ -12,7 +12,8 @@ from UM.Platform import Platform #WORKAROUND: GITHUB-88 GITHUB-385 GITHUB-612 if Platform.isLinux(): # Needed for platform.linux_distribution, which is not available on Windows and OSX # For Ubuntu: https://bugs.launchpad.net/ubuntu/+source/python-qt4/+bug/941826 - if platform.linux_distribution()[0] in ("debian", "Ubuntu", "LinuxMint", "Fedora"): # TODO: Needs a "if X11_GFX == 'nvidia'" here. The workaround is only needed on Ubuntu+NVidia drivers. Other drivers are not affected, but fine with this fix. + linux_distro_name = platform.linux_distribution()[0].lower() + if linux_distro_name in ("debian", "ubuntu", "linuxmint", "fedora"): # TODO: Needs a "if X11_GFX == 'nvidia'" here. The workaround is only needed on Ubuntu+NVidia drivers. Other drivers are not affected, but fine with this fix. import ctypes from ctypes.util import find_library libGL = find_library("GL") From b4e7216f5b4744bb36093df12c37b10d4f9a60a6 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Fri, 8 Dec 2017 11:08:24 +0100 Subject: [PATCH 60/62] Always show extruder tabs in Machine Settings dialog CURA-4693 --- .../MachineSettingsAction/MachineSettingsAction.qml | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/plugins/MachineSettingsAction/MachineSettingsAction.qml b/plugins/MachineSettingsAction/MachineSettingsAction.qml index f50dd9d206..b36fb989f0 100644 --- a/plugins/MachineSettingsAction/MachineSettingsAction.qml +++ b/plugins/MachineSettingsAction/MachineSettingsAction.qml @@ -22,7 +22,7 @@ Cura.MachineAction onModelChanged: { var extruderCount = base.extrudersModel.rowCount(); - base.extruderTabsCount = extruderCount > 1 ? extruderCount : 0; + base.extruderTabsCount = extruderCount; } } @@ -241,7 +241,6 @@ Cura.MachineAction UM.TooltipArea { - visible: manager.definedExtruderCount > 1 height: childrenRect.height width: childrenRect.width text: machineExtruderCountProvider.properties.description @@ -291,15 +290,6 @@ Cura.MachineAction property var afterOnEditingFinished: manager.updateMaterialForDiameter property string label: catalog.i18nc("@label", "Material diameter") } - Loader - { - id: nozzleSizeField - visible: !Cura.MachineManager.hasVariants && machineExtruderCountProvider.properties.value == 1 - sourceComponent: numericTextFieldWithUnit - property string settingKey: "machine_nozzle_size" - property string label: catalog.i18nc("@label", "Nozzle size") - property string unit: catalog.i18nc("@label", "mm") - } } } From 8cd943949792152796859b55b84b56afeb2fc68f Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Fri, 8 Dec 2017 14:33:21 +0100 Subject: [PATCH 61/62] ignore octoprint plugin --- .gitignore | 1 + resources/qml/MonitorButton.qml | 2 +- resources/qml/SaveButton.qml | 22 +++++++++++----------- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/.gitignore b/.gitignore index 7284597fa9..8d9ba4b2d2 100644 --- a/.gitignore +++ b/.gitignore @@ -46,6 +46,7 @@ plugins/cura-siemensnx-plugin plugins/CuraVariSlicePlugin plugins/CuraLiveScriptingPlugin plugins/CuraPrintProfileCreator +plugins/OctoPrintPlugin #Build stuff CMakeCache.txt diff --git a/resources/qml/MonitorButton.qml b/resources/qml/MonitorButton.qml index 29b00f50e6..87eceb69cd 100644 --- a/resources/qml/MonitorButton.qml +++ b/resources/qml/MonitorButton.qml @@ -18,7 +18,6 @@ Item property bool printerConnected: Cura.MachineManager.printerOutputDevices.length != 0 property bool printerAcceptsCommands: printerConnected && Cura.MachineManager.printerOutputDevices[0].acceptsCommands property real progress: printerConnected ? Cura.MachineManager.printerOutputDevices[0].progress : 0 - property int backendState: UM.Backend.state property bool showProgress: { // determine if we need to show the progress bar + percentage @@ -203,6 +202,7 @@ Item target: Printer onAdditionalComponentsChanged: { + print("areaId", areaId) if(areaId == "monitorButtons") { for (var component in CuraApplication.additionalComponents["monitorButtons"]) { CuraApplication.additionalComponents["monitorButtons"][component].parent = additionalComponentsRow diff --git a/resources/qml/SaveButton.qml b/resources/qml/SaveButton.qml index 390b868a9b..25bc10d122 100644 --- a/resources/qml/SaveButton.qml +++ b/resources/qml/SaveButton.qml @@ -28,6 +28,10 @@ Item { return catalog.i18nc("@label:PrintjobStatus", "Please load a 3D model"); } + if (base.backendState == "undefined") { + return "" + } + switch(base.backendState) { case 1: @@ -81,7 +85,7 @@ Item { height: parent.height color: UM.Theme.getColor("progressbar_control") radius: UM.Theme.getSize("progressbar_radius").width - visible: base.backendState == 2 ? true : false + visible: (base.backendState != "undefined" && base.backendState == 2) ? true : false } } @@ -159,10 +163,8 @@ Item { tooltip: [1, 5].indexOf(UM.Backend.state) != -1 ? catalog.i18nc("@info:tooltip","Slice current printjob") : catalog.i18nc("@info:tooltip","Cancel slicing process") // 1 = not started, 2 = Processing - enabled: (base.backendState == 1 || base.backendState == 2) && base.activity == true - visible: { - return !autoSlice && (base.backendState == 1 || base.backendState == 2) && base.activity == true; - } + enabled: base.backendState != "undefined" && (base.backendState == 1 || base.backendState == 2) && base.activity == true + visible: base.backendState != "undefined" && !autoSlice && (base.backendState == 1 || base.backendState == 2) && base.activity == true property bool autoSlice height: UM.Theme.getSize("save_button_save_to_button").height @@ -235,10 +237,8 @@ Item { tooltip: UM.OutputDeviceManager.activeDeviceDescription; // 3 = done, 5 = disabled - enabled: (base.backendState == 3 || base.backendState == 5) && base.activity == true - visible: { - return autoSlice || ((base.backendState == 3 || base.backendState == 5) && base.activity == true); - } + enabled: base.backendState != "undefined" && (base.backendState == 3 || base.backendState == 5) && base.activity == true + visible: base.backendState != "undefined" && autoSlice || ((base.backendState == 3 || base.backendState == 5) && base.activity == true) property bool autoSlice height: UM.Theme.getSize("save_button_save_to_button").height @@ -315,8 +315,8 @@ Item { width: UM.Theme.getSize("save_button_save_to_button").height height: UM.Theme.getSize("save_button_save_to_button").height // 3 = Done, 5 = Disabled - enabled: (base.backendState == 3 || base.backendState == 5) && base.activity == true - visible: (devicesModel.deviceCount > 1) && (base.backendState == 3 || base.backendState == 5) && base.activity == true + enabled: base.backendState != "undefined" && (base.backendState == 3 || base.backendState == 5) && base.activity == true + visible: base.backendState != "undefined" && (devicesModel.deviceCount > 1) && (base.backendState == 3 || base.backendState == 5) && base.activity == true style: ButtonStyle { From c4d7a33c31463c56ab0cf4247c1c3e51f4cbbb6b Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Fri, 8 Dec 2017 14:56:03 +0100 Subject: [PATCH 62/62] Monitor view component loader should have width and height from parent --- plugins/MonitorStage/MonitorMainView.qml | 7 ++++++- resources/qml/MonitorButton.qml | 1 - 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/plugins/MonitorStage/MonitorMainView.qml b/plugins/MonitorStage/MonitorMainView.qml index 038403e6d3..15b05bed0a 100644 --- a/plugins/MonitorStage/MonitorMainView.qml +++ b/plugins/MonitorStage/MonitorMainView.qml @@ -8,6 +8,9 @@ import Cura 1.0 as Cura Item { + width: parent.width + height: parent.height + // We show a nice overlay on the 3D viewer when the current output device has no monitor view Rectangle { @@ -16,7 +19,6 @@ Item color: UM.Theme.getColor("viewport_overlay") width: parent.width height: parent.height - visible: monitorViewComponent.sourceComponent == null ? 1 : 0 MouseArea { @@ -30,6 +32,9 @@ Item { id: monitorViewComponent + width: parent.width + height: parent.height + property real maximumWidth: parent.width property real maximumHeight: parent.height diff --git a/resources/qml/MonitorButton.qml b/resources/qml/MonitorButton.qml index 87eceb69cd..6454e32117 100644 --- a/resources/qml/MonitorButton.qml +++ b/resources/qml/MonitorButton.qml @@ -202,7 +202,6 @@ Item target: Printer onAdditionalComponentsChanged: { - print("areaId", areaId) if(areaId == "monitorButtons") { for (var component in CuraApplication.additionalComponents["monitorButtons"]) { CuraApplication.additionalComponents["monitorButtons"][component].parent = additionalComponentsRow