Merge branch '4.0' into CURA-6004_missing_tooltips

This commit is contained in:
Diego Prado Gesto 2018-12-19 09:03:31 +01:00
commit 17cddac024
214 changed files with 2873 additions and 5497 deletions

View file

@ -44,7 +44,7 @@ class Account(QObject):
OAUTH_SERVER_URL= self._oauth_root, OAUTH_SERVER_URL= self._oauth_root,
CALLBACK_PORT=self._callback_port, CALLBACK_PORT=self._callback_port,
CALLBACK_URL="http://localhost:{}/callback".format(self._callback_port), CALLBACK_URL="http://localhost:{}/callback".format(self._callback_port),
CLIENT_ID="um---------------ultimaker_cura_drive_plugin", CLIENT_ID="um----------------------------ultimaker_cura",
CLIENT_SCOPES="account.user.read drive.backup.read drive.backup.write packages.download packages.rating.read packages.rating.write", CLIENT_SCOPES="account.user.read drive.backup.read drive.backup.write packages.download packages.rating.read packages.rating.write",
AUTH_DATA_PREFERENCE_KEY="general/ultimaker_auth_data", AUTH_DATA_PREFERENCE_KEY="general/ultimaker_auth_data",
AUTH_SUCCESS_REDIRECT="{}/app/auth-success".format(self._oauth_root), AUTH_SUCCESS_REDIRECT="{}/app/auth-success".format(self._oauth_root),

View file

@ -83,7 +83,14 @@ class BuildVolume(SceneNode):
" with printed models."), title = catalog.i18nc("@info:title", "Build Volume")) " with printed models."), title = catalog.i18nc("@info:title", "Build Volume"))
self._global_container_stack = None self._global_container_stack = None
self._stack_change_timer = QTimer()
self._stack_change_timer.setInterval(100)
self._stack_change_timer.setSingleShot(True)
self._stack_change_timer.timeout.connect(self._onStackChangeTimerFinished)
self._application.globalContainerStackChanged.connect(self._onStackChanged) self._application.globalContainerStackChanged.connect(self._onStackChanged)
self._onStackChanged() self._onStackChanged()
self._engine_ready = False self._engine_ready = False
@ -105,6 +112,8 @@ class BuildVolume(SceneNode):
self._setting_change_timer.setSingleShot(True) self._setting_change_timer.setSingleShot(True)
self._setting_change_timer.timeout.connect(self._onSettingChangeTimerFinished) self._setting_change_timer.timeout.connect(self._onSettingChangeTimerFinished)
# Must be after setting _build_volume_message, apparently that is used in getMachineManager. # Must be after setting _build_volume_message, apparently that is used in getMachineManager.
# activeQualityChanged is always emitted after setActiveVariant, setActiveMaterial and setActiveQuality. # activeQualityChanged is always emitted after setActiveVariant, setActiveMaterial and setActiveQuality.
# Therefore this works. # Therefore this works.
@ -479,6 +488,8 @@ class BuildVolume(SceneNode):
maximum = Vector(max_w - bed_adhesion_size - 1, max_h - self._raft_thickness - self._extra_z_clearance, max_d - disallowed_area_size + bed_adhesion_size - 1) maximum = Vector(max_w - bed_adhesion_size - 1, max_h - self._raft_thickness - self._extra_z_clearance, max_d - disallowed_area_size + bed_adhesion_size - 1)
) )
self._application.getController().getScene()._maximum_bounds = scale_to_max_bounds
self.updateNodeBoundaryCheck() self.updateNodeBoundaryCheck()
def getBoundingBox(self) -> AxisAlignedBox: def getBoundingBox(self) -> AxisAlignedBox:
@ -524,8 +535,11 @@ class BuildVolume(SceneNode):
if extra_z != self._extra_z_clearance: if extra_z != self._extra_z_clearance:
self._extra_z_clearance = extra_z self._extra_z_clearance = extra_z
## Update the build volume visualization
def _onStackChanged(self): def _onStackChanged(self):
self._stack_change_timer.start()
## Update the build volume visualization
def _onStackChangeTimerFinished(self):
if self._global_container_stack: if self._global_container_stack:
self._global_container_stack.propertyChanged.disconnect(self._onSettingPropertyChanged) self._global_container_stack.propertyChanged.disconnect(self._onSettingPropertyChanged)
extruders = ExtruderManager.getInstance().getActiveExtruderStacks() extruders = ExtruderManager.getInstance().getActiveExtruderStacks()

View file

@ -36,18 +36,14 @@ else:
except ImportError: except ImportError:
CuraDebugMode = False # [CodeStyle: Reflecting imported value] CuraDebugMode = False # [CodeStyle: Reflecting imported value]
# List of exceptions that should be considered "fatal" and abort the program. # List of exceptions that should not be considered "fatal" and abort the program.
# These are primarily some exception types that we simply cannot really recover from # These are primarily some exception types that we simply skip
# (MemoryError and SystemError) and exceptions that indicate grave errors in the skip_exception_types = [
# code that cause the Python interpreter to fail (SyntaxError, ImportError). SystemExit,
fatal_exception_types = [ KeyboardInterrupt,
MemoryError, GeneratorExit
SyntaxError,
ImportError,
SystemError,
] ]
class CrashHandler: class CrashHandler:
crash_url = "https://stats.ultimaker.com/api/cura" crash_url = "https://stats.ultimaker.com/api/cura"
@ -70,7 +66,7 @@ class CrashHandler:
# If Cura has fully started, we only show fatal errors. # If Cura has fully started, we only show fatal errors.
# If Cura has not fully started yet, we always show the early crash dialog. Otherwise, Cura will just crash # If Cura has not fully started yet, we always show the early crash dialog. Otherwise, Cura will just crash
# without any information. # without any information.
if has_started and exception_type not in fatal_exception_types: if has_started and exception_type in skip_exception_types:
return return
if not has_started: if not has_started:
@ -387,7 +383,7 @@ class CrashHandler:
Application.getInstance().callLater(self._show) Application.getInstance().callLater(self._show)
def _show(self): def _show(self):
# When the exception is not in the fatal_exception_types list, the dialog is not created, so we don't need to show it # When the exception is in the skip_exception_types list, the dialog is not created, so we don't need to show it
if self.dialog: if self.dialog:
self.dialog.exec_() self.dialog.exec_()
os._exit(1) os._exit(1)

View file

@ -134,7 +134,7 @@ except ImportError:
CuraVersion = "master" # [CodeStyle: Reflecting imported value] CuraVersion = "master" # [CodeStyle: Reflecting imported value]
CuraBuildType = "" CuraBuildType = ""
CuraDebugMode = False CuraDebugMode = False
CuraSDKVersion = "5.0.0" CuraSDKVersion = "6.0.0"
class CuraApplication(QtApplication): class CuraApplication(QtApplication):
@ -181,7 +181,6 @@ class CuraApplication(QtApplication):
# Variables set from CLI # Variables set from CLI
self._files_to_open = [] self._files_to_open = []
self._use_single_instance = False self._use_single_instance = False
self._trigger_early_crash = False # For debug only
self._single_instance = None self._single_instance = None
@ -206,6 +205,8 @@ class CuraApplication(QtApplication):
self._container_manager = None self._container_manager = None
self._object_manager = None self._object_manager = None
self._extruders_model = None
self._extruders_model_with_optional = None
self._build_plate_model = None self._build_plate_model = None
self._multi_build_plate_model = None self._multi_build_plate_model = None
self._setting_visibility_presets_model = None self._setting_visibility_presets_model = None
@ -292,7 +293,10 @@ class CuraApplication(QtApplication):
sys.exit(0) sys.exit(0)
self._use_single_instance = self._cli_args.single_instance self._use_single_instance = self._cli_args.single_instance
self._trigger_early_crash = self._cli_args.trigger_early_crash # FOR TESTING ONLY
if self._cli_args.trigger_early_crash:
assert not "This crash is triggered by the trigger_early_crash command line argument."
for filename in self._cli_args.file: for filename in self._cli_args.file:
self._files_to_open.append(os.path.abspath(filename)) self._files_to_open.append(os.path.abspath(filename))
@ -860,6 +864,19 @@ class CuraApplication(QtApplication):
self._object_manager = ObjectsModel.createObjectsModel() self._object_manager = ObjectsModel.createObjectsModel()
return self._object_manager return self._object_manager
@pyqtSlot(result = QObject)
def getExtrudersModel(self, *args) -> "ExtrudersModel":
if self._extruders_model is None:
self._extruders_model = ExtrudersModel(self)
return self._extruders_model
@pyqtSlot(result = QObject)
def getExtrudersModelWithOptional(self, *args) -> "ExtrudersModel":
if self._extruders_model_with_optional is None:
self._extruders_model_with_optional = ExtrudersModel(self)
self._extruders_model_with_optional.setAddOptionalExtruder(True)
return self._extruders_model_with_optional
@pyqtSlot(result = QObject) @pyqtSlot(result = QObject)
def getMultiBuildPlateModel(self, *args) -> MultiBuildPlateModel: def getMultiBuildPlateModel(self, *args) -> MultiBuildPlateModel:
if self._multi_build_plate_model is None: if self._multi_build_plate_model is None:

View file

@ -64,21 +64,21 @@ class MachineErrorChecker(QObject):
def _onMachineChanged(self) -> None: def _onMachineChanged(self) -> None:
if self._global_stack: if self._global_stack:
self._global_stack.propertyChanged.disconnect(self.startErrorCheck) self._global_stack.propertyChanged.disconnect(self.startErrorCheckPropertyChanged)
self._global_stack.containersChanged.disconnect(self.startErrorCheck) self._global_stack.containersChanged.disconnect(self.startErrorCheck)
for extruder in self._global_stack.extruders.values(): for extruder in self._global_stack.extruders.values():
extruder.propertyChanged.disconnect(self.startErrorCheck) extruder.propertyChanged.disconnect(self.startErrorCheckPropertyChanged)
extruder.containersChanged.disconnect(self.startErrorCheck) extruder.containersChanged.disconnect(self.startErrorCheck)
self._global_stack = self._machine_manager.activeMachine self._global_stack = self._machine_manager.activeMachine
if self._global_stack: if self._global_stack:
self._global_stack.propertyChanged.connect(self.startErrorCheck) self._global_stack.propertyChanged.connect(self.startErrorCheckPropertyChanged)
self._global_stack.containersChanged.connect(self.startErrorCheck) self._global_stack.containersChanged.connect(self.startErrorCheck)
for extruder in self._global_stack.extruders.values(): for extruder in self._global_stack.extruders.values():
extruder.propertyChanged.connect(self.startErrorCheck) extruder.propertyChanged.connect(self.startErrorCheckPropertyChanged)
extruder.containersChanged.connect(self.startErrorCheck) extruder.containersChanged.connect(self.startErrorCheck)
hasErrorUpdated = pyqtSignal() hasErrorUpdated = pyqtSignal()
@ -93,6 +93,13 @@ class MachineErrorChecker(QObject):
def needToWaitForResult(self) -> bool: def needToWaitForResult(self) -> bool:
return self._need_to_check or self._check_in_progress return self._need_to_check or self._check_in_progress
# Start the error check for property changed
# this is seperate from the startErrorCheck because it ignores a number property types
def startErrorCheckPropertyChanged(self, key, property_name):
if property_name != "value":
return
self.startErrorCheck()
# Starts the error check timer to schedule a new error check. # Starts the error check timer to schedule a new error check.
def startErrorCheck(self, *args) -> None: def startErrorCheck(self, *args) -> None:
if not self._check_in_progress: if not self._check_in_progress:

View file

@ -1,5 +1,6 @@
# Copyright (c) 2018 Ultimaker B.V. # Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher. # Cura is released under the terms of the LGPLv3 or higher.
from typing import Optional, Dict, Set
from PyQt5.QtCore import Qt, pyqtSignal, pyqtProperty from PyQt5.QtCore import Qt, pyqtSignal, pyqtProperty
from UM.Qt.ListModel import ListModel from UM.Qt.ListModel import ListModel
@ -9,6 +10,9 @@ from UM.Qt.ListModel import ListModel
# Those 2 models are used by the material drop down menu to show generic materials and branded materials separately. # Those 2 models are used by the material drop down menu to show generic materials and branded materials separately.
# The extruder position defined here is being used to bound a menu to the correct extruder. This is used in the top # The extruder position defined here is being used to bound a menu to the correct extruder. This is used in the top
# bar menu "Settings" -> "Extruder nr" -> "Material" -> this menu # bar menu "Settings" -> "Extruder nr" -> "Material" -> this menu
from cura.Machines.MaterialNode import MaterialNode
class BaseMaterialsModel(ListModel): class BaseMaterialsModel(ListModel):
extruderPositionChanged = pyqtSignal() extruderPositionChanged = pyqtSignal()
@ -54,8 +58,8 @@ class BaseMaterialsModel(ListModel):
self._extruder_position = 0 self._extruder_position = 0
self._extruder_stack = None self._extruder_stack = None
self._available_materials = None self._available_materials = None # type: Optional[Dict[str, MaterialNode]]
self._favorite_ids = None self._favorite_ids = set() # type: Set[str]
def _updateExtruderStack(self): def _updateExtruderStack(self):
global_stack = self._machine_manager.activeMachine global_stack = self._machine_manager.activeMachine
@ -102,8 +106,10 @@ class BaseMaterialsModel(ListModel):
return False return False
extruder_stack = global_stack.extruders[extruder_position] extruder_stack = global_stack.extruders[extruder_position]
available_materials = self._material_manager.getAvailableMaterialsForMachineExtruder(global_stack, extruder_stack)
self._available_materials = self._material_manager.getAvailableMaterialsForMachineExtruder(global_stack, extruder_stack) if available_materials == self._available_materials:
return False
self._available_materials = available_materials
if self._available_materials is None: if self._available_materials is None:
return False return False

View file

@ -4,17 +4,14 @@
from UM.Logger import Logger from UM.Logger import Logger
from cura.Machines.Models.BaseMaterialsModel import BaseMaterialsModel from cura.Machines.Models.BaseMaterialsModel import BaseMaterialsModel
class FavoriteMaterialsModel(BaseMaterialsModel):
class FavoriteMaterialsModel(BaseMaterialsModel):
def __init__(self, parent = None): def __init__(self, parent = None):
super().__init__(parent) super().__init__(parent)
self._update() self._update()
def _update(self): def _update(self):
# Perform standard check and reset if the check fails
if not self._canUpdate(): if not self._canUpdate():
self.setItems([])
return return
# Get updated list of favorites # Get updated list of favorites

View file

@ -11,10 +11,7 @@ class GenericMaterialsModel(BaseMaterialsModel):
self._update() self._update()
def _update(self): def _update(self):
# Perform standard check and reset if the check fails
if not self._canUpdate(): if not self._canUpdate():
self.setItems([])
return return
# Get updated list of favorites # Get updated list of favorites

View file

@ -28,12 +28,8 @@ class MaterialBrandsModel(BaseMaterialsModel):
self._update() self._update()
def _update(self): def _update(self):
# Perform standard check and reset if the check fails
if not self._canUpdate(): if not self._canUpdate():
self.setItems([])
return return
# Get updated list of favorites # Get updated list of favorites
self._favorite_ids = self._material_manager.getFavorites() self._favorite_ids = self._material_manager.getFavorites()

View file

@ -33,8 +33,6 @@ class NozzleModel(ListModel):
def _update(self): def _update(self):
Logger.log("d", "Updating {model_class_name}.".format(model_class_name = self.__class__.__name__)) Logger.log("d", "Updating {model_class_name}.".format(model_class_name = self.__class__.__name__))
self.items.clear()
global_stack = self._machine_manager.activeMachine global_stack = self._machine_manager.activeMachine
if global_stack is None: if global_stack is None:
self.setItems([]) self.setItems([])

View file

@ -81,9 +81,14 @@ class AuthorizationHelpers:
# \param access_token: The encoded JWT token. # \param access_token: The encoded JWT token.
# \return: Dict containing some profile data. # \return: Dict containing some profile data.
def parseJWT(self, access_token: str) -> Optional["UserProfile"]: def parseJWT(self, access_token: str) -> Optional["UserProfile"]:
token_request = requests.get("{}/check-token".format(self._settings.OAUTH_SERVER_URL), headers = { try:
"Authorization": "Bearer {}".format(access_token) token_request = requests.get("{}/check-token".format(self._settings.OAUTH_SERVER_URL), headers = {
}) "Authorization": "Bearer {}".format(access_token)
})
except ConnectionError:
# Connection was suddenly dropped. Nothing we can do about that.
Logger.logException("e", "Something failed while attempting to parse the JWT token")
return None
if token_request.status_code not in (200, 201): if token_request.status_code not in (200, 201):
Logger.log("w", "Could not retrieve token data from auth server: %s", token_request.text) Logger.log("w", "Could not retrieve token data from auth server: %s", token_request.text)
return None return None

View file

@ -187,7 +187,10 @@ class ConvexHullDecorator(SceneNodeDecorator):
for child in self._node.getChildren(): for child in self._node.getChildren():
child_hull = child.callDecoration("_compute2DConvexHull") child_hull = child.callDecoration("_compute2DConvexHull")
if child_hull: if child_hull:
points = numpy.append(points, child_hull.getPoints(), axis = 0) try:
points = numpy.append(points, child_hull.getPoints(), axis = 0)
except ValueError:
pass
if points.size < 3: if points.size < 3:
return None return None

View file

@ -83,8 +83,9 @@ class ExtruderManager(QObject):
# \param index The index of the new active extruder. # \param index The index of the new active extruder.
@pyqtSlot(int) @pyqtSlot(int)
def setActiveExtruderIndex(self, index: int) -> None: def setActiveExtruderIndex(self, index: int) -> None:
self._active_extruder_index = index if self._active_extruder_index != index:
self.activeExtruderChanged.emit() self._active_extruder_index = index
self.activeExtruderChanged.emit()
@pyqtProperty(int, notify = activeExtruderChanged) @pyqtProperty(int, notify = activeExtruderChanged)
def activeExtruderIndex(self) -> int: def activeExtruderIndex(self) -> int:
@ -344,6 +345,7 @@ class ExtruderManager(QObject):
if extruders_changed: if extruders_changed:
self.extrudersChanged.emit(global_stack_id) self.extrudersChanged.emit(global_stack_id)
self.setActiveExtruderIndex(0) self.setActiveExtruderIndex(0)
self.activeExtruderChanged.emit()
# After 3.4, all single-extrusion machines have their own extruder definition files instead of reusing # After 3.4, all single-extrusion machines have their own extruder definition files instead of reusing
# "fdmextruder". We need to check a machine here so its extruder definition is correct according to this. # "fdmextruder". We need to check a machine here so its extruder definition is correct according to this.

View file

@ -1,7 +1,7 @@
# Copyright (c) 2018 Ultimaker B.V. # Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher. # Cura is released under the terms of the LGPLv3 or higher.
from PyQt5.QtCore import Qt, pyqtSignal, pyqtSlot, pyqtProperty, QTimer from PyQt5.QtCore import Qt, pyqtSignal, pyqtProperty, QTimer
from typing import Iterable from typing import Iterable
from UM.i18n import i18nCatalog from UM.i18n import i18nCatalog
@ -78,8 +78,6 @@ class ExtrudersModel(UM.Qt.ListModel.ListModel):
self._update_extruder_timer.setSingleShot(True) self._update_extruder_timer.setSingleShot(True)
self._update_extruder_timer.timeout.connect(self.__updateExtruders) self._update_extruder_timer.timeout.connect(self.__updateExtruders)
self._simple_names = False
self._active_machine_extruders = [] # type: Iterable[ExtruderStack] self._active_machine_extruders = [] # type: Iterable[ExtruderStack]
self._add_optional_extruder = False self._add_optional_extruder = False
@ -101,21 +99,6 @@ class ExtrudersModel(UM.Qt.ListModel.ListModel):
def addOptionalExtruder(self): def addOptionalExtruder(self):
return self._add_optional_extruder return self._add_optional_extruder
## Set the simpleNames property.
def setSimpleNames(self, simple_names):
if simple_names != self._simple_names:
self._simple_names = simple_names
self.simpleNamesChanged.emit()
self._updateExtruders()
## Emitted when the simpleNames property changes.
simpleNamesChanged = pyqtSignal()
## Whether or not the model should show all definitions regardless of visibility.
@pyqtProperty(bool, fset = setSimpleNames, notify = simpleNamesChanged)
def simpleNames(self):
return self._simple_names
## Links to the stack-changed signal of the new extruders when an extruder ## Links to the stack-changed signal of the new extruders when an extruder
# is swapped out or added in the current machine. # is swapped out or added in the current machine.
# #
@ -221,9 +204,14 @@ class ExtrudersModel(UM.Qt.ListModel.ListModel):
"enabled": True, "enabled": True,
"color": "#ffffff", "color": "#ffffff",
"index": -1, "index": -1,
"definition": "" "definition": "",
"material": "",
"variant": "",
"stack": None,
"material_brand": "",
"color_name": "",
} }
items.append(item) items.append(item)
if self._items != items:
self.setItems(items) self.setItems(items)
self.modelChanged.emit() self.modelChanged.emit()

View file

@ -3,8 +3,8 @@
from collections import defaultdict from collections import defaultdict
import threading import threading
from typing import Any, Dict, Optional, Set, TYPE_CHECKING from typing import Any, Dict, Optional, Set, TYPE_CHECKING, List
from PyQt5.QtCore import pyqtProperty, pyqtSlot from PyQt5.QtCore import pyqtProperty, pyqtSlot, pyqtSignal
from UM.Decorators import override from UM.Decorators import override
from UM.MimeTypeDatabase import MimeType, MimeTypeDatabase from UM.MimeTypeDatabase import MimeType, MimeTypeDatabase
@ -42,13 +42,23 @@ class GlobalStack(CuraContainerStack):
# Per thread we have our own resolving_settings, or strange things sometimes occur. # Per thread we have our own resolving_settings, or strange things sometimes occur.
self._resolving_settings = defaultdict(set) #type: Dict[str, Set[str]] # keys are thread names self._resolving_settings = defaultdict(set) #type: Dict[str, Set[str]] # keys are thread names
extrudersChanged = pyqtSignal()
## Get the list of extruders of this stack. ## Get the list of extruders of this stack.
# #
# \return The extruders registered with this stack. # \return The extruders registered with this stack.
@pyqtProperty("QVariantMap") @pyqtProperty("QVariantMap", notify = extrudersChanged)
def extruders(self) -> Dict[str, "ExtruderStack"]: def extruders(self) -> Dict[str, "ExtruderStack"]:
return self._extruders return self._extruders
@pyqtProperty("QVariantList", notify = extrudersChanged)
def extruderList(self) -> List["ExtruderStack"]:
result_tuple_list = sorted(list(self.extruders.items()), key=lambda x: int(x[0]))
result_list = [item[1] for item in result_tuple_list]
machine_extruder_count = self.getProperty("machine_extruder_count", "value")
return result_list[:machine_extruder_count]
@classmethod @classmethod
def getLoadingPriority(cls) -> int: def getLoadingPriority(cls) -> int:
return 2 return 2

View file

@ -88,12 +88,14 @@ class MachineManager(QObject):
self._onGlobalContainerChanged() self._onGlobalContainerChanged()
ExtruderManager.getInstance().activeExtruderChanged.connect(self._onActiveExtruderStackChanged) extruder_manager = self._application.getExtruderManager()
extruder_manager.activeExtruderChanged.connect(self._onActiveExtruderStackChanged)
self._onActiveExtruderStackChanged() self._onActiveExtruderStackChanged()
ExtruderManager.getInstance().activeExtruderChanged.connect(self.activeMaterialChanged) extruder_manager.activeExtruderChanged.connect(self.activeMaterialChanged)
ExtruderManager.getInstance().activeExtruderChanged.connect(self.activeVariantChanged) extruder_manager.activeExtruderChanged.connect(self.activeVariantChanged)
ExtruderManager.getInstance().activeExtruderChanged.connect(self.activeQualityChanged) extruder_manager.activeExtruderChanged.connect(self.activeQualityChanged)
self.globalContainerChanged.connect(self.activeStackChanged) self.globalContainerChanged.connect(self.activeStackChanged)
self.globalValueChanged.connect(self.activeStackValueChanged) self.globalValueChanged.connect(self.activeStackValueChanged)
@ -1527,6 +1529,10 @@ class MachineManager(QObject):
def activeQualityChangesGroup(self) -> Optional["QualityChangesGroup"]: def activeQualityChangesGroup(self) -> Optional["QualityChangesGroup"]:
return self._current_quality_changes_group return self._current_quality_changes_group
@pyqtProperty(bool, notify = activeQualityChangesGroupChanged)
def hasCustomQuality(self) -> bool:
return self._current_quality_changes_group is not None
@pyqtProperty(str, notify = activeQualityGroupChanged) @pyqtProperty(str, notify = activeQualityGroupChanged)
def activeQualityOrQualityChangesName(self) -> str: def activeQualityOrQualityChangesName(self) -> str:
name = empty_quality_container.getName() name = empty_quality_container.getName()

View file

@ -1,7 +1,8 @@
# Copyright (c) 2017 Ultimaker B.V. # Copyright (c) 2017 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher. # Cura is released under the terms of the LGPLv3 or higher.
from typing import Set
from PyQt5.QtCore import QObject, pyqtSignal, pyqtProperty from PyQt5.QtCore import QObject, pyqtSignal, pyqtProperty, pyqtSlot
from UM.Application import Application from UM.Application import Application
@ -16,15 +17,11 @@ class SimpleModeSettingsManager(QObject):
self._is_profile_user_created = False # True when profile was custom created by user self._is_profile_user_created = False # True when profile was custom created by user
self._machine_manager.activeStackValueChanged.connect(self._updateIsProfileCustomized) self._machine_manager.activeStackValueChanged.connect(self._updateIsProfileCustomized)
self._machine_manager.activeQualityGroupChanged.connect(self._updateIsProfileUserCreated)
self._machine_manager.activeQualityChangesGroupChanged.connect(self._updateIsProfileUserCreated)
# update on create as the activeQualityChanged signal is emitted before this manager is created when Cura starts # update on create as the activeQualityChanged signal is emitted before this manager is created when Cura starts
self._updateIsProfileCustomized() self._updateIsProfileCustomized()
self._updateIsProfileUserCreated()
isProfileCustomizedChanged = pyqtSignal() isProfileCustomizedChanged = pyqtSignal()
isProfileUserCreatedChanged = pyqtSignal()
@pyqtProperty(bool, notify = isProfileCustomizedChanged) @pyqtProperty(bool, notify = isProfileCustomizedChanged)
def isProfileCustomized(self): def isProfileCustomized(self):
@ -57,33 +54,6 @@ class SimpleModeSettingsManager(QObject):
self._is_profile_customized = has_customized_user_settings self._is_profile_customized = has_customized_user_settings
self.isProfileCustomizedChanged.emit() self.isProfileCustomizedChanged.emit()
@pyqtProperty(bool, notify = isProfileUserCreatedChanged)
def isProfileUserCreated(self):
return self._is_profile_user_created
def _updateIsProfileUserCreated(self):
quality_changes_keys = set()
if not self._machine_manager.activeMachine:
return False
global_stack = self._machine_manager.activeMachine
# check quality changes settings in the global stack
quality_changes_keys.update(global_stack.qualityChanges.getAllKeys())
# check quality changes settings in the extruder stacks
if global_stack.extruders:
for extruder_stack in global_stack.extruders.values():
quality_changes_keys.update(extruder_stack.qualityChanges.getAllKeys())
# check if the qualityChanges container is not empty (meaning it is a user created profile)
has_quality_changes = len(quality_changes_keys) > 0
if has_quality_changes != self._is_profile_user_created:
self._is_profile_user_created = has_quality_changes
self.isProfileUserCreatedChanged.emit()
# These are the settings included in the Simple ("Recommended") Mode, so only when the other settings have been # These are the settings included in the Simple ("Recommended") Mode, so only when the other settings have been
# changed, we consider it as a user customized profile in the Simple ("Recommended") Mode. # changed, we consider it as a user customized profile in the Simple ("Recommended") Mode.
__ignored_custom_setting_keys = ["support_enable", __ignored_custom_setting_keys = ["support_enable",

View file

@ -24,10 +24,6 @@ class CuraStage(Stage):
def mainComponent(self) -> QUrl: def mainComponent(self) -> QUrl:
return self.getDisplayComponent("main") return self.getDisplayComponent("main")
@pyqtProperty(QUrl, constant = True)
def sidebarComponent(self) -> QUrl:
return self.getDisplayComponent("sidebar")
@pyqtProperty(QUrl, constant = True) @pyqtProperty(QUrl, constant = True)
def stageMenuComponent(self) -> QUrl: def stageMenuComponent(self) -> QUrl:
return self.getDisplayComponent("menu") return self.getDisplayComponent("menu")

View file

@ -17,12 +17,6 @@ parser.add_argument("--debug",
default = False, default = False,
help = "Turn on the debug mode by setting this option." help = "Turn on the debug mode by setting this option."
) )
parser.add_argument("--trigger-early-crash",
dest = "trigger_early_crash",
action = "store_true",
default = False,
help = "FOR TESTING ONLY. Trigger an early crash to show the crash dialog."
)
known_args = vars(parser.parse_known_args()[0]) known_args = vars(parser.parse_known_args()[0])
if not known_args["debug"]: if not known_args["debug"]:

View file

@ -1,8 +1,8 @@
{ {
"name": "3MF Reader", "name": "3MF Reader",
"author": "Ultimaker B.V.", "author": "Ultimaker B.V.",
"version": "1.0.0", "version": "1.0.1",
"description": "Provides support for reading 3MF files.", "description": "Provides support for reading 3MF files.",
"api": 5, "api": "6.0",
"i18n-catalog": "cura" "i18n-catalog": "cura"
} }

View file

@ -1,8 +1,8 @@
{ {
"name": "3MF Writer", "name": "3MF Writer",
"author": "Ultimaker B.V.", "author": "Ultimaker B.V.",
"version": "1.0.0", "version": "1.0.1",
"description": "Provides support for writing 3MF files.", "description": "Provides support for writing 3MF files.",
"api": 5, "api": "6.0",
"i18n-catalog": "cura" "i18n-catalog": "cura"
} }

View file

@ -1,8 +1,8 @@
{ {
"name": "Changelog", "name": "Changelog",
"author": "Ultimaker B.V.", "author": "Ultimaker B.V.",
"version": "1.0.0", "version": "1.0.1",
"description": "Shows changes since latest checked version.", "description": "Shows changes since latest checked version.",
"api": 5, "api": "6.0",
"i18n-catalog": "cura" "i18n-catalog": "cura"
} }

View file

@ -203,7 +203,7 @@ class CuraEngineBackend(QObject, Backend):
@pyqtSlot() @pyqtSlot()
def stopSlicing(self) -> None: def stopSlicing(self) -> None:
self.backendStateChange.emit(BackendState.NotStarted) self.setState(BackendState.NotStarted)
if self._slicing: # We were already slicing. Stop the old job. if self._slicing: # We were already slicing. Stop the old job.
self._terminate() self._terminate()
self._createSocket() self._createSocket()
@ -322,7 +322,7 @@ class CuraEngineBackend(QObject, Backend):
self._start_slice_job = None self._start_slice_job = None
if job.isCancelled() or job.getError() or job.getResult() == StartJobResult.Error: if job.isCancelled() or job.getError() or job.getResult() == StartJobResult.Error:
self.backendStateChange.emit(BackendState.Error) self.setState(BackendState.Error)
self.backendError.emit(job) self.backendError.emit(job)
return return
@ -331,10 +331,10 @@ class CuraEngineBackend(QObject, Backend):
self._error_message = Message(catalog.i18nc("@info:status", self._error_message = Message(catalog.i18nc("@info:status",
"Unable to slice with the current material as it is incompatible with the selected machine or configuration."), title = catalog.i18nc("@info:title", "Unable to slice")) "Unable to slice with the current material as it is incompatible with the selected machine or configuration."), title = catalog.i18nc("@info:title", "Unable to slice"))
self._error_message.show() self._error_message.show()
self.backendStateChange.emit(BackendState.Error) self.setState(BackendState.Error)
self.backendError.emit(job) self.backendError.emit(job)
else: else:
self.backendStateChange.emit(BackendState.NotStarted) self.setState(BackendState.NotStarted)
return return
if job.getResult() == StartJobResult.SettingError: if job.getResult() == StartJobResult.SettingError:
@ -362,10 +362,10 @@ class CuraEngineBackend(QObject, Backend):
self._error_message = Message(catalog.i18nc("@info:status", "Unable to slice with the current settings. The following settings have errors: {0}").format(", ".join(error_labels)), self._error_message = Message(catalog.i18nc("@info:status", "Unable to slice with the current settings. The following settings have errors: {0}").format(", ".join(error_labels)),
title = catalog.i18nc("@info:title", "Unable to slice")) title = catalog.i18nc("@info:title", "Unable to slice"))
self._error_message.show() self._error_message.show()
self.backendStateChange.emit(BackendState.Error) self.setState(BackendState.Error)
self.backendError.emit(job) self.backendError.emit(job)
else: else:
self.backendStateChange.emit(BackendState.NotStarted) self.setState(BackendState.NotStarted)
return return
elif job.getResult() == StartJobResult.ObjectSettingError: elif job.getResult() == StartJobResult.ObjectSettingError:
@ -386,7 +386,7 @@ class CuraEngineBackend(QObject, Backend):
self._error_message = Message(catalog.i18nc("@info:status", "Unable to slice due to some per-model settings. The following settings have errors on one or more models: {error_labels}").format(error_labels = ", ".join(errors.values())), self._error_message = Message(catalog.i18nc("@info:status", "Unable to slice due to some per-model settings. The following settings have errors on one or more models: {error_labels}").format(error_labels = ", ".join(errors.values())),
title = catalog.i18nc("@info:title", "Unable to slice")) title = catalog.i18nc("@info:title", "Unable to slice"))
self._error_message.show() self._error_message.show()
self.backendStateChange.emit(BackendState.Error) self.setState(BackendState.Error)
self.backendError.emit(job) self.backendError.emit(job)
return return
@ -395,16 +395,16 @@ class CuraEngineBackend(QObject, Backend):
self._error_message = Message(catalog.i18nc("@info:status", "Unable to slice because the prime tower or prime position(s) are invalid."), self._error_message = Message(catalog.i18nc("@info:status", "Unable to slice because the prime tower or prime position(s) are invalid."),
title = catalog.i18nc("@info:title", "Unable to slice")) title = catalog.i18nc("@info:title", "Unable to slice"))
self._error_message.show() self._error_message.show()
self.backendStateChange.emit(BackendState.Error) self.setState(BackendState.Error)
self.backendError.emit(job) self.backendError.emit(job)
else: else:
self.backendStateChange.emit(BackendState.NotStarted) self.setState(BackendState.NotStarted)
if job.getResult() == StartJobResult.ObjectsWithDisabledExtruder: if job.getResult() == StartJobResult.ObjectsWithDisabledExtruder:
self._error_message = Message(catalog.i18nc("@info:status", "Unable to slice because there are objects associated with disabled Extruder %s." % job.getMessage()), self._error_message = Message(catalog.i18nc("@info:status", "Unable to slice because there are objects associated with disabled Extruder %s." % job.getMessage()),
title = catalog.i18nc("@info:title", "Unable to slice")) title = catalog.i18nc("@info:title", "Unable to slice"))
self._error_message.show() self._error_message.show()
self.backendStateChange.emit(BackendState.Error) self.setState(BackendState.Error)
self.backendError.emit(job) self.backendError.emit(job)
return return
@ -413,10 +413,10 @@ class CuraEngineBackend(QObject, Backend):
self._error_message = Message(catalog.i18nc("@info:status", "Nothing to slice because none of the models fit the build volume. Please scale or rotate models to fit."), self._error_message = Message(catalog.i18nc("@info:status", "Nothing to slice because none of the models fit the build volume. Please scale or rotate models to fit."),
title = catalog.i18nc("@info:title", "Unable to slice")) title = catalog.i18nc("@info:title", "Unable to slice"))
self._error_message.show() self._error_message.show()
self.backendStateChange.emit(BackendState.Error) self.setState(BackendState.Error)
self.backendError.emit(job) self.backendError.emit(job)
else: else:
self.backendStateChange.emit(BackendState.NotStarted) self.setState(BackendState.NotStarted)
self._invokeSlice() self._invokeSlice()
return return
@ -424,7 +424,7 @@ class CuraEngineBackend(QObject, Backend):
self._socket.sendMessage(job.getSliceMessage()) self._socket.sendMessage(job.getSliceMessage())
# Notify the user that it's now up to the backend to do it's job # Notify the user that it's now up to the backend to do it's job
self.backendStateChange.emit(BackendState.Processing) self.setState(BackendState.Processing)
if self._slice_start_time: if self._slice_start_time:
Logger.log("d", "Sending slice message took %s seconds", time() - self._slice_start_time ) Logger.log("d", "Sending slice message took %s seconds", time() - self._slice_start_time )
@ -442,7 +442,7 @@ class CuraEngineBackend(QObject, Backend):
for node in DepthFirstIterator(self._scene.getRoot()): #type: ignore #Ignore type error because iter() should get called automatically by Python syntax. for node in DepthFirstIterator(self._scene.getRoot()): #type: ignore #Ignore type error because iter() should get called automatically by Python syntax.
if node.callDecoration("isBlockSlicing"): if node.callDecoration("isBlockSlicing"):
enable_timer = False enable_timer = False
self.backendStateChange.emit(BackendState.Disabled) self.setState(BackendState.Disabled)
self._is_disabled = True self._is_disabled = True
gcode_list = node.callDecoration("getGCodeList") gcode_list = node.callDecoration("getGCodeList")
if gcode_list is not None: if gcode_list is not None:
@ -451,7 +451,7 @@ class CuraEngineBackend(QObject, Backend):
if self._use_timer == enable_timer: if self._use_timer == enable_timer:
return self._use_timer return self._use_timer
if enable_timer: if enable_timer:
self.backendStateChange.emit(BackendState.NotStarted) self.setState(BackendState.NotStarted)
self.enableTimer() self.enableTimer()
return True return True
else: else:
@ -518,7 +518,7 @@ class CuraEngineBackend(QObject, Backend):
self._build_plates_to_be_sliced.append(build_plate_number) self._build_plates_to_be_sliced.append(build_plate_number)
self.printDurationMessage.emit(source_build_plate_number, {}, []) self.printDurationMessage.emit(source_build_plate_number, {}, [])
self.processingProgress.emit(0.0) self.processingProgress.emit(0.0)
self.backendStateChange.emit(BackendState.NotStarted) self.setState(BackendState.NotStarted)
# if not self._use_timer: # if not self._use_timer:
# With manually having to slice, we want to clear the old invalid layer data. # With manually having to slice, we want to clear the old invalid layer data.
self._clearLayerData(build_plate_changed) self._clearLayerData(build_plate_changed)
@ -567,7 +567,7 @@ class CuraEngineBackend(QObject, Backend):
self.stopSlicing() self.stopSlicing()
self.markSliceAll() self.markSliceAll()
self.processingProgress.emit(0.0) self.processingProgress.emit(0.0)
self.backendStateChange.emit(BackendState.NotStarted) self.setState(BackendState.NotStarted)
if not self._use_timer: if not self._use_timer:
# With manually having to slice, we want to clear the old invalid layer data. # With manually having to slice, we want to clear the old invalid layer data.
self._clearLayerData() self._clearLayerData()
@ -613,7 +613,7 @@ class CuraEngineBackend(QObject, Backend):
# \param message The protobuf message containing the slicing progress. # \param message The protobuf message containing the slicing progress.
def _onProgressMessage(self, message: Arcus.PythonMessage) -> None: def _onProgressMessage(self, message: Arcus.PythonMessage) -> None:
self.processingProgress.emit(message.amount) self.processingProgress.emit(message.amount)
self.backendStateChange.emit(BackendState.Processing) self.setState(BackendState.Processing)
def _invokeSlice(self) -> None: def _invokeSlice(self) -> None:
if self._use_timer: if self._use_timer:
@ -632,7 +632,7 @@ class CuraEngineBackend(QObject, Backend):
# #
# \param message The protobuf message signalling that slicing is finished. # \param message The protobuf message signalling that slicing is finished.
def _onSlicingFinishedMessage(self, message: Arcus.PythonMessage) -> None: def _onSlicingFinishedMessage(self, message: Arcus.PythonMessage) -> None:
self.backendStateChange.emit(BackendState.Done) self.setState(BackendState.Done)
self.processingProgress.emit(1.0) self.processingProgress.emit(1.0)
gcode_list = self._scene.gcode_dict[self._start_slice_job_build_plate] #type: ignore #Because we generate this attribute dynamically. gcode_list = self._scene.gcode_dict[self._start_slice_job_build_plate] #type: ignore #Because we generate this attribute dynamically.

View file

@ -323,7 +323,7 @@ class StartSliceJob(Job):
value = stack.getProperty(key, "value") value = stack.getProperty(key, "value")
result[key] = value result[key] = value
Job.yieldThread() Job.yieldThread()
result["print_bed_temperature"] = result["material_bed_temperature"] # Renamed settings. result["print_bed_temperature"] = result["material_bed_temperature"] # Renamed settings.
result["print_temperature"] = result["material_print_temperature"] result["print_temperature"] = result["material_print_temperature"]
result["time"] = time.strftime("%H:%M:%S") #Some extra settings. result["time"] = time.strftime("%H:%M:%S") #Some extra settings.

View file

@ -2,7 +2,7 @@
"name": "CuraEngine Backend", "name": "CuraEngine Backend",
"author": "Ultimaker B.V.", "author": "Ultimaker B.V.",
"description": "Provides the link to the CuraEngine slicing backend.", "description": "Provides the link to the CuraEngine slicing backend.",
"api": 5, "api": "6.0",
"version": "1.0.0", "version": "1.0.1",
"i18n-catalog": "cura" "i18n-catalog": "cura"
} }

View file

@ -1,8 +1,8 @@
{ {
"name": "Cura Profile Reader", "name": "Cura Profile Reader",
"author": "Ultimaker B.V.", "author": "Ultimaker B.V.",
"version": "1.0.0", "version": "1.0.1",
"description": "Provides support for importing Cura profiles.", "description": "Provides support for importing Cura profiles.",
"api": 5, "api": "6.0",
"i18n-catalog": "cura" "i18n-catalog": "cura"
} }

View file

@ -1,8 +1,8 @@
{ {
"name": "Cura Profile Writer", "name": "Cura Profile Writer",
"author": "Ultimaker B.V.", "author": "Ultimaker B.V.",
"version": "1.0.0", "version": "1.0.1",
"description": "Provides support for exporting Cura profiles.", "description": "Provides support for exporting Cura profiles.",
"api": 5, "api": "6.0",
"i18n-catalog":"cura" "i18n-catalog":"cura"
} }

View file

@ -1,8 +1,8 @@
{ {
"name": "Firmware Update Checker", "name": "Firmware Update Checker",
"author": "Ultimaker B.V.", "author": "Ultimaker B.V.",
"version": "1.0.0", "version": "1.0.1",
"description": "Checks for firmware updates.", "description": "Checks for firmware updates.",
"api": 5, "api": "6.0",
"i18n-catalog": "cura" "i18n-catalog": "cura"
} }

View file

@ -1,8 +1,8 @@
{ {
"name": "Firmware Updater", "name": "Firmware Updater",
"author": "Ultimaker B.V.", "author": "Ultimaker B.V.",
"version": "1.0.0", "version": "1.0.1",
"description": "Provides a machine actions for updating firmware.", "description": "Provides a machine actions for updating firmware.",
"api": 5, "api": "6.0",
"i18n-catalog": "cura" "i18n-catalog": "cura"
} }

View file

@ -1,8 +1,8 @@
{ {
"name": "Compressed G-code Reader", "name": "Compressed G-code Reader",
"author": "Ultimaker B.V.", "author": "Ultimaker B.V.",
"version": "1.0.0", "version": "1.0.1",
"description": "Reads g-code from a compressed archive.", "description": "Reads g-code from a compressed archive.",
"api": 5, "api": "6.0",
"i18n-catalog": "cura" "i18n-catalog": "cura"
} }

View file

@ -1,8 +1,8 @@
{ {
"name": "Compressed G-code Writer", "name": "Compressed G-code Writer",
"author": "Ultimaker B.V.", "author": "Ultimaker B.V.",
"version": "1.0.0", "version": "1.0.1",
"description": "Writes g-code to a compressed archive.", "description": "Writes g-code to a compressed archive.",
"api": 5, "api": "6.0",
"i18n-catalog": "cura" "i18n-catalog": "cura"
} }

View file

@ -1,8 +1,8 @@
{ {
"name": "G-code Profile Reader", "name": "G-code Profile Reader",
"author": "Ultimaker B.V.", "author": "Ultimaker B.V.",
"version": "1.0.0", "version": "1.0.1",
"description": "Provides support for importing profiles from g-code files.", "description": "Provides support for importing profiles from g-code files.",
"api": 5, "api": "6.0",
"i18n-catalog": "cura" "i18n-catalog": "cura"
} }

View file

@ -364,6 +364,8 @@ class FlavorParser:
self._layer_type = LayerPolygon.SupportType self._layer_type = LayerPolygon.SupportType
elif type == "FILL": elif type == "FILL":
self._layer_type = LayerPolygon.InfillType self._layer_type = LayerPolygon.InfillType
elif type == "SUPPORT-INTERFACE":
self._layer_type = LayerPolygon.SupportInterfaceType
else: else:
Logger.log("w", "Encountered a unknown type (%s) while parsing g-code.", type) Logger.log("w", "Encountered a unknown type (%s) while parsing g-code.", type)

View file

@ -1,8 +1,8 @@
{ {
"name": "G-code Reader", "name": "G-code Reader",
"author": "Victor Larchenko", "author": "Victor Larchenko, Ultimaker",
"version": "1.0.0", "version": "1.0.1",
"description": "Allows loading and displaying G-code files.", "description": "Allows loading and displaying G-code files.",
"api": 5, "api": "6.0",
"i18n-catalog": "cura" "i18n-catalog": "cura"
} }

View file

@ -1,8 +1,8 @@
{ {
"name": "G-code Writer", "name": "G-code Writer",
"author": "Ultimaker B.V.", "author": "Ultimaker B.V.",
"version": "1.0.0", "version": "1.0.1",
"description": "Writes g-code to a file.", "description": "Writes g-code to a file.",
"api": 5, "api": "6.0",
"i18n-catalog": "cura" "i18n-catalog": "cura"
} }

View file

@ -1,8 +1,8 @@
{ {
"name": "Image Reader", "name": "Image Reader",
"author": "Ultimaker B.V.", "author": "Ultimaker B.V.",
"version": "1.0.0", "version": "1.0.1",
"description": "Enables ability to generate printable geometry from 2D image files.", "description": "Enables ability to generate printable geometry from 2D image files.",
"api": 5, "api": "6.0",
"i18n-catalog": "cura" "i18n-catalog": "cura"
} }

View file

@ -1,8 +1,8 @@
{ {
"name": "Legacy Cura Profile Reader", "name": "Legacy Cura Profile Reader",
"author": "Ultimaker B.V.", "author": "Ultimaker B.V.",
"version": "1.0.0", "version": "1.0.1",
"description": "Provides support for importing profiles from legacy Cura versions.", "description": "Provides support for importing profiles from legacy Cura versions.",
"api": 5, "api": "6.0",
"i18n-catalog": "cura" "i18n-catalog": "cura"
} }

View file

@ -13,7 +13,7 @@ import Cura 1.0 as Cura
Cura.MachineAction Cura.MachineAction
{ {
id: base id: base
property var extrudersModel: Cura.ExtrudersModel{} property var extrudersModel: CuraApplication.getExtrudersModel()
property int extruderTabsCount: 0 property int extruderTabsCount: 0
property var activeMachineId: Cura.MachineManager.activeMachine != null ? Cura.MachineManager.activeMachine.id : "" property var activeMachineId: Cura.MachineManager.activeMachine != null ? Cura.MachineManager.activeMachine.id : ""

View file

@ -1,8 +1,8 @@
{ {
"name": "Machine Settings action", "name": "Machine Settings action",
"author": "fieldOfView", "author": "fieldOfView",
"version": "1.0.0", "version": "1.0.1",
"description": "Provides a way to change machine settings (such as build volume, nozzle size, etc.).", "description": "Provides a way to change machine settings (such as build volume, nozzle size, etc.).",
"api": 5, "api": "6.0",
"i18n-catalog": "cura" "i18n-catalog": "cura"
} }

View file

@ -1,8 +1,8 @@
{ {
"name": "Model Checker", "name": "Model Checker",
"author": "Ultimaker B.V.", "author": "Ultimaker B.V.",
"version": "0.1", "version": "1.0.1",
"api": 5, "api": "6.0",
"description": "Checks models and print configuration for possible printing issues and give suggestions.", "description": "Checks models and print configuration for possible printing issues and give suggestions.",
"i18n-catalog": "cura" "i18n-catalog": "cura"
} }

View file

@ -35,6 +35,6 @@ Item
property real maximumWidth: parent.width property real maximumWidth: parent.width
property real maximumHeight: parent.height property real maximumHeight: parent.height
sourceComponent: Cura.MachineManager.printerOutputDevices.length > 0 ? Cura.MachineManager.printerOutputDevices[0].monitorItem: null sourceComponent: Cura.MachineManager.printerOutputDevices.length > 0 ? Cura.MachineManager.printerOutputDevices[0].monitorItem : null
} }
} }

View file

@ -1,8 +1,8 @@
{ {
"name": "Monitor Stage", "name": "Monitor Stage",
"author": "Ultimaker B.V.", "author": "Ultimaker B.V.",
"version": "1.0.0", "version": "1.0.1",
"description": "Provides a monitor stage in Cura.", "description": "Provides a monitor stage in Cura.",
"api": 5, "api": "6.0",
"i18n-catalog": "cura" "i18n-catalog": "cura"
} }

View file

@ -1,8 +1,8 @@
{ {
"name": "Per Model Settings Tool", "name": "Per Model Settings Tool",
"author": "Ultimaker B.V.", "author": "Ultimaker B.V.",
"version": "1.0.0", "version": "1.0.1",
"description": "Provides the Per Model Settings.", "description": "Provides the Per Model Settings.",
"api": 5, "api": "6.0",
"i18n-catalog": "cura" "i18n-catalog": "cura"
} }

View file

@ -1,8 +1,8 @@
{ {
"name": "Post Processing", "name": "Post Processing",
"author": "Ultimaker", "author": "Ultimaker",
"version": "2.2", "version": "2.2.1",
"api": 5, "api": "6.0",
"description": "Extension that allows for user created scripts for post processing", "description": "Extension that allows for user created scripts for post processing",
"catalog": "cura" "catalog": "cura"
} }

View file

@ -100,7 +100,7 @@ Item
source: UM.Theme.getIcon("load") source: UM.Theme.getIcon("load")
width: UM.Theme.getSize("button_icon").width width: UM.Theme.getSize("button_icon").width
height: UM.Theme.getSize("button_icon").height height: UM.Theme.getSize("button_icon").height
color: UM.Theme.getColor("toolbar_button_text") color: UM.Theme.getColor("icon")
sourceSize.height: height sourceSize.height: height
} }

View file

@ -1,4 +1,4 @@
# Copyright (c) 2017 Ultimaker B.V. # Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher. # Cura is released under the terms of the LGPLv3 or higher.
import os.path import os.path
from UM.Application import Application from UM.Application import Application
@ -15,9 +15,5 @@ class PrepareStage(CuraStage):
Application.getInstance().engineCreatedSignal.connect(self._engineCreated) Application.getInstance().engineCreatedSignal.connect(self._engineCreated)
def _engineCreated(self): def _engineCreated(self):
sidebar_component_path = os.path.join(Resources.getPath(Application.getInstance().ResourceTypes.QmlFiles),
"PrepareSidebar.qml")
menu_component_path = os.path.join(PluginRegistry.getInstance().getPluginPath("PrepareStage"), "PrepareMenu.qml") menu_component_path = os.path.join(PluginRegistry.getInstance().getPluginPath("PrepareStage"), "PrepareMenu.qml")
self.addDisplayComponent("menu", menu_component_path) self.addDisplayComponent("menu", menu_component_path)
self.addDisplayComponent("sidebar", sidebar_component_path)

View file

@ -1,8 +1,8 @@
{ {
"name": "Prepare Stage", "name": "Prepare Stage",
"author": "Ultimaker B.V.", "author": "Ultimaker B.V.",
"version": "1.0.0", "version": "1.0.1",
"description": "Provides a prepare stage in Cura.", "description": "Provides a prepare stage in Cura.",
"api": 5, "api": "6.0",
"i18n-catalog": "cura" "i18n-catalog": "cura"
} }

View file

@ -2,6 +2,7 @@
// Cura is released under the terms of the LGPLv3 or higher. // Cura is released under the terms of the LGPLv3 or higher.
import QtQuick 2.7 import QtQuick 2.7
import QtQuick.Layouts 1.1
import QtQuick.Controls 2.3 import QtQuick.Controls 2.3
import UM 1.3 as UM import UM 1.3 as UM
@ -19,12 +20,15 @@ Item
name: "cura" name: "cura"
} }
Row Row
{ {
id: stageMenuRow id: stageMenuRow
anchors.centerIn: parent anchors.centerIn: parent
height: parent.height height: parent.height
width: childrenRect.width
// We want this row to have a preferred with equals to the 85% of the parent
property int preferredWidth: Math.round(0.85 * previewMenu.width)
Cura.ViewsSelector Cura.ViewsSelector
{ {
@ -45,11 +49,12 @@ Item
color: UM.Theme.getColor("lining") color: UM.Theme.getColor("lining")
} }
// This component will grow freely up to complete the preferredWidth of the row.
Loader Loader
{ {
id: viewPanel id: viewPanel
height: parent.height height: parent.height
width: childrenRect.width width: source != "" ? (stageMenuRow.preferredWidth - viewsSelector.width - printSetupSelectorItem.width - 2 * UM.Theme.getSize("default_lining").width) : 0
source: UM.Controller.activeView != null && UM.Controller.activeView.stageMenuComponent != null ? UM.Controller.activeView.stageMenuComponent : "" source: UM.Controller.activeView != null && UM.Controller.activeView.stageMenuComponent != null ? UM.Controller.activeView.stageMenuComponent : ""
} }

View file

@ -1,8 +1,8 @@
{ {
"name": "Preview Stage", "name": "Preview Stage",
"author": "Ultimaker B.V.", "author": "Ultimaker B.V.",
"version": "1.0.0", "version": "1.0.1",
"description": "Provides a preview stage in Cura.", "description": "Provides a preview stage in Cura.",
"api": 5, "api": "6.0",
"i18n-catalog": "cura" "i18n-catalog": "cura"
} }

View file

@ -2,7 +2,7 @@
"name": "Removable Drive Output Device Plugin", "name": "Removable Drive Output Device Plugin",
"author": "Ultimaker B.V.", "author": "Ultimaker B.V.",
"description": "Provides removable drive hotplugging and writing support.", "description": "Provides removable drive hotplugging and writing support.",
"version": "1.0.0", "version": "1.0.1",
"api": 5, "api": "6.0",
"i18n-catalog": "cura" "i18n-catalog": "cura"
} }

View file

@ -15,7 +15,7 @@ Cura.ExpandableComponent
{ {
id: base id: base
width: UM.Theme.getSize("layerview_menu_size").width contentHeaderTitle: catalog.i18nc("@label", "Color scheme")
Connections Connections
{ {
@ -34,14 +34,36 @@ Cura.ExpandableComponent
} }
} }
headerItem: Label headerItem: Item
{ {
id: layerViewTypesLabel Label
text: catalog.i18nc("@label", "Color scheme") {
font: UM.Theme.getFont("default") id: colorSchemeLabel
color: UM.Theme.getColor("setting_control_text") text: catalog.i18nc("@label", "Color scheme")
height: base.height verticalAlignment: Text.AlignVCenter
verticalAlignment: Text.AlignVCenter height: parent.height
elide: Text.ElideRight
font: UM.Theme.getFont("default")
color: UM.Theme.getColor("text_medium")
renderType: Text.NativeRendering
}
Label
{
text: layerTypeCombobox.currentText
verticalAlignment: Text.AlignVCenter
anchors
{
left: colorSchemeLabel.right
leftMargin: UM.Theme.getSize("default_margin").width
right: parent.right
}
height: parent.height
elide: Text.ElideRight
font: UM.Theme.getFont("default")
color: UM.Theme.getColor("text")
renderType: Text.NativeRendering
}
} }
contentItem: Column contentItem: Column
@ -124,7 +146,7 @@ Cura.ExpandableComponent
Label Label
{ {
id: compatibilityModeLabel id: compatibilityModeLabel
text: catalog.i18nc("@label","Compatibility Mode") text: catalog.i18nc("@label", "Compatibility Mode")
font: UM.Theme.getFont("default") font: UM.Theme.getFont("default")
color: UM.Theme.getColor("text") color: UM.Theme.getColor("text")
visible: UM.SimulationView.compatibilityMode visible: UM.SimulationView.compatibilityMode
@ -135,13 +157,13 @@ Cura.ExpandableComponent
Item // Spacer Item // Spacer
{ {
height: Math.round(UM.Theme.getSize("default_margin").width / 2) height: UM.Theme.getSize("narrow_margin").width
width: width width: width
} }
Repeater Repeater
{ {
model: Cura.ExtrudersModel{} model: CuraApplication.getExtrudersModel()
CheckBox CheckBox
{ {
@ -160,17 +182,16 @@ Cura.ExpandableComponent
style: UM.Theme.styles.checkbox style: UM.Theme.styles.checkbox
Rectangle
UM.RecolorImage
{ {
id: swatch
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
anchors.right: extrudersModelCheckBox.right anchors.right: extrudersModelCheckBox.right
width: UM.Theme.getSize("layerview_legend_size").width width: UM.Theme.getSize("layerview_legend_size").width
height: UM.Theme.getSize("layerview_legend_size").height height: UM.Theme.getSize("layerview_legend_size").height
source: UM.Theme.getIcon("extruder_button")
color: model.color color: model.color
radius: Math.round(width / 2)
border.width: UM.Theme.getSize("default_lining").width
border.color: UM.Theme.getColor("lining")
visible: !viewSettings.show_legend && !viewSettings.show_gradient
} }
Label Label
@ -200,25 +221,25 @@ Cura.ExpandableComponent
Component.onCompleted: Component.onCompleted:
{ {
typesLegendModel.append({ typesLegendModel.append({
label: catalog.i18nc("@label", "Show Travels"), label: catalog.i18nc("@label", "Travels"),
initialValue: viewSettings.show_travel_moves, initialValue: viewSettings.show_travel_moves,
preference: "layerview/show_travel_moves", preference: "layerview/show_travel_moves",
colorId: "layerview_move_combing" colorId: "layerview_move_combing"
}); });
typesLegendModel.append({ typesLegendModel.append({
label: catalog.i18nc("@label", "Show Helpers"), label: catalog.i18nc("@label", "Helpers"),
initialValue: viewSettings.show_helpers, initialValue: viewSettings.show_helpers,
preference: "layerview/show_helpers", preference: "layerview/show_helpers",
colorId: "layerview_support" colorId: "layerview_support"
}); });
typesLegendModel.append({ typesLegendModel.append({
label: catalog.i18nc("@label", "Show Shell"), label: catalog.i18nc("@label", "Shell"),
initialValue: viewSettings.show_skin, initialValue: viewSettings.show_skin,
preference: "layerview/show_skin", preference: "layerview/show_skin",
colorId: "layerview_inset_0" colorId: "layerview_inset_0"
}); });
typesLegendModel.append({ typesLegendModel.append({
label: catalog.i18nc("@label", "Show Infill"), label: catalog.i18nc("@label", "Infill"),
initialValue: viewSettings.show_infill, initialValue: viewSettings.show_infill,
preference: "layerview/show_infill", preference: "layerview/show_infill",
colorId: "layerview_infill" colorId: "layerview_infill"

View file

@ -1,8 +1,8 @@
{ {
"name": "Simulation View", "name": "Simulation View",
"author": "Ultimaker B.V.", "author": "Ultimaker B.V.",
"version": "1.0.0", "version": "1.0.1",
"description": "Provides the Simulation view.", "description": "Provides the Simulation view.",
"api": 5, "api": "6.0",
"i18n-catalog": "cura" "i18n-catalog": "cura"
} }

View file

@ -1,8 +1,8 @@
{ {
"name": "Slice info", "name": "Slice info",
"author": "Ultimaker B.V.", "author": "Ultimaker B.V.",
"version": "1.0.0", "version": "1.0.1",
"description": "Submits anonymous slice info. Can be disabled through preferences.", "description": "Submits anonymous slice info. Can be disabled through preferences.",
"api": 5, "api": "6.0",
"i18n-catalog": "cura" "i18n-catalog": "cura"
} }

View file

@ -12,7 +12,6 @@ from UM.Math.Color import Color
from UM.View.GL.OpenGL import OpenGL from UM.View.GL.OpenGL import OpenGL
from cura.Settings.ExtruderManager import ExtruderManager from cura.Settings.ExtruderManager import ExtruderManager
from cura.Settings.ExtrudersModel import ExtrudersModel
import math import math
@ -29,13 +28,16 @@ class SolidView(View):
self._non_printing_shader = None self._non_printing_shader = None
self._support_mesh_shader = None self._support_mesh_shader = None
self._extruders_model = ExtrudersModel() self._extruders_model = None
self._theme = None self._theme = None
def beginRendering(self): def beginRendering(self):
scene = self.getController().getScene() scene = self.getController().getScene()
renderer = self.getRenderer() renderer = self.getRenderer()
if not self._extruders_model:
self._extruders_model = Application.getInstance().getExtrudersModel()
if not self._theme: if not self._theme:
self._theme = Application.getInstance().getTheme() self._theme = Application.getInstance().getTheme()

View file

@ -1,8 +1,8 @@
{ {
"name": "Solid View", "name": "Solid View",
"author": "Ultimaker B.V.", "author": "Ultimaker B.V.",
"version": "1.0.0", "version": "1.0.1",
"description": "Provides a normal solid mesh view.", "description": "Provides a normal solid mesh view.",
"api": 5, "api": "6.0",
"i18n-catalog": "cura" "i18n-catalog": "cura"
} }

View file

@ -1,8 +1,8 @@
{ {
"name": "Support Eraser", "name": "Support Eraser",
"author": "Ultimaker B.V.", "author": "Ultimaker B.V.",
"version": "1.0.0", "version": "1.0.1",
"description": "Creates an eraser mesh to block the printing of support in certain places", "description": "Creates an eraser mesh to block the printing of support in certain places",
"api": 5, "api": "6.0",
"i18n-catalog": "cura" "i18n-catalog": "cura"
} }

View file

@ -1,7 +1,7 @@
{ {
"name": "Toolbox", "name": "Toolbox",
"author": "Ultimaker B.V.", "author": "Ultimaker B.V.",
"version": "1.0.0", "version": "1.0.1",
"api": 5, "api": "6.0",
"description": "Find, manage and install new Cura packages." "description": "Find, manage and install new Cura packages."
} }

View file

@ -0,0 +1,106 @@
import QtQuick 2.7
import QtQuick.Controls 2.1
import UM 1.0 as UM
import Cura 1.1 as Cura
Item
{
id: ratingWidget
property real rating: 0
property int indexHovered: -1
property string packageId: ""
property int userRating: 0
property bool canRate: false
signal rated(int rating)
width: contentRow.width
height: contentRow.height
MouseArea
{
id: mouseArea
anchors.fill: parent
hoverEnabled: ratingWidget.canRate
acceptedButtons: Qt.NoButton
onExited:
{
if(ratingWidget.canRate)
{
ratingWidget.indexHovered = -1
}
}
Row
{
id: contentRow
height: childrenRect.height
Repeater
{
model: 5 // We need to get 5 stars
Button
{
id: control
hoverEnabled: true
onHoveredChanged:
{
if(hovered && ratingWidget.canRate)
{
indexHovered = index
}
}
ToolTip.visible: control.hovered && !ratingWidget.canRate
ToolTip.text: !Cura.API.account.isLoggedIn ? catalog.i18nc("@label", "You need to login first before you can rate"): catalog.i18nc("@label", "You need to install the package before you can rate")
property bool isStarFilled:
{
// If the entire widget is hovered, override the actual rating.
if(ratingWidget.indexHovered >= 0)
{
return indexHovered >= index
}
if(ratingWidget.userRating > 0)
{
return userRating >= index +1
}
return rating >= index + 1
}
contentItem: Item {}
height: UM.Theme.getSize("rating_star").height
width: UM.Theme.getSize("rating_star").width
background: UM.RecolorImage
{
source: UM.Theme.getIcon(control.isStarFilled ? "star_filled" : "star_empty")
sourceSize.width: width
sourceSize.height: height
// Unfilled stars should always have the default color. Only filled stars should change on hover
color:
{
if(!ratingWidget.canRate)
{
return UM.Theme.getColor("rating_star")
}
if((ratingWidget.indexHovered >= 0 || ratingWidget.userRating > 0) && isStarFilled)
{
return UM.Theme.getColor("primary")
}
return UM.Theme.getColor("rating_star")
}
}
onClicked:
{
if(ratingWidget.canRate)
{
rated(index + 1) // Notify anyone who cares about this.
}
}
}
}
}
}
}

View file

@ -0,0 +1,36 @@
import QtQuick 2.3
import QtQuick.Controls 1.4
import UM 1.1 as UM
import Cura 1.1 as Cura
Row
{
id: rating
height: UM.Theme.getSize("rating_star").height
visible: model.average_rating > 0 //Has a rating at all.
spacing: UM.Theme.getSize("thick_lining").width
width: starIcon.width + spacing + numRatingsLabel.width
UM.RecolorImage
{
id: starIcon
source: UM.Theme.getIcon("star_filled")
color: model.user_rating == 0 ? UM.Theme.getColor("rating_star") : UM.Theme.getColor("primary")
height: UM.Theme.getSize("rating_star").height
width: UM.Theme.getSize("rating_star").width
sourceSize.height: height
sourceSize.width: width
}
Label
{
id: numRatingsLabel
text: model.average_rating != undefined ? model.average_rating.toFixed(1) + " (" + model.num_ratings + " " + catalog.i18nc("@label", "ratings") + ")": ""
verticalAlignment: Text.AlignVCenter
height: starIcon.height
width: contentWidth
anchors.verticalCenter: starIcon.verticalCenter
color: starIcon.color
font: UM.Theme.getFont("small")
renderType: Text.NativeRendering
}
}

View file

@ -14,8 +14,8 @@ Window
modality: Qt.ApplicationModal modality: Qt.ApplicationModal
flags: Qt.Dialog | Qt.CustomizeWindowHint | Qt.WindowTitleHint | Qt.WindowCloseButtonHint flags: Qt.Dialog | Qt.CustomizeWindowHint | Qt.WindowTitleHint | Qt.WindowCloseButtonHint
width: 720 * screenScaleFactor width: Math.floor(720 * screenScaleFactor)
height: 640 * screenScaleFactor height: Math.floor(640 * screenScaleFactor)
minimumWidth: width minimumWidth: width
maximumWidth: minimumWidth maximumWidth: minimumWidth
minimumHeight: height minimumHeight: height
@ -95,6 +95,7 @@ Window
licenseDialog.show(); licenseDialog.show();
} }
} }
ToolboxLicenseDialog ToolboxLicenseDialog
{ {
id: licenseDialog id: licenseDialog

View file

@ -1,7 +1,7 @@
// Copyright (c) 2018 Ultimaker B.V. // Copyright (c) 2018 Ultimaker B.V.
// Toolbox is released under the terms of the LGPLv3 or higher. // Toolbox is released under the terms of the LGPLv3 or higher.
import QtQuick 2.3 import QtQuick 2.10
import QtQuick.Controls 1.4 import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4 import QtQuick.Controls.Styles 1.4
import UM 1.1 as UM import UM 1.1 as UM
@ -59,6 +59,7 @@ Item
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
width: parent.width width: parent.width
height: UM.Theme.getSize("toolbox_property_label").height height: UM.Theme.getSize("toolbox_property_label").height
renderType: Text.NativeRendering
} }
Label Label
{ {
@ -70,6 +71,7 @@ Item
left: title.left left: title.left
topMargin: UM.Theme.getSize("default_margin").height topMargin: UM.Theme.getSize("default_margin").height
} }
renderType: Text.NativeRendering
} }
Column Column
{ {
@ -88,12 +90,14 @@ Item
text: catalog.i18nc("@label", "Website") + ":" text: catalog.i18nc("@label", "Website") + ":"
font: UM.Theme.getFont("default") font: UM.Theme.getFont("default")
color: UM.Theme.getColor("text_medium") color: UM.Theme.getColor("text_medium")
renderType: Text.NativeRendering
} }
Label Label
{ {
text: catalog.i18nc("@label", "Email") + ":" text: catalog.i18nc("@label", "Email") + ":"
font: UM.Theme.getFont("default") font: UM.Theme.getFont("default")
color: UM.Theme.getColor("text_medium") color: UM.Theme.getColor("text_medium")
renderType: Text.NativeRendering
} }
} }
Column Column
@ -122,6 +126,7 @@ Item
color: UM.Theme.getColor("text") color: UM.Theme.getColor("text")
linkColor: UM.Theme.getColor("text_link") linkColor: UM.Theme.getColor("text_link")
onLinkActivated: Qt.openUrlExternally(link) onLinkActivated: Qt.openUrlExternally(link)
renderType: Text.NativeRendering
} }
Label Label
@ -138,6 +143,7 @@ Item
color: UM.Theme.getColor("text") color: UM.Theme.getColor("text")
linkColor: UM.Theme.getColor("text_link") linkColor: UM.Theme.getColor("text_link")
onLinkActivated: Qt.openUrlExternally(link) onLinkActivated: Qt.openUrlExternally(link)
renderType: Text.NativeRendering
} }
} }
Rectangle Rectangle

View file

@ -1,7 +1,7 @@
// Copyright (c) 2018 Ultimaker B.V. // Copyright (c) 2018 Ultimaker B.V.
// Toolbox is released under the terms of the LGPLv3 or higher. // Toolbox is released under the terms of the LGPLv3 or higher.
import QtQuick 2.2 import QtQuick 2.10
import QtQuick.Controls 1.4 import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4 import QtQuick.Controls.Styles 1.4
import UM 1.1 as UM import UM 1.1 as UM
@ -64,6 +64,7 @@ Item
font: UM.Theme.getFont("default_bold") font: UM.Theme.getFont("default_bold")
horizontalAlignment: Text.AlignRight horizontalAlignment: Text.AlignRight
width: control.width width: control.width
renderType: Text.NativeRendering
} }
} }
} }

View file

@ -1,7 +1,7 @@
// Copyright (c) 2018 Ultimaker B.V. // Copyright (c) 2018 Ultimaker B.V.
// Toolbox is released under the terms of the LGPLv3 or higher. // Toolbox is released under the terms of the LGPLv3 or higher.
import QtQuick 2.7 import QtQuick 2.10
import QtQuick.Controls 1.4 import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4 import QtQuick.Controls.Styles 1.4
import UM 1.1 as UM import UM 1.1 as UM
@ -67,6 +67,7 @@ Item
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
color: UM.Theme.getColor("text_medium") color: UM.Theme.getColor("text_medium")
font: UM.Theme.getFont("medium") font: UM.Theme.getFont("medium")
renderType: Text.NativeRendering
} }
TableView TableView
@ -99,6 +100,7 @@ Item
text: styleData.value || "" text: styleData.value || ""
color: UM.Theme.getColor("text") color: UM.Theme.getColor("text")
font: UM.Theme.getFont("default_bold") font: UM.Theme.getFont("default_bold")
renderType: Text.NativeRendering
} }
Rectangle Rectangle
{ {
@ -118,6 +120,7 @@ Item
text: styleData.value || "" text: styleData.value || ""
color: UM.Theme.getColor("text_medium") color: UM.Theme.getColor("text_medium")
font: UM.Theme.getFont("default") font: UM.Theme.getFont("default")
renderType: Text.NativeRendering
} }
} }
itemDelegate: Item itemDelegate: Item
@ -130,6 +133,7 @@ Item
text: styleData.value || "" text: styleData.value || ""
color: UM.Theme.getColor("text_medium") color: UM.Theme.getColor("text_medium")
font: UM.Theme.getFont("default") font: UM.Theme.getFont("default")
renderType: Text.NativeRendering
} }
} }
@ -144,6 +148,7 @@ Item
elide: Text.ElideRight elide: Text.ElideRight
color: UM.Theme.getColor("text_medium") color: UM.Theme.getColor("text_medium")
font: UM.Theme.getFont("default") font: UM.Theme.getFont("default")
renderType: Text.NativeRendering
} }
} }
@ -232,5 +237,6 @@ Item
color: UM.Theme.getColor("text") color: UM.Theme.getColor("text")
linkColor: UM.Theme.getColor("text_link") linkColor: UM.Theme.getColor("text_link")
onLinkActivated: Qt.openUrlExternally(link) onLinkActivated: Qt.openUrlExternally(link)
renderType: Text.NativeRendering
} }
} }

View file

@ -1,7 +1,7 @@
// Copyright (c) 2018 Ultimaker B.V. // Copyright (c) 2018 Ultimaker B.V.
// Cura is released under the terms of the LGPLv3 or higher. // Cura is released under the terms of the LGPLv3 or higher.
import QtQuick 2.2 import QtQuick 2.10
import QtQuick.Controls 1.1 import QtQuick.Controls 1.1
import QtQuick.Controls.Styles 1.1 import QtQuick.Controls.Styles 1.1
import QtQuick.Layouts 1.1 import QtQuick.Layouts 1.1
@ -66,6 +66,7 @@ UM.Dialog
anchors.right: parent.right anchors.right: parent.right
font: UM.Theme.getFont("default") font: UM.Theme.getFont("default")
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
renderType: Text.NativeRendering
} }
// Buttons // Buttons

View file

@ -26,10 +26,19 @@ Item
} }
height: childrenRect.height + 2 * UM.Theme.getSize("wide_margin").height height: childrenRect.height + 2 * UM.Theme.getSize("wide_margin").height
spacing: UM.Theme.getSize("default_margin").height spacing: UM.Theme.getSize("default_margin").height
Repeater Repeater
{ {
model: toolbox.packagesModel model: toolbox.packagesModel
delegate: ToolboxDetailTile {} delegate: Loader
{
// FIXME: When using asynchronous loading, on Mac and Windows, the tile may fail to load complete,
// leaving an empty space below the title part. We turn it off for now to make it work on Mac and
// Windows.
// Can be related to this QT bug: https://bugreports.qt.io/browse/QTBUG-50992
asynchronous: false
source: "ToolboxDetailTile.qml"
}
} }
} }
} }

View file

@ -1,11 +1,13 @@
// Copyright (c) 2018 Ultimaker B.V. // Copyright (c) 2018 Ultimaker B.V.
// Toolbox is released under the terms of the LGPLv3 or higher. // Toolbox is released under the terms of the LGPLv3 or higher.
import QtQuick 2.3 import QtQuick 2.10
import QtQuick.Controls 1.4 import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4 import QtQuick.Controls.Styles 1.4
import UM 1.1 as UM import UM 1.1 as UM
import Cura 1.1 as Cura
Item Item
{ {
id: page id: page
@ -24,7 +26,7 @@ Item
right: parent.right right: parent.right
rightMargin: UM.Theme.getSize("wide_margin").width rightMargin: UM.Theme.getSize("wide_margin").width
} }
height: UM.Theme.getSize("toolbox_detail_header").height height: childrenRect.height + 3 * UM.Theme.getSize("default_margin").width
Rectangle Rectangle
{ {
id: thumbnail id: thumbnail
@ -37,7 +39,7 @@ Item
leftMargin: UM.Theme.getSize("wide_margin").width leftMargin: UM.Theme.getSize("wide_margin").width
topMargin: UM.Theme.getSize("wide_margin").height topMargin: UM.Theme.getSize("wide_margin").height
} }
color: "white" //Always a white background for image (regardless of theme). color: UM.Theme.getColor("main_background")
Image Image
{ {
anchors.fill: parent anchors.fill: parent
@ -55,16 +57,21 @@ Item
top: thumbnail.top top: thumbnail.top
left: thumbnail.right left: thumbnail.right
leftMargin: UM.Theme.getSize("default_margin").width leftMargin: UM.Theme.getSize("default_margin").width
right: parent.right
rightMargin: UM.Theme.getSize("wide_margin").width
bottomMargin: UM.Theme.getSize("default_margin").height
} }
text: details === null ? "" : (details.name || "") text: details === null ? "" : (details.name || "")
font: UM.Theme.getFont("large") font: UM.Theme.getFont("large")
color: UM.Theme.getColor("text") color: UM.Theme.getColor("text")
wrapMode: Text.WordWrap width: contentWidth
width: parent.width height: contentHeight
height: UM.Theme.getSize("toolbox_property_label").height renderType: Text.NativeRendering
}
SmallRatingWidget
{
anchors.left: title.right
anchors.leftMargin: UM.Theme.getSize("default_margin").width
anchors.verticalCenter: title.verticalCenter
property var model: details
} }
Column Column
@ -80,28 +87,39 @@ Item
width: childrenRect.width width: childrenRect.width
height: childrenRect.height height: childrenRect.height
Label Label
{
text: catalog.i18nc("@label", "Your rating") + ":"
font: UM.Theme.getFont("default")
color: UM.Theme.getColor("text_medium")
renderType: Text.NativeRendering
}
Label
{ {
text: catalog.i18nc("@label", "Version") + ":" text: catalog.i18nc("@label", "Version") + ":"
font: UM.Theme.getFont("default") font: UM.Theme.getFont("default")
color: UM.Theme.getColor("text_medium") color: UM.Theme.getColor("text_medium")
renderType: Text.NativeRendering
} }
Label Label
{ {
text: catalog.i18nc("@label", "Last updated") + ":" text: catalog.i18nc("@label", "Last updated") + ":"
font: UM.Theme.getFont("default") font: UM.Theme.getFont("default")
color: UM.Theme.getColor("text_medium") color: UM.Theme.getColor("text_medium")
renderType: Text.NativeRendering
} }
Label Label
{ {
text: catalog.i18nc("@label", "Author") + ":" text: catalog.i18nc("@label", "Author") + ":"
font: UM.Theme.getFont("default") font: UM.Theme.getFont("default")
color: UM.Theme.getColor("text_medium") color: UM.Theme.getColor("text_medium")
renderType: Text.NativeRendering
} }
Label Label
{ {
text: catalog.i18nc("@label", "Downloads") + ":" text: catalog.i18nc("@label", "Downloads") + ":"
font: UM.Theme.getFont("default") font: UM.Theme.getFont("default")
color: UM.Theme.getColor("text_medium") color: UM.Theme.getColor("text_medium")
renderType: Text.NativeRendering
} }
} }
Column Column
@ -116,11 +134,54 @@ Item
} }
spacing: Math.floor(UM.Theme.getSize("narrow_margin").height) spacing: Math.floor(UM.Theme.getSize("narrow_margin").height)
height: childrenRect.height height: childrenRect.height
RatingWidget
{
id: rating
visible: details.type == "plugin"
packageId: details.id != undefined ? details.id: ""
userRating: details.user_rating != undefined ? details.user_rating: 0
canRate: toolbox.isInstalled(details.id) && Cura.API.account.isLoggedIn
onRated:
{
toolbox.ratePackage(details.id, rating)
// HACK: This is a far from optimal solution, but without major refactoring, this is the best we can
// do. Since a rework of this is scheduled, it shouldn't live that long...
var index = toolbox.pluginsAvailableModel.find("id", details.id)
if(index != -1)
{
if(details.user_rating == 0) // User never rated before.
{
toolbox.pluginsAvailableModel.setProperty(index, "num_ratings", details.num_ratings + 1)
}
toolbox.pluginsAvailableModel.setProperty(index, "user_rating", rating)
// Hack; This is because the current selection is an outdated copy, so we need to re-copy it.
base.selection = toolbox.pluginsAvailableModel.getItem(index)
return
}
index = toolbox.pluginsShowcaseModel.find("id", details.id)
if(index != -1)
{
if(details.user_rating == 0) // User never rated before.
{
toolbox.pluginsShowcaseModel.setProperty(index, "user_rating", rating)
}
toolbox.pluginsShowcaseModel.setProperty(index, "num_ratings", details.num_ratings + 1)
// Hack; This is because the current selection is an outdated copy, so we need to re-copy it.
base.selection = toolbox.pluginsShowcaseModel.getItem(index)
}
}
}
Label Label
{ {
text: details === null ? "" : (details.version || catalog.i18nc("@label", "Unknown")) text: details === null ? "" : (details.version || catalog.i18nc("@label", "Unknown"))
font: UM.Theme.getFont("default") font: UM.Theme.getFont("default")
color: UM.Theme.getColor("text") color: UM.Theme.getColor("text")
renderType: Text.NativeRendering
} }
Label Label
{ {
@ -135,6 +196,7 @@ Item
} }
font: UM.Theme.getFont("default") font: UM.Theme.getFont("default")
color: UM.Theme.getColor("text") color: UM.Theme.getColor("text")
renderType: Text.NativeRendering
} }
Label Label
{ {
@ -153,21 +215,16 @@ Item
color: UM.Theme.getColor("text") color: UM.Theme.getColor("text")
linkColor: UM.Theme.getColor("text_link") linkColor: UM.Theme.getColor("text_link")
onLinkActivated: Qt.openUrlExternally(link) onLinkActivated: Qt.openUrlExternally(link)
renderType: Text.NativeRendering
} }
Label Label
{ {
text: details === null ? "" : (details.download_count || catalog.i18nc("@label", "Unknown")) text: details === null ? "" : (details.download_count || catalog.i18nc("@label", "Unknown"))
font: UM.Theme.getFont("default") font: UM.Theme.getFont("default")
color: UM.Theme.getColor("text") color: UM.Theme.getColor("text")
renderType: Text.NativeRendering
} }
} }
Rectangle
{
color: UM.Theme.getColor("lining")
width: parent.width
height: UM.Theme.getSize("default_lining").height
anchors.bottom: parent.bottom
}
} }
ToolboxDetailList ToolboxDetailList
{ {

View file

@ -1,7 +1,7 @@
// Copyright (c) 2018 Ultimaker B.V. // Copyright (c) 2018 Ultimaker B.V.
// Toolbox is released under the terms of the LGPLv3 or higher. // Toolbox is released under the terms of the LGPLv3 or higher.
import QtQuick 2.7 import QtQuick 2.10
import QtQuick.Controls 1.4 import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4 import QtQuick.Controls.Styles 1.4
import UM 1.1 as UM import UM 1.1 as UM
@ -31,6 +31,7 @@ Item
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
color: UM.Theme.getColor("text") color: UM.Theme.getColor("text")
font: UM.Theme.getFont("medium_bold") font: UM.Theme.getFont("medium_bold")
renderType: Text.NativeRendering
} }
Label Label
{ {
@ -42,6 +43,7 @@ Item
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
color: UM.Theme.getColor("text") color: UM.Theme.getColor("text")
font: UM.Theme.getFont("default") font: UM.Theme.getFont("default")
renderType: Text.NativeRendering
} }
} }

View file

@ -1,40 +1,69 @@
// Copyright (c) 2018 Ultimaker B.V. // Copyright (c) 2018 Ultimaker B.V.
// Toolbox is released under the terms of the LGPLv3 or higher. // Toolbox is released under the terms of the LGPLv3 or higher.
import QtQuick 2.7 import QtQuick 2.10
import QtQuick.Controls 1.4 import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4 import QtQuick.Controls.Styles 1.4
import UM 1.1 as UM import UM 1.1 as UM
import Cura 1.1 as Cura
Column Column
{ {
property bool installed: toolbox.isInstalled(model.id) property bool installed: toolbox.isInstalled(model.id)
property bool canUpdate: toolbox.canUpdate(model.id) property bool canUpdate: toolbox.canUpdate(model.id)
property bool loginRequired: model.login_required && !Cura.API.account.isLoggedIn
width: UM.Theme.getSize("toolbox_action_button").width width: UM.Theme.getSize("toolbox_action_button").width
spacing: UM.Theme.getSize("narrow_margin").height spacing: UM.Theme.getSize("narrow_margin").height
ToolboxProgressButton Item
{ {
id: installButton width: installButton.width
active: toolbox.isDownloading && toolbox.activePackage == model height: installButton.height
complete: installed ToolboxProgressButton
readyAction: function()
{ {
toolbox.activePackage = model id: installButton
toolbox.startDownload(model.download_url) active: toolbox.isDownloading && toolbox.activePackage == model
onReadyAction:
{
toolbox.activePackage = model
toolbox.startDownload(model.download_url)
}
onActiveAction: toolbox.cancelDownload()
// Don't allow installing while another download is running
enabled: installed || (!(toolbox.isDownloading && toolbox.activePackage != model) && !loginRequired)
opacity: enabled ? 1.0 : 0.5
visible: !updateButton.visible && !installed// Don't show when the update button is visible
} }
activeAction: function()
Cura.SecondaryButton
{ {
toolbox.cancelDownload() visible: installed
onClicked: toolbox.viewCategory = "installed"
text: catalog.i18nc("@action:button", "Installed")
fixedWidthMode: true
width: installButton.width
height: installButton.height
} }
completeAction: function() }
Label
{
wrapMode: Text.WordWrap
text: catalog.i18nc("@label:The string between <a href=> and </a> is the highlighted link", "<a href='%1'>Log in</a> is required to install or update")
font: UM.Theme.getFont("default")
color: UM.Theme.getColor("text")
linkColor: UM.Theme.getColor("text_link")
visible: loginRequired
width: installButton.width
renderType: Text.NativeRendering
MouseArea
{ {
toolbox.viewCategory = "installed" anchors.fill: parent
onClicked: Cura.API.account.login()
} }
// Don't allow installing while another download is running
enabled: installed || !(toolbox.isDownloading && toolbox.activePackage != model)
opacity: enabled ? 1.0 : 0.5
visible: !updateButton.visible // Don't show when the update button is visible
} }
ToolboxProgressButton ToolboxProgressButton
@ -44,20 +73,19 @@ Column
readyLabel: catalog.i18nc("@action:button", "Update") readyLabel: catalog.i18nc("@action:button", "Update")
activeLabel: catalog.i18nc("@action:button", "Updating") activeLabel: catalog.i18nc("@action:button", "Updating")
completeLabel: catalog.i18nc("@action:button", "Updated") completeLabel: catalog.i18nc("@action:button", "Updated")
readyAction: function()
onReadyAction:
{ {
toolbox.activePackage = model toolbox.activePackage = model
toolbox.update(model.id) toolbox.update(model.id)
} }
activeAction: function() onActiveAction: toolbox.cancelDownload()
{
toolbox.cancelDownload()
}
// Don't allow installing while another download is running // Don't allow installing while another download is running
enabled: !(toolbox.isDownloading && toolbox.activePackage != model) enabled: !(toolbox.isDownloading && toolbox.activePackage != model) && !loginRequired
opacity: enabled ? 1.0 : 0.5 opacity: enabled ? 1.0 : 0.5
visible: canUpdate visible: canUpdate
} }
Connections Connections
{ {
target: toolbox target: toolbox

View file

@ -1,7 +1,7 @@
// Copyright (c) 2018 Ultimaker B.V. // Copyright (c) 2018 Ultimaker B.V.
// Toolbox is released under the terms of the LGPLv3 or higher. // Toolbox is released under the terms of the LGPLv3 or higher.
import QtQuick 2.7 import QtQuick 2.10
import QtQuick.Controls 1.4 import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4 import QtQuick.Controls.Styles 1.4
import QtQuick.Layouts 1.3 import QtQuick.Layouts 1.3
@ -23,8 +23,9 @@ Column
width: parent.width width: parent.width
color: UM.Theme.getColor("text_medium") color: UM.Theme.getColor("text_medium")
font: UM.Theme.getFont("medium") font: UM.Theme.getFont("medium")
renderType: Text.NativeRendering
} }
GridLayout Grid
{ {
id: grid id: grid
width: parent.width - 2 * parent.padding width: parent.width - 2 * parent.padding
@ -34,10 +35,12 @@ Column
Repeater Repeater
{ {
model: gridArea.model model: gridArea.model
delegate: ToolboxDownloadsGridTile delegate: Loader
{ {
Layout.preferredWidth: (grid.width - (grid.columns - 1) * grid.columnSpacing) / grid.columns asynchronous: true
Layout.preferredHeight: UM.Theme.getSize("toolbox_thumbnail_small").height width: Math.round((grid.width - (grid.columns - 1) * grid.columnSpacing) / grid.columns)
height: UM.Theme.getSize("toolbox_thumbnail_small").height
source: "ToolboxDownloadsGridTile.qml"
} }
} }
} }

View file

@ -1,11 +1,12 @@
// Copyright (c) 2018 Ultimaker B.V. // Copyright (c) 2018 Ultimaker B.V.
// Toolbox is released under the terms of the LGPLv3 or higher. // Toolbox is released under the terms of the LGPLv3 or higher.
import QtQuick 2.3 import QtQuick 2.10
import QtQuick.Controls 1.4 import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4 import QtQuick.Controls.Styles 1.4
import QtQuick.Layouts 1.3 import QtQuick.Layouts 1.3
import UM 1.1 as UM import UM 1.1 as UM
import Cura 1.1 as Cura
Item Item
{ {
@ -14,90 +15,13 @@ Item
property int installedPackages: (toolbox.viewCategory == "material" && model.type === undefined) ? toolbox.getNumberOfInstalledPackagesByAuthor(model.id) : (toolbox.isInstalled(model.id) ? 1 : 0) property int installedPackages: (toolbox.viewCategory == "material" && model.type === undefined) ? toolbox.getNumberOfInstalledPackagesByAuthor(model.id) : (toolbox.isInstalled(model.id) ? 1 : 0)
height: childrenRect.height height: childrenRect.height
Layout.alignment: Qt.AlignTop | Qt.AlignLeft Layout.alignment: Qt.AlignTop | Qt.AlignLeft
Rectangle
{
id: highlight
anchors.fill: parent
opacity: 0.0
color: UM.Theme.getColor("primary")
}
Row
{
width: parent.width
height: childrenRect.height
spacing: Math.floor(UM.Theme.getSize("narrow_margin").width)
Rectangle
{
id: thumbnail
width: UM.Theme.getSize("toolbox_thumbnail_small").width
height: UM.Theme.getSize("toolbox_thumbnail_small").height
color: "white"
border.width: UM.Theme.getSize("default_lining").width
border.color: UM.Theme.getColor("lining")
Image
{
anchors.centerIn: parent
width: UM.Theme.getSize("toolbox_thumbnail_small").width - UM.Theme.getSize("wide_margin").width
height: UM.Theme.getSize("toolbox_thumbnail_small").height - UM.Theme.getSize("wide_margin").width
fillMode: Image.PreserveAspectFit
source: model.icon_url || "../images/logobot.svg"
mipmap: true
}
UM.RecolorImage
{
width: (parent.width * 0.4) | 0
height: (parent.height * 0.4) | 0
anchors
{
bottom: parent.bottom
right: parent.right
}
sourceSize.height: height
visible: installedPackages != 0
color: (installedPackages == packageCount) ? UM.Theme.getColor("primary") : UM.Theme.getColor("border")
source: "../images/installed_check.svg"
}
}
Column
{
width: parent.width - thumbnail.width - parent.spacing
spacing: Math.floor(UM.Theme.getSize("narrow_margin").width)
Label
{
id: name
text: model.name
width: parent.width
wrapMode: Text.WordWrap
color: UM.Theme.getColor("text")
font: UM.Theme.getFont("default_bold")
}
Label
{
id: info
text: model.description
maximumLineCount: 2
elide: Text.ElideRight
width: parent.width
wrapMode: Text.WordWrap
color: UM.Theme.getColor("text_medium")
font: UM.Theme.getFont("default")
}
}
}
MouseArea MouseArea
{ {
anchors.fill: parent anchors.fill: parent
hoverEnabled: true hoverEnabled: true
onEntered: onEntered: thumbnail.border.color = UM.Theme.getColor("primary")
{ onExited: thumbnail.border.color = UM.Theme.getColor("lining")
thumbnail.border.color = UM.Theme.getColor("primary")
highlight.opacity = 0.1
}
onExited:
{
thumbnail.border.color = UM.Theme.getColor("lining")
highlight.opacity = 0.0
}
onClicked: onClicked:
{ {
base.selection = model base.selection = model
@ -127,4 +51,83 @@ Item
} }
} }
} }
Rectangle
{
id: thumbnail
width: UM.Theme.getSize("toolbox_thumbnail_small").width
height: UM.Theme.getSize("toolbox_thumbnail_small").height
color: UM.Theme.getColor("main_background")
border.width: UM.Theme.getSize("default_lining").width
border.color: UM.Theme.getColor("lining")
Image
{
anchors.centerIn: parent
width: UM.Theme.getSize("toolbox_thumbnail_small").width - UM.Theme.getSize("wide_margin").width
height: UM.Theme.getSize("toolbox_thumbnail_small").height - UM.Theme.getSize("wide_margin").width
fillMode: Image.PreserveAspectFit
source: model.icon_url || "../images/logobot.svg"
mipmap: true
}
UM.RecolorImage
{
width: (parent.width * 0.4) | 0
height: (parent.height * 0.4) | 0
anchors
{
bottom: parent.bottom
right: parent.right
}
sourceSize.height: height
visible: installedPackages != 0
color: (installedPackages == packageCount) ? UM.Theme.getColor("primary") : UM.Theme.getColor("border")
source: "../images/installed_check.svg"
}
}
Item
{
anchors
{
left: thumbnail.right
leftMargin: Math.floor(UM.Theme.getSize("narrow_margin").width)
right: parent.right
top: parent.top
bottom: parent.bottom
}
Label
{
id: name
text: model.name
width: parent.width
elide: Text.ElideRight
color: UM.Theme.getColor("text")
font: UM.Theme.getFont("default_bold")
}
Label
{
id: info
text: model.description
elide: Text.ElideRight
width: parent.width
wrapMode: Text.WordWrap
color: UM.Theme.getColor("text_medium")
font: UM.Theme.getFont("default")
anchors.top: name.bottom
anchors.bottom: rating.top
verticalAlignment: Text.AlignVCenter
maximumLineCount: 2
}
SmallRatingWidget
{
id: rating
anchors
{
bottom: parent.bottom
left: parent.left
right: parent.right
}
}
}
} }

View file

@ -1,7 +1,7 @@
// Copyright (c) 2018 Ultimaker B.V. // Copyright (c) 2018 Ultimaker B.V.
// Toolbox is released under the terms of the LGPLv3 or higher. // Toolbox is released under the terms of the LGPLv3 or higher.
import QtQuick 2.7 import QtQuick 2.10
import QtQuick.Controls 1.4 import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4 import QtQuick.Controls.Styles 1.4
import UM 1.1 as UM import UM 1.1 as UM
@ -24,29 +24,33 @@ Rectangle
width: parent.width width: parent.width
color: UM.Theme.getColor("text_medium") color: UM.Theme.getColor("text_medium")
font: UM.Theme.getFont("medium") font: UM.Theme.getFont("medium")
renderType: Text.NativeRendering
} }
Grid Grid
{ {
height: childrenRect.height height: childrenRect.height
spacing: UM.Theme.getSize("wide_margin").width spacing: UM.Theme.getSize("wide_margin").width
columns: 3 columns: 3
anchors anchors.horizontalCenter: parent.horizontalCenter
{
horizontalCenter: parent.horizontalCenter
}
Repeater Repeater
{ {
model: { model:
if ( toolbox.viewCategory == "plugin" ) {
if (toolbox.viewCategory == "plugin")
{ {
return toolbox.pluginsShowcaseModel return toolbox.pluginsShowcaseModel
} }
if ( toolbox.viewCategory == "material" ) if (toolbox.viewCategory == "material")
{ {
return toolbox.materialsShowcaseModel return toolbox.materialsShowcaseModel
} }
} }
delegate: ToolboxDownloadsShowcaseTile {} delegate: Loader
{
asynchronous: true
source: "ToolboxDownloadsShowcaseTile.qml"
}
} }
} }
} }

View file

@ -1,7 +1,7 @@
// Copyright (c) 2018 Ultimaker B.V. // Copyright (c) 2018 Ultimaker B.V.
// Cura is released under the terms of the LGPLv3 or higher. // Cura is released under the terms of the LGPLv3 or higher.
import QtQuick 2.7 import QtQuick 2.10
import QtQuick.Controls 1.4 import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4 import QtQuick.Controls.Styles 1.4
import QtGraphicalEffects 1.0 import QtGraphicalEffects 1.0
@ -13,90 +13,79 @@ Rectangle
property int installedPackages: toolbox.viewCategory == "material" ? toolbox.getNumberOfInstalledPackagesByAuthor(model.id) : (toolbox.isInstalled(model.id) ? 1 : 0) property int installedPackages: toolbox.viewCategory == "material" ? toolbox.getNumberOfInstalledPackagesByAuthor(model.id) : (toolbox.isInstalled(model.id) ? 1 : 0)
id: tileBase id: tileBase
width: UM.Theme.getSize("toolbox_thumbnail_large").width + (2 * UM.Theme.getSize("default_lining").width) width: UM.Theme.getSize("toolbox_thumbnail_large").width + (2 * UM.Theme.getSize("default_lining").width)
height: thumbnail.height + packageNameBackground.height + (2 * UM.Theme.getSize("default_lining").width) height: thumbnail.height + packageName.height + rating.height + UM.Theme.getSize("default_margin").width
border.width: UM.Theme.getSize("default_lining").width border.width: UM.Theme.getSize("default_lining").width
border.color: UM.Theme.getColor("lining") border.color: UM.Theme.getColor("lining")
color: "transparent" color: UM.Theme.getColor("main_background")
Rectangle Image
{ {
id: thumbnail id: thumbnail
color: "white" height: UM.Theme.getSize("toolbox_thumbnail_large").height - 4 * UM.Theme.getSize("default_margin").height
width: UM.Theme.getSize("toolbox_thumbnail_large").width width: UM.Theme.getSize("toolbox_thumbnail_large").height - 4 * UM.Theme.getSize("default_margin").height
height: UM.Theme.getSize("toolbox_thumbnail_large").height fillMode: Image.PreserveAspectFit
source: model.icon_url || "../images/logobot.svg"
mipmap: true
anchors anchors
{ {
top: parent.top top: parent.top
topMargin: UM.Theme.getSize("default_margin").height
horizontalCenter: parent.horizontalCenter horizontalCenter: parent.horizontalCenter
topMargin: UM.Theme.getSize("default_lining").width
} }
Image }
Label
{
id: packageName
text: model.name
anchors
{ {
anchors.centerIn: parent horizontalCenter: parent.horizontalCenter
width: UM.Theme.getSize("toolbox_thumbnail_large").width - 2 * UM.Theme.getSize("default_margin").width top: thumbnail.bottom
height: UM.Theme.getSize("toolbox_thumbnail_large").height - 2 * UM.Theme.getSize("default_margin").height
fillMode: Image.PreserveAspectFit
source: model.icon_url || "../images/logobot.svg"
mipmap: true
} }
UM.RecolorImage verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
renderType: Text.NativeRendering
height: UM.Theme.getSize("toolbox_heading_label").height
width: parent.width - UM.Theme.getSize("default_margin").width
wrapMode: Text.WordWrap
elide: Text.ElideRight
font: UM.Theme.getFont("medium_bold")
}
UM.RecolorImage
{
width: (parent.width * 0.20) | 0
height: width
anchors
{ {
width: (parent.width * 0.3) | 0 bottom: bottomBorder.top
height: (parent.height * 0.3) | 0 right: parent.right
anchors
{
bottom: parent.bottom
right: parent.right
bottomMargin: UM.Theme.getSize("default_lining").width
}
visible: installedPackages != 0
color: (installedPackages == packageCount) ? UM.Theme.getColor("primary") : UM.Theme.getColor("border")
source: "../images/installed_check.svg"
} }
visible: installedPackages != 0
color: (installedPackages == packageCount) ? UM.Theme.getColor("primary") : UM.Theme.getColor("border")
source: "../images/installed_check.svg"
}
SmallRatingWidget
{
id: rating
anchors.bottom: parent.bottom
anchors.bottomMargin: UM.Theme.getSize("narrow_margin").height
anchors.horizontalCenter: parent.horizontalCenter
} }
Rectangle Rectangle
{ {
id: packageNameBackground id: bottomBorder
color: UM.Theme.getColor("primary") color: UM.Theme.getColor("primary")
anchors anchors.bottom: parent.bottom
{
top: thumbnail.bottom
horizontalCenter: parent.horizontalCenter
}
height: UM.Theme.getSize("toolbox_heading_label").height
width: parent.width width: parent.width
Label height: UM.Theme.getSize("toolbox_header_highlight").height
{
id: packageName
text: model.name
anchors
{
horizontalCenter: parent.horizontalCenter
}
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
height: UM.Theme.getSize("toolbox_heading_label").height
width: parent.width
wrapMode: Text.WordWrap
color: UM.Theme.getColor("button_text")
font: UM.Theme.getFont("medium_bold")
}
} }
MouseArea MouseArea
{ {
anchors.fill: parent anchors.fill: parent
hoverEnabled: true hoverEnabled: true
onEntered: onEntered: tileBase.border.color = UM.Theme.getColor("primary")
{ onExited: tileBase.border.color = UM.Theme.getColor("lining")
packageName.color = UM.Theme.getColor("button_text_hover")
packageNameBackground.color = UM.Theme.getColor("primary_hover")
tileBase.border.color = UM.Theme.getColor("primary_hover")
}
onExited:
{
packageName.color = UM.Theme.getColor("button_text")
packageNameBackground.color = UM.Theme.getColor("primary")
tileBase.border.color = UM.Theme.getColor("lining")
}
onClicked: onClicked:
{ {
base.selection = model base.selection = model

View file

@ -1,7 +1,7 @@
// Copyright (c) 2018 Ultimaker B.V. // Copyright (c) 2018 Ultimaker B.V.
// Toolbox is released under the terms of the LGPLv3 or higher. // Toolbox is released under the terms of the LGPLv3 or higher.
import QtQuick 2.7 import QtQuick 2.10
import QtQuick.Controls 1.4 import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4 import QtQuick.Controls.Styles 1.4
@ -18,5 +18,6 @@ Rectangle
{ {
centerIn: parent centerIn: parent
} }
renderType: Text.NativeRendering
} }
} }

View file

@ -1,22 +1,24 @@
// Copyright (c) 2018 Ultimaker B.V. // Copyright (c) 2018 Ultimaker B.V.
// Toolbox is released under the terms of the LGPLv3 or higher. // Toolbox is released under the terms of the LGPLv3 or higher.
import QtQuick 2.2 import QtQuick 2.10
import QtQuick.Controls 1.4 import QtQuick.Controls 2.3
import QtQuick.Controls.Styles 1.4
import UM 1.1 as UM import UM 1.1 as UM
import Cura 1.0 as Cura
Item Item
{ {
id: footer id: footer
width: parent.width width: parent.width
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
height: visible ? Math.floor(UM.Theme.getSize("toolbox_footer").height) : 0 height: visible ? UM.Theme.getSize("toolbox_footer").height : 0
Label Label
{ {
text: catalog.i18nc("@info", "You will need to restart Cura before changes in packages have effect.") text: catalog.i18nc("@info", "You will need to restart Cura before changes in packages have effect.")
color: UM.Theme.getColor("text") color: UM.Theme.getColor("text")
height: Math.floor(UM.Theme.getSize("toolbox_footer_button").height) height: UM.Theme.getSize("toolbox_footer_button").height
verticalAlignment: Text.AlignVCenter verticalAlignment: Text.AlignVCenter
anchors anchors
{ {
@ -26,12 +28,12 @@ Item
right: restartButton.right right: restartButton.right
rightMargin: UM.Theme.getSize("default_margin").width rightMargin: UM.Theme.getSize("default_margin").width
} }
renderType: Text.NativeRendering
} }
Button
Cura.PrimaryButton
{ {
id: restartButton id: restartButton
text: catalog.i18nc("@info:button", "Quit Cura")
anchors anchors
{ {
top: parent.top top: parent.top
@ -39,26 +41,11 @@ Item
right: parent.right right: parent.right
rightMargin: UM.Theme.getSize("wide_margin").width rightMargin: UM.Theme.getSize("wide_margin").width
} }
iconName: "dialog-restart" height: UM.Theme.getSize("toolbox_footer_button").height
text: catalog.i18nc("@info:button", "Quit Cura")
onClicked: toolbox.restart() onClicked: toolbox.restart()
style: ButtonStyle
{
background: Rectangle
{
implicitWidth: UM.Theme.getSize("toolbox_footer_button").width
implicitHeight: Math.floor(UM.Theme.getSize("toolbox_footer_button").height)
color: control.hovered ? UM.Theme.getColor("primary_hover") : UM.Theme.getColor("primary")
}
label: Label
{
color: UM.Theme.getColor("button_text")
font: UM.Theme.getFont("default_bold")
text: control.text
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
}
}
} }
ToolboxShadow ToolboxShadow
{ {
visible: footer.visible visible: footer.visible

View file

@ -1,7 +1,7 @@
// Copyright (c) 2018 Ultimaker B.V. // Copyright (c) 2018 Ultimaker B.V.
// Toolbox is released under the terms of the LGPLv3 or higher. // Toolbox is released under the terms of the LGPLv3 or higher.
import QtQuick 2.7 import QtQuick 2.10
import QtQuick.Dialogs 1.1 import QtQuick.Dialogs 1.1
import QtQuick.Window 2.2 import QtQuick.Window 2.2
import QtQuick.Controls 1.4 import QtQuick.Controls 1.4
@ -21,44 +21,40 @@ ScrollView
Column Column
{ {
spacing: UM.Theme.getSize("default_margin").height spacing: UM.Theme.getSize("default_margin").height
visible: toolbox.pluginsInstalledModel.items.length > 0
height: childrenRect.height + 4 * UM.Theme.getSize("default_margin").height
anchors anchors
{ {
right: parent.right right: parent.right
left: parent.left left: parent.left
leftMargin: UM.Theme.getSize("wide_margin").width margins: UM.Theme.getSize("default_margin").width
topMargin: UM.Theme.getSize("wide_margin").height
bottomMargin: UM.Theme.getSize("wide_margin").height
top: parent.top top: parent.top
} }
height: childrenRect.height + 4 * UM.Theme.getSize("default_margin").height
Label Label
{ {
visible: toolbox.pluginsInstalledModel.items.length > 0 width: page.width
width: parent.width
text: catalog.i18nc("@title:tab", "Plugins") text: catalog.i18nc("@title:tab", "Plugins")
color: UM.Theme.getColor("text_medium") color: UM.Theme.getColor("text_medium")
font: UM.Theme.getFont("medium") font: UM.Theme.getFont("medium")
renderType: Text.NativeRendering
} }
Rectangle Rectangle
{ {
visible: toolbox.pluginsInstalledModel.items.length > 0
color: "transparent" color: "transparent"
width: parent.width width: parent.width
height: childrenRect.height + 1 * UM.Theme.getSize("default_lining").width height: childrenRect.height + UM.Theme.getSize("default_margin").width
border.color: UM.Theme.getColor("lining") border.color: UM.Theme.getColor("lining")
border.width: UM.Theme.getSize("default_lining").width border.width: UM.Theme.getSize("default_lining").width
Column Column
{ {
height: childrenRect.height
anchors anchors
{ {
top: parent.top top: parent.top
right: parent.right right: parent.right
left: parent.left left: parent.left
leftMargin: UM.Theme.getSize("default_margin").width margins: UM.Theme.getSize("default_margin").width
rightMargin: UM.Theme.getSize("default_margin").width
topMargin: UM.Theme.getSize("default_lining").width
bottomMargin: UM.Theme.getSize("default_lining").width
} }
Repeater Repeater
{ {
@ -70,32 +66,27 @@ ScrollView
} }
Label Label
{ {
visible: toolbox.materialsInstalledModel.items.length > 0
width: page.width
text: catalog.i18nc("@title:tab", "Materials") text: catalog.i18nc("@title:tab", "Materials")
color: UM.Theme.getColor("text_medium") color: UM.Theme.getColor("text_medium")
font: UM.Theme.getFont("medium") font: UM.Theme.getFont("medium")
renderType: Text.NativeRendering
} }
Rectangle Rectangle
{ {
visible: toolbox.materialsInstalledModel.items.length > 0
color: "transparent" color: "transparent"
width: parent.width width: parent.width
height: childrenRect.height + 1 * UM.Theme.getSize("default_lining").width height: childrenRect.height + UM.Theme.getSize("default_margin").width
border.color: UM.Theme.getColor("lining") border.color: UM.Theme.getColor("lining")
border.width: UM.Theme.getSize("default_lining").width border.width: UM.Theme.getSize("default_lining").width
Column Column
{ {
height: Math.max( UM.Theme.getSize("wide_margin").height, childrenRect.height)
anchors anchors
{ {
top: parent.top top: parent.top
right: parent.right right: parent.right
left: parent.left left: parent.left
leftMargin: UM.Theme.getSize("default_margin").width margins: UM.Theme.getSize("default_margin").width
rightMargin: UM.Theme.getSize("default_margin").width
topMargin: UM.Theme.getSize("default_lining").width
bottomMargin: UM.Theme.getSize("default_lining").width
} }
Repeater Repeater
{ {

View file

@ -1,7 +1,7 @@
// Copyright (c) 2018 Ultimaker B.V. // Copyright (c) 2018 Ultimaker B.V.
// Toolbox is released under the terms of the LGPLv3 or higher. // Toolbox is released under the terms of the LGPLv3 or higher.
import QtQuick 2.7 import QtQuick 2.10
import QtQuick.Controls 1.4 import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4 import QtQuick.Controls.Styles 1.4
import UM 1.1 as UM import UM 1.1 as UM
@ -51,6 +51,7 @@ Item
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
font: UM.Theme.getFont("default_bold") font: UM.Theme.getFont("default_bold")
color: pluginInfo.color color: pluginInfo.color
renderType: Text.NativeRendering
} }
Label Label
{ {
@ -60,6 +61,7 @@ Item
width: parent.width width: parent.width
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
color: pluginInfo.color color: pluginInfo.color
renderType: Text.NativeRendering
} }
} }
Column Column
@ -88,6 +90,7 @@ Item
onLinkActivated: Qt.openUrlExternally("mailto:" + model.author_email + "?Subject=Cura: " + model.name + " Plugin") onLinkActivated: Qt.openUrlExternally("mailto:" + model.author_email + "?Subject=Cura: " + model.name + " Plugin")
color: model.enabled ? UM.Theme.getColor("text") : UM.Theme.getColor("lining") color: model.enabled ? UM.Theme.getColor("text") : UM.Theme.getColor("lining")
linkColor: UM.Theme.getColor("text_link") linkColor: UM.Theme.getColor("text_link")
renderType: Text.NativeRendering
} }
Label Label
@ -98,6 +101,7 @@ Item
color: UM.Theme.getColor("text") color: UM.Theme.getColor("text")
verticalAlignment: Text.AlignVCenter verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignLeft horizontalAlignment: Text.AlignLeft
renderType: Text.NativeRendering
} }
} }
ToolboxInstalledTileActions ToolboxInstalledTileActions

View file

@ -1,15 +1,18 @@
// Copyright (c) 2018 Ultimaker B.V. // Copyright (c) 2018 Ultimaker B.V.
// Toolbox is released under the terms of the LGPLv3 or higher. // Toolbox is released under the terms of the LGPLv3 or higher.
import QtQuick 2.7 import QtQuick 2.10
import QtQuick.Controls 1.4 import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4 import QtQuick.Controls.Styles 1.4
import UM 1.1 as UM import UM 1.1 as UM
import Cura 1.1 as Cura
Column Column
{ {
property bool canUpdate: false property bool canUpdate: false
property bool canDowngrade: false property bool canDowngrade: false
property bool loginRequired: model.login_required && !Cura.API.account.isLoggedIn
width: UM.Theme.getSize("toolbox_action_button").width width: UM.Theme.getSize("toolbox_action_button").width
spacing: UM.Theme.getSize("narrow_margin").height spacing: UM.Theme.getSize("narrow_margin").height
@ -21,6 +24,7 @@ Column
font: UM.Theme.getFont("default") font: UM.Theme.getFont("default")
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
width: parent.width width: parent.width
renderType: Text.NativeRendering
} }
ToolboxProgressButton ToolboxProgressButton
@ -30,59 +34,49 @@ Column
readyLabel: catalog.i18nc("@action:button", "Update") readyLabel: catalog.i18nc("@action:button", "Update")
activeLabel: catalog.i18nc("@action:button", "Updating") activeLabel: catalog.i18nc("@action:button", "Updating")
completeLabel: catalog.i18nc("@action:button", "Updated") completeLabel: catalog.i18nc("@action:button", "Updated")
readyAction: function() onReadyAction:
{ {
toolbox.activePackage = model toolbox.activePackage = model
toolbox.update(model.id) toolbox.update(model.id)
} }
activeAction: function() onActiveAction: toolbox.cancelDownload()
{
toolbox.cancelDownload()
}
// Don't allow installing while another download is running // Don't allow installing while another download is running
enabled: !(toolbox.isDownloading && toolbox.activePackage != model) enabled: !(toolbox.isDownloading && toolbox.activePackage != model) && !loginRequired
opacity: enabled ? 1.0 : 0.5 opacity: enabled ? 1.0 : 0.5
visible: canUpdate visible: canUpdate
} }
Button Label
{
wrapMode: Text.WordWrap
text: catalog.i18nc("@label:The string between <a href=> and </a> is the highlighted link", "<a href='%1'>Log in</a> is required to update")
font: UM.Theme.getFont("default")
color: UM.Theme.getColor("text")
linkColor: UM.Theme.getColor("text_link")
visible: loginRequired
width: updateButton.width
renderType: Text.NativeRendering
MouseArea
{
anchors.fill: parent
onClicked: Cura.API.account.login()
}
}
Cura.SecondaryButton
{ {
id: removeButton id: removeButton
text: canDowngrade ? catalog.i18nc("@action:button", "Downgrade") : catalog.i18nc("@action:button", "Uninstall") text: canDowngrade ? catalog.i18nc("@action:button", "Downgrade") : catalog.i18nc("@action:button", "Uninstall")
visible: !model.is_bundled && model.is_installed visible: !model.is_bundled && model.is_installed
enabled: !toolbox.isDownloading enabled: !toolbox.isDownloading
style: ButtonStyle
{ width: UM.Theme.getSize("toolbox_action_button").width
background: Rectangle height: UM.Theme.getSize("toolbox_action_button").height
{
implicitWidth: UM.Theme.getSize("toolbox_action_button").width fixedWidthMode: true
implicitHeight: UM.Theme.getSize("toolbox_action_button").height
color: "transparent"
border
{
width: UM.Theme.getSize("default_lining").width
color:
{
if (control.hovered)
{
return UM.Theme.getColor("primary_hover")
}
else
{
return UM.Theme.getColor("lining")
}
}
}
}
label: Label
{
text: control.text
color: UM.Theme.getColor("text")
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
font: UM.Theme.getFont("default")
}
}
onClicked: toolbox.checkPackageUsageAndUninstall(model.id) onClicked: toolbox.checkPackageUsageAndUninstall(model.id)
Connections Connections
{ {

View file

@ -1,7 +1,7 @@
// Copyright (c) 2018 Ultimaker B.V. // Copyright (c) 2018 Ultimaker B.V.
// Toolbox is released under the terms of the LGPLv3 or higher. // Toolbox is released under the terms of the LGPLv3 or higher.
import QtQuick 2.2 import QtQuick 2.10
import QtQuick.Dialogs 1.1 import QtQuick.Dialogs 1.1
import QtQuick.Window 2.2 import QtQuick.Window 2.2
import QtQuick.Controls 1.4 import QtQuick.Controls 1.4
@ -32,6 +32,7 @@ UM.Dialog
anchors.right: parent.right anchors.right: parent.right
text: licenseDialog.pluginName + catalog.i18nc("@label", "This plugin contains a license.\nYou need to accept this license to install this plugin.\nDo you agree with the terms below?") text: licenseDialog.pluginName + catalog.i18nc("@label", "This plugin contains a license.\nYou need to accept this license to install this plugin.\nDo you agree with the terms below?")
wrapMode: Text.Wrap wrapMode: Text.Wrap
renderType: Text.NativeRendering
} }
TextArea TextArea
{ {

View file

@ -1,7 +1,7 @@
// Copyright (c) 2018 Ultimaker B.V. // Copyright (c) 2018 Ultimaker B.V.
// Toolbox is released under the terms of the LGPLv3 or higher. // Toolbox is released under the terms of the LGPLv3 or higher.
import QtQuick 2.7 import QtQuick 2.10
import QtQuick.Controls 1.4 import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4 import QtQuick.Controls.Styles 1.4
@ -18,5 +18,6 @@ Rectangle
{ {
centerIn: parent centerIn: parent
} }
renderType: Text.NativeRendering
} }
} }

View file

@ -5,6 +5,7 @@ import QtQuick 2.2
import QtQuick.Controls 1.4 import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4 import QtQuick.Controls.Styles 1.4
import UM 1.1 as UM import UM 1.1 as UM
import Cura 1.0 as Cura
Item Item
@ -18,16 +19,19 @@ Item
property var activeLabel: catalog.i18nc("@action:button", "Cancel") property var activeLabel: catalog.i18nc("@action:button", "Cancel")
property var completeLabel: catalog.i18nc("@action:button", "Installed") property var completeLabel: catalog.i18nc("@action:button", "Installed")
property var readyAction: null // Action when button is ready and clicked (likely install) signal readyAction() // Action when button is ready and clicked (likely install)
property var activeAction: null // Action when button is active and clicked (likely cancel) signal activeAction() // Action when button is active and clicked (likely cancel)
property var completeAction: null // Action when button is complete and clicked (likely go to installed) signal completeAction() // Action when button is complete and clicked (likely go to installed)
width: UM.Theme.getSize("toolbox_action_button").width width: UM.Theme.getSize("toolbox_action_button").width
height: UM.Theme.getSize("toolbox_action_button").height height: UM.Theme.getSize("toolbox_action_button").height
Button Cura.PrimaryButton
{ {
id: button id: button
width: UM.Theme.getSize("toolbox_action_button").width
height: UM.Theme.getSize("toolbox_action_button").height
fixedWidthMode: true
text: text:
{ {
if (complete) if (complete)
@ -47,101 +51,15 @@ Item
{ {
if (complete) if (complete)
{ {
return completeAction() completeAction()
} }
else if (active) else if (active)
{ {
return activeAction() activeAction()
} }
else else
{ {
return readyAction() readyAction()
}
}
style: ButtonStyle
{
background: Rectangle
{
implicitWidth: UM.Theme.getSize("toolbox_action_button").width
implicitHeight: UM.Theme.getSize("toolbox_action_button").height
color:
{
if (base.complete)
{
return "transparent"
}
else
{
if (control.hovered)
{
return UM.Theme.getColor("primary_hover")
}
else
{
return UM.Theme.getColor("primary")
}
}
}
border
{
width:
{
if (base.complete)
{
UM.Theme.getSize("default_lining").width
}
else
{
return 0
}
}
color:
{
if (control.hovered)
{
return UM.Theme.getColor("primary_hover")
}
else
{
return UM.Theme.getColor("lining")
}
}
}
}
label: Label
{
text: control.text
color:
{
if (base.complete)
{
return UM.Theme.getColor("text")
}
else
{
if (control.hovered)
{
return UM.Theme.getColor("button_text_hover")
}
else
{
return UM.Theme.getColor("button_text")
}
}
}
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
font:
{
if (base.complete)
{
return UM.Theme.getFont("default")
}
else
{
return UM.Theme.getFont("default_bold")
}
}
} }
} }
} }

View file

@ -1,51 +1,51 @@
// Copyright (c) 2018 Ultimaker B.V. // Copyright (c) 2018 Ultimaker B.V.
// Toolbox is released under the terms of the LGPLv3 or higher. // Toolbox is released under the terms of the LGPLv3 or higher.
import QtQuick 2.2 import QtQuick 2.10
import QtQuick.Controls 1.4 import QtQuick.Controls 2.3
import QtQuick.Controls.Styles 1.4
import UM 1.1 as UM import UM 1.1 as UM
Button Button
{ {
id: control
property bool active: false property bool active: false
style: ButtonStyle hoverEnabled: true
background: Item
{ {
background: Rectangle implicitWidth: UM.Theme.getSize("toolbox_header_tab").width
implicitHeight: UM.Theme.getSize("toolbox_header_tab").height
Rectangle
{ {
color: "transparent" visible: control.active
implicitWidth: UM.Theme.getSize("toolbox_header_tab").width color: UM.Theme.getColor("primary")
implicitHeight: UM.Theme.getSize("toolbox_header_tab").height anchors.bottom: parent.bottom
Rectangle width: parent.width
{ height: UM.Theme.getSize("toolbox_header_highlight").height
visible: control.active
color: UM.Theme.getColor("toolbox_header_highlight_hover")
anchors.bottom: parent.bottom
width: parent.width
height: UM.Theme.getSize("toolbox_header_highlight").height
}
}
label: Label
{
text: control.text
color:
{
if(control.hovered)
{
return UM.Theme.getColor("toolbox_header_button_text_hovered");
}
if(control.active)
{
return UM.Theme.getColor("toolbox_header_button_text_active");
}
else
{
return UM.Theme.getColor("toolbox_header_button_text_inactive");
}
}
font: control.enabled ? (control.active ? UM.Theme.getFont("medium_bold") : UM.Theme.getFont("medium")) : UM.Theme.getFont("default_italic")
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
} }
} }
} contentItem: Label
{
id: label
text: control.text
color:
{
if(control.hovered)
{
return UM.Theme.getColor("toolbox_header_button_text_hovered");
}
if(control.active)
{
return UM.Theme.getColor("toolbox_header_button_text_active");
}
else
{
return UM.Theme.getColor("toolbox_header_button_text_inactive");
}
}
font: control.enabled ? (control.active ? UM.Theme.getFont("medium_bold") : UM.Theme.getFont("medium")) : UM.Theme.getFont("default_italic")
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
renderType: Text.NativeRendering
}
}

View file

@ -2,18 +2,19 @@
# Cura is released under the terms of the LGPLv3 or higher. # Cura is released under the terms of the LGPLv3 or higher.
import re import re
from typing import Dict from typing import Dict, List, Optional, Union
from PyQt5.QtCore import Qt, pyqtProperty, pyqtSignal from PyQt5.QtCore import Qt, pyqtProperty, pyqtSignal
from UM.Qt.ListModel import ListModel from UM.Qt.ListModel import ListModel
## Model that holds cura packages. By setting the filter property the instances held by this model can be changed. ## Model that holds cura packages. By setting the filter property the instances held by this model can be changed.
class AuthorsModel(ListModel): class AuthorsModel(ListModel):
def __init__(self, parent = None): def __init__(self, parent = None) -> None:
super().__init__(parent) super().__init__(parent)
self._metadata = None self._metadata = None # type: Optional[List[Dict[str, Union[str, List[str], int]]]]
self.addRoleName(Qt.UserRole + 1, "id") self.addRoleName(Qt.UserRole + 1, "id")
self.addRoleName(Qt.UserRole + 2, "name") self.addRoleName(Qt.UserRole + 2, "name")
@ -25,39 +26,40 @@ class AuthorsModel(ListModel):
self.addRoleName(Qt.UserRole + 8, "description") self.addRoleName(Qt.UserRole + 8, "description")
# List of filters for queries. The result is the union of the each list of results. # List of filters for queries. The result is the union of the each list of results.
self._filter = {} # type: Dict[str,str] self._filter = {} # type: Dict[str, str]
def setMetadata(self, data): def setMetadata(self, data: List[Dict[str, Union[str, List[str], int]]]):
self._metadata = data if self._metadata != data:
self._update() self._metadata = data
self._update()
def _update(self): def _update(self) -> None:
items = [] items = [] # type: List[Dict[str, Union[str, List[str], int, None]]]
if not self._metadata: if not self._metadata:
self.setItems([]) self.setItems(items)
return return
for author in self._metadata: for author in self._metadata:
items.append({ items.append({
"id": author["author_id"], "id": author.get("author_id"),
"name": author["display_name"], "name": author.get("display_name"),
"email": author["email"] if "email" in author else None, "email": author.get("email"),
"website": author["website"], "website": author.get("website"),
"package_count": author["package_count"] if "package_count" in author else 0, "package_count": author.get("package_count", 0),
"package_types": author["package_types"] if "package_types" in author else [], "package_types": author.get("package_types", []),
"icon_url": author["icon_url"] if "icon_url" in author else None, "icon_url": author.get("icon_url"),
"description": "Material and quality profiles from {author_name}".format(author_name = author["display_name"]) "description": "Material and quality profiles from {author_name}".format(author_name = author.get("display_name", ""))
}) })
# Filter on all the key-word arguments. # Filter on all the key-word arguments.
for key, value in self._filter.items(): for key, value in self._filter.items():
if key is "package_types": if key is "package_types":
key_filter = lambda item, value = value: value in item["package_types"] key_filter = lambda item, value = value: value in item["package_types"] # type: ignore
elif "*" in value: elif "*" in value:
key_filter = lambda item, key = key, value = value: self._matchRegExp(item, key, value) key_filter = lambda item, key = key, value = value: self._matchRegExp(item, key, value) # type: ignore
else: else:
key_filter = lambda item, key = key, value = value: self._matchString(item, key, value) key_filter = lambda item, key = key, value = value: self._matchString(item, key, value) # type: ignore
items = filter(key_filter, items) items = filter(key_filter, items) # type: ignore
# Execute all filters. # Execute all filters.
filtered_items = list(items) filtered_items = list(items)

View file

@ -33,20 +33,25 @@ class PackagesModel(ListModel):
self.addRoleName(Qt.UserRole + 12, "last_updated") self.addRoleName(Qt.UserRole + 12, "last_updated")
self.addRoleName(Qt.UserRole + 13, "is_bundled") self.addRoleName(Qt.UserRole + 13, "is_bundled")
self.addRoleName(Qt.UserRole + 14, "is_active") self.addRoleName(Qt.UserRole + 14, "is_active")
self.addRoleName(Qt.UserRole + 15, "is_installed") # Scheduled pkgs are included in the model but should not be marked as actually installed self.addRoleName(Qt.UserRole + 15, "is_installed") # Scheduled pkgs are included in the model but should not be marked as actually installed
self.addRoleName(Qt.UserRole + 16, "has_configs") self.addRoleName(Qt.UserRole + 16, "has_configs")
self.addRoleName(Qt.UserRole + 17, "supported_configs") self.addRoleName(Qt.UserRole + 17, "supported_configs")
self.addRoleName(Qt.UserRole + 18, "download_count") self.addRoleName(Qt.UserRole + 18, "download_count")
self.addRoleName(Qt.UserRole + 19, "tags") self.addRoleName(Qt.UserRole + 19, "tags")
self.addRoleName(Qt.UserRole + 20, "links") self.addRoleName(Qt.UserRole + 20, "links")
self.addRoleName(Qt.UserRole + 21, "website") self.addRoleName(Qt.UserRole + 21, "website")
self.addRoleName(Qt.UserRole + 22, "login_required")
self.addRoleName(Qt.UserRole + 23, "average_rating")
self.addRoleName(Qt.UserRole + 24, "num_ratings")
self.addRoleName(Qt.UserRole + 25, "user_rating")
# List of filters for queries. The result is the union of the each list of results. # List of filters for queries. The result is the union of the each list of results.
self._filter = {} # type: Dict[str, str] self._filter = {} # type: Dict[str, str]
def setMetadata(self, data): def setMetadata(self, data):
self._metadata = data if self._metadata != data:
self._update() self._metadata = data
self._update()
def _update(self): def _update(self):
items = [] items = []
@ -99,6 +104,10 @@ class PackagesModel(ListModel):
"tags": package["tags"] if "tags" in package else [], "tags": package["tags"] if "tags" in package else [],
"links": links_dict, "links": links_dict,
"website": package["website"] if "website" in package else None, "website": package["website"] if "website" in package else None,
"login_required": "login-required" in package.get("tags", []),
"average_rating": float(package.get("rating", {}).get("average", 0)),
"num_ratings": package.get("rating", {}).get("count", 0),
"user_rating": package.get("rating", {}).get("user_rating", 0)
}) })
# Filter on all the key-word arguments. # Filter on all the key-word arguments.

View file

@ -13,7 +13,6 @@ from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkRequest, QNetworkRepl
from UM.Logger import Logger from UM.Logger import Logger
from UM.PluginRegistry import PluginRegistry from UM.PluginRegistry import PluginRegistry
from UM.Extension import Extension from UM.Extension import Extension
from UM.Qt.ListModel import ListModel
from UM.i18n import i18nCatalog from UM.i18n import i18nCatalog
from UM.Version import Version from UM.Version import Version
@ -31,8 +30,8 @@ i18n_catalog = i18nCatalog("cura")
## The Toolbox class is responsible of communicating with the server through the API ## The Toolbox class is responsible of communicating with the server through the API
class Toolbox(QObject, Extension): class Toolbox(QObject, Extension):
DEFAULT_CLOUD_API_ROOT = "https://api.ultimaker.com" #type: str DEFAULT_CLOUD_API_ROOT = "https://api.ultimaker.com" # type: str
DEFAULT_CLOUD_API_VERSION = 1 #type: int DEFAULT_CLOUD_API_VERSION = 1 # type: int
def __init__(self, application: CuraApplication) -> None: def __init__(self, application: CuraApplication) -> None:
super().__init__() super().__init__()
@ -50,47 +49,35 @@ class Toolbox(QObject, Extension):
self._download_progress = 0 # type: float self._download_progress = 0 # type: float
self._is_downloading = False # type: bool self._is_downloading = False # type: bool
self._network_manager = None # type: Optional[QNetworkAccessManager] self._network_manager = None # type: Optional[QNetworkAccessManager]
self._request_header = [ self._request_headers = [] # type: List[Tuple[bytes, bytes]]
b"User-Agent", self._updateRequestHeader()
str.encode(
"%s/%s (%s %s)" % (
self._application.getApplicationName(),
self._application.getVersion(),
platform.system(),
platform.machine(),
)
)
]
self._request_urls = {} # type: Dict[str, QUrl] self._request_urls = {} # type: Dict[str, QUrl]
self._to_update = [] # type: List[str] # Package_ids that are waiting to be updated self._to_update = [] # type: List[str] # Package_ids that are waiting to be updated
self._old_plugin_ids = set() # type: Set[str] self._old_plugin_ids = set() # type: Set[str]
self._old_plugin_metadata = dict() # type: Dict[str, Dict[str, Any]] self._old_plugin_metadata = dict() # type: Dict[str, Dict[str, Any]]
# Data: # The responses as given by the server parsed to a list.
self._metadata = { self._server_response_data = {
"authors": [], "authors": [],
"packages": [], "packages": []
"plugins_showcase": [],
"plugins_available": [],
"plugins_installed": [],
"materials_showcase": [],
"materials_available": [],
"materials_installed": [],
"materials_generic": []
} # type: Dict[str, List[Any]] } # type: Dict[str, List[Any]]
# Models: # Models:
self._models = { self._models = {
"authors": AuthorsModel(self), "authors": AuthorsModel(self),
"packages": PackagesModel(self), "packages": PackagesModel(self),
"plugins_showcase": PackagesModel(self), } # type: Dict[str, Union[AuthorsModel, PackagesModel]]
"plugins_available": PackagesModel(self),
"plugins_installed": PackagesModel(self), self._plugins_showcase_model = PackagesModel(self)
"materials_showcase": AuthorsModel(self), self._plugins_available_model = PackagesModel(self)
"materials_available": AuthorsModel(self), self._plugins_installed_model = PackagesModel(self)
"materials_installed": PackagesModel(self),
"materials_generic": PackagesModel(self) self._materials_showcase_model = AuthorsModel(self)
} # type: Dict[str, ListModel] self._materials_available_model = AuthorsModel(self)
self._materials_installed_model = PackagesModel(self)
self._materials_generic_model = PackagesModel(self)
# These properties are for keeping track of the UI state: # These properties are for keeping track of the UI state:
# ---------------------------------------------------------------------- # ----------------------------------------------------------------------
@ -120,6 +107,7 @@ class Toolbox(QObject, Extension):
self._restart_dialog_message = "" # type: str self._restart_dialog_message = "" # type: str
self._application.initializationFinished.connect(self._onAppInitialized) self._application.initializationFinished.connect(self._onAppInitialized)
self._application.getCuraAPI().account.loginStateChanged.connect(self._updateRequestHeader)
# Signals: # Signals:
# -------------------------------------------------------------------------- # --------------------------------------------------------------------------
@ -139,12 +127,38 @@ class Toolbox(QObject, Extension):
showLicenseDialog = pyqtSignal() showLicenseDialog = pyqtSignal()
uninstallVariablesChanged = pyqtSignal() uninstallVariablesChanged = pyqtSignal()
def _updateRequestHeader(self):
self._request_headers = [
(b"User-Agent",
str.encode(
"%s/%s (%s %s)" % (
self._application.getApplicationName(),
self._application.getVersion(),
platform.system(),
platform.machine(),
)
))
]
access_token = self._application.getCuraAPI().account.accessToken
if access_token:
self._request_headers.append((b"Authorization", "Bearer {}".format(access_token).encode()))
def _resetUninstallVariables(self) -> None: def _resetUninstallVariables(self) -> None:
self._package_id_to_uninstall = None # type: Optional[str] self._package_id_to_uninstall = None # type: Optional[str]
self._package_name_to_uninstall = "" self._package_name_to_uninstall = ""
self._package_used_materials = [] # type: List[Tuple[GlobalStack, str, str]] self._package_used_materials = [] # type: List[Tuple[GlobalStack, str, str]]
self._package_used_qualities = [] # type: List[Tuple[GlobalStack, str, str]] self._package_used_qualities = [] # type: List[Tuple[GlobalStack, str, str]]
@pyqtSlot(str, int)
def ratePackage(self, package_id: str, rating: int) -> None:
url = QUrl("{base_url}/packages/{package_id}/ratings".format(base_url=self._api_url, package_id = package_id))
self._rate_request = QNetworkRequest(url)
for header_name, header_value in self._request_headers:
cast(QNetworkRequest, self._rate_request).setRawHeader(header_name, header_value)
data = "{\"data\": {\"cura_version\": \"%s\", \"rating\": %i}}" % (Version(self._application.getVersion()), rating)
self._rate_reply = cast(QNetworkAccessManager, self._network_manager).put(self._rate_request, data.encode())
@pyqtSlot(result = str) @pyqtSlot(result = str)
def getLicenseDialogPluginName(self) -> str: def getLicenseDialogPluginName(self) -> str:
return self._license_dialog_plugin_name return self._license_dialog_plugin_name
@ -178,12 +192,7 @@ class Toolbox(QObject, Extension):
) )
self._request_urls = { self._request_urls = {
"authors": QUrl("{base_url}/authors".format(base_url = self._api_url)), "authors": QUrl("{base_url}/authors".format(base_url = self._api_url)),
"packages": QUrl("{base_url}/packages".format(base_url = self._api_url)), "packages": QUrl("{base_url}/packages".format(base_url = self._api_url))
"plugins_showcase": QUrl("{base_url}/showcase".format(base_url = self._api_url)),
"plugins_available": QUrl("{base_url}/packages?package_type=plugin".format(base_url = self._api_url)),
"materials_showcase": QUrl("{base_url}/showcase".format(base_url = self._api_url)),
"materials_available": QUrl("{base_url}/packages?package_type=material".format(base_url = self._api_url)),
"materials_generic": QUrl("{base_url}/packages?package_type=material&tags=generic".format(base_url = self._api_url))
} }
# Get the API root for the packages API depending on Cura version settings. # Get the API root for the packages API depending on Cura version settings.
@ -192,9 +201,9 @@ class Toolbox(QObject, Extension):
return self.DEFAULT_CLOUD_API_ROOT return self.DEFAULT_CLOUD_API_ROOT
if not hasattr(cura.CuraVersion, "CuraCloudAPIRoot"): # type: ignore if not hasattr(cura.CuraVersion, "CuraCloudAPIRoot"): # type: ignore
return self.DEFAULT_CLOUD_API_ROOT return self.DEFAULT_CLOUD_API_ROOT
if not cura.CuraVersion.CuraCloudAPIRoot: # type: ignore if not cura.CuraVersion.CuraCloudAPIRoot: # type: ignore
return self.DEFAULT_CLOUD_API_ROOT return self.DEFAULT_CLOUD_API_ROOT
return cura.CuraVersion.CuraCloudAPIRoot # type: ignore return cura.CuraVersion.CuraCloudAPIRoot # type: ignore
# Get the cloud API version from CuraVersion # Get the cloud API version from CuraVersion
def _getCloudAPIVersion(self) -> int: def _getCloudAPIVersion(self) -> int:
@ -202,9 +211,9 @@ class Toolbox(QObject, Extension):
return self.DEFAULT_CLOUD_API_VERSION return self.DEFAULT_CLOUD_API_VERSION
if not hasattr(cura.CuraVersion, "CuraCloudAPIVersion"): # type: ignore if not hasattr(cura.CuraVersion, "CuraCloudAPIVersion"): # type: ignore
return self.DEFAULT_CLOUD_API_VERSION return self.DEFAULT_CLOUD_API_VERSION
if not cura.CuraVersion.CuraCloudAPIVersion: # type: ignore if not cura.CuraVersion.CuraCloudAPIVersion: # type: ignore
return self.DEFAULT_CLOUD_API_VERSION return self.DEFAULT_CLOUD_API_VERSION
return cura.CuraVersion.CuraCloudAPIVersion # type: ignore return cura.CuraVersion.CuraCloudAPIVersion # type: ignore
# Get the packages version depending on Cura version settings. # Get the packages version depending on Cura version settings.
def _getSDKVersion(self) -> Union[int, str]: def _getSDKVersion(self) -> Union[int, str]:
@ -231,12 +240,6 @@ class Toolbox(QObject, Extension):
# Make remote requests: # Make remote requests:
self._makeRequestByType("packages") self._makeRequestByType("packages")
self._makeRequestByType("authors") self._makeRequestByType("authors")
# TODO: Uncomment in the future when the tag-filtered api calls work in the cloud server
# self._makeRequestByType("plugins_showcase")
# self._makeRequestByType("plugins_available")
# self._makeRequestByType("materials_showcase")
# self._makeRequestByType("materials_available")
# self._makeRequestByType("materials_generic")
# Gather installed packages: # Gather installed packages:
self._updateInstalledModels() self._updateInstalledModels()
@ -281,7 +284,7 @@ class Toolbox(QObject, Extension):
"description": plugin_data["plugin"]["description"] "description": plugin_data["plugin"]["description"]
} }
return formatted return formatted
except: except KeyError:
Logger.log("w", "Unable to convert plugin meta data %s", str(plugin_data)) Logger.log("w", "Unable to convert plugin meta data %s", str(plugin_data))
return None return None
@ -319,13 +322,10 @@ class Toolbox(QObject, Extension):
if plugin_id not in all_plugin_package_ids) if plugin_id not in all_plugin_package_ids)
self._old_plugin_metadata = {k: v for k, v in self._old_plugin_metadata.items() if k in self._old_plugin_ids} self._old_plugin_metadata = {k: v for k, v in self._old_plugin_metadata.items() if k in self._old_plugin_ids}
self._metadata["plugins_installed"] = all_packages["plugin"] + list(self._old_plugin_metadata.values()) self._plugins_installed_model.setMetadata(all_packages["plugin"] + list(self._old_plugin_metadata.values()))
self._models["plugins_installed"].setMetadata(self._metadata["plugins_installed"])
self.metadataChanged.emit() self.metadataChanged.emit()
if "material" in all_packages: if "material" in all_packages:
self._metadata["materials_installed"] = all_packages["material"] self._materials_installed_model.setMetadata(all_packages["material"])
# TODO: ADD MATERIALS HERE ONCE MATERIALS PORTION OF TOOLBOX IS LIVE
self._models["materials_installed"].setMetadata(self._metadata["materials_installed"])
self.metadataChanged.emit() self.metadataChanged.emit()
@pyqtSlot(str) @pyqtSlot(str)
@ -479,7 +479,7 @@ class Toolbox(QObject, Extension):
def getRemotePackage(self, package_id: str) -> Optional[Dict]: def getRemotePackage(self, package_id: str) -> Optional[Dict]:
# TODO: make the lookup in a dict, not a loop. canUpdate is called for every item. # TODO: make the lookup in a dict, not a loop. canUpdate is called for every item.
remote_package = None remote_package = None
for package in self._metadata["packages"]: for package in self._server_response_data["packages"]:
if package["package_id"] == package_id: if package["package_id"] == package_id:
remote_package = package remote_package = package
break break
@ -491,11 +491,8 @@ class Toolbox(QObject, Extension):
def canUpdate(self, package_id: str) -> bool: def canUpdate(self, package_id: str) -> bool:
local_package = self._package_manager.getInstalledPackageInfo(package_id) local_package = self._package_manager.getInstalledPackageInfo(package_id)
if local_package is None: if local_package is None:
Logger.log("i", "Could not find package [%s] as installed in the package manager, fall back to check the old plugins",
package_id)
local_package = self.getOldPluginPackageMetadata(package_id) local_package = self.getOldPluginPackageMetadata(package_id)
if local_package is None: if local_package is None:
Logger.log("i", "Could not find package [%s] in the old plugins", package_id)
return False return False
remote_package = self.getRemotePackage(package_id) remote_package = self.getRemotePackage(package_id)
@ -545,8 +542,8 @@ class Toolbox(QObject, Extension):
@pyqtSlot(str, result = int) @pyqtSlot(str, result = int)
def getNumberOfInstalledPackagesByAuthor(self, author_id: str) -> int: def getNumberOfInstalledPackagesByAuthor(self, author_id: str) -> int:
count = 0 count = 0
for package in self._metadata["materials_installed"]: for package in self._materials_installed_model.items:
if package["author"]["author_id"] == author_id: if package["author_id"] == author_id:
count += 1 count += 1
return count return count
@ -554,7 +551,7 @@ class Toolbox(QObject, Extension):
@pyqtSlot(str, result = int) @pyqtSlot(str, result = int)
def getTotalNumberOfMaterialPackagesByAuthor(self, author_id: str) -> int: def getTotalNumberOfMaterialPackagesByAuthor(self, author_id: str) -> int:
count = 0 count = 0
for package in self._metadata["packages"]: for package in self._server_response_data["packages"]:
if package["package_type"] == "material": if package["package_type"] == "material":
if package["author"]["author_id"] == author_id: if package["author"]["author_id"] == author_id:
count += 1 count += 1
@ -568,34 +565,31 @@ class Toolbox(QObject, Extension):
# Check for plugins that were installed with the old plugin browser # Check for plugins that were installed with the old plugin browser
def isOldPlugin(self, plugin_id: str) -> bool: def isOldPlugin(self, plugin_id: str) -> bool:
if plugin_id in self._old_plugin_ids: return plugin_id in self._old_plugin_ids
return True
return False
def getOldPluginPackageMetadata(self, plugin_id: str) -> Optional[Dict[str, Any]]: def getOldPluginPackageMetadata(self, plugin_id: str) -> Optional[Dict[str, Any]]:
return self._old_plugin_metadata.get(plugin_id) return self._old_plugin_metadata.get(plugin_id)
def loadingComplete(self) -> bool: def isLoadingComplete(self) -> bool:
populated = 0 populated = 0
for list in self._metadata.items(): for metadata_list in self._server_response_data.items():
if len(list) > 0: if metadata_list:
populated += 1 populated += 1
if populated == len(self._metadata.items()): return populated == len(self._server_response_data.items())
return True
return False
# Make API Calls # Make API Calls
# -------------------------------------------------------------------------- # --------------------------------------------------------------------------
def _makeRequestByType(self, type: str) -> None: def _makeRequestByType(self, request_type: str) -> None:
Logger.log("i", "Marketplace: Requesting %s metadata from server.", type) Logger.log("i", "Requesting %s metadata from server.", request_type)
request = QNetworkRequest(self._request_urls[type]) request = QNetworkRequest(self._request_urls[request_type])
request.setRawHeader(*self._request_header) for header_name, header_value in self._request_headers:
request.setRawHeader(header_name, header_value)
if self._network_manager: if self._network_manager:
self._network_manager.get(request) self._network_manager.get(request)
@pyqtSlot(str) @pyqtSlot(str)
def startDownload(self, url: str) -> None: def startDownload(self, url: str) -> None:
Logger.log("i", "Marketplace: Attempting to download & install package from %s.", url) Logger.log("i", "Attempting to download & install package from %s.", url)
url = QUrl(url) url = QUrl(url)
self._download_request = QNetworkRequest(url) self._download_request = QNetworkRequest(url)
if hasattr(QNetworkRequest, "FollowRedirectsAttribute"): if hasattr(QNetworkRequest, "FollowRedirectsAttribute"):
@ -604,7 +598,8 @@ class Toolbox(QObject, Extension):
if hasattr(QNetworkRequest, "RedirectPolicyAttribute"): if hasattr(QNetworkRequest, "RedirectPolicyAttribute"):
# Patch for Qt 5.9+ # Patch for Qt 5.9+
cast(QNetworkRequest, self._download_request).setAttribute(QNetworkRequest.RedirectPolicyAttribute, True) cast(QNetworkRequest, self._download_request).setAttribute(QNetworkRequest.RedirectPolicyAttribute, True)
cast(QNetworkRequest, self._download_request).setRawHeader(*self._request_header) for header_name, header_value in self._request_headers:
cast(QNetworkRequest, self._download_request).setRawHeader(header_name, header_value)
self._download_reply = cast(QNetworkAccessManager, self._network_manager).get(self._download_request) self._download_reply = cast(QNetworkAccessManager, self._network_manager).get(self._download_request)
self.setDownloadProgress(0) self.setDownloadProgress(0)
self.setIsDownloading(True) self.setIsDownloading(True)
@ -612,15 +607,15 @@ class Toolbox(QObject, Extension):
@pyqtSlot() @pyqtSlot()
def cancelDownload(self) -> None: def cancelDownload(self) -> None:
Logger.log("i", "Marketplace: User cancelled the download of a package.") Logger.log("i", "User cancelled the download of a package.")
self.resetDownload() self.resetDownload()
def resetDownload(self) -> None: def resetDownload(self) -> None:
if self._download_reply: if self._download_reply:
try: try:
self._download_reply.downloadProgress.disconnect(self._onDownloadProgress) self._download_reply.downloadProgress.disconnect(self._onDownloadProgress)
except TypeError: #Raised when the method is not connected to the signal yet. except TypeError: # Raised when the method is not connected to the signal yet.
pass #Don't need to disconnect. pass # Don't need to disconnect.
self._download_reply.abort() self._download_reply.abort()
self._download_reply = None self._download_reply = None
self._download_request = None self._download_request = None
@ -646,22 +641,8 @@ class Toolbox(QObject, Extension):
self.resetDownload() self.resetDownload()
return return
# HACK: These request are not handled independently at this moment, but together from the "packages" call
do_not_handle = [
"materials_available",
"materials_showcase",
"materials_generic",
"plugins_available",
"plugins_showcase",
]
if reply.operation() == QNetworkAccessManager.GetOperation: if reply.operation() == QNetworkAccessManager.GetOperation:
for type, url in self._request_urls.items(): for response_type, url in self._request_urls.items():
# HACK: Do nothing because we'll handle these from the "packages" call
if type in do_not_handle:
continue
if reply.url() == url: if reply.url() == url:
if reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) == 200: if reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) == 200:
try: try:
@ -674,39 +655,33 @@ class Toolbox(QObject, Extension):
return return
# Create model and apply metadata: # Create model and apply metadata:
if not self._models[type]: if not self._models[response_type]:
Logger.log("e", "Could not find the %s model.", type) Logger.log("e", "Could not find the %s model.", response_type)
break break
self._metadata[type] = json_data["data"] self._server_response_data[response_type] = json_data["data"]
self._models[type].setMetadata(self._metadata[type]) self._models[response_type].setMetadata(self._server_response_data[response_type])
# Do some auto filtering if response_type is "packages":
# TODO: Make multiple API calls in the future to handle this self._models[response_type].setFilter({"type": "plugin"})
if type is "packages": self.reBuildMaterialsModels()
self._models[type].setFilter({"type": "plugin"}) self.reBuildPluginsModels()
self.buildMaterialsModels() elif response_type is "authors":
self.buildPluginsModels() self._models[response_type].setFilter({"package_types": "material"})
if type is "authors": self._models[response_type].setFilter({"tags": "generic"})
self._models[type].setFilter({"package_types": "material"})
if type is "materials_generic":
self._models[type].setFilter({"tags": "generic"})
self.metadataChanged.emit() self.metadataChanged.emit()
if self.loadingComplete() is True: if self.isLoadingComplete():
self.setViewPage("overview") self.setViewPage("overview")
return
except json.decoder.JSONDecodeError: except json.decoder.JSONDecodeError:
Logger.log("w", "Marketplace: Received invalid JSON for %s.", type) Logger.log("w", "Received invalid JSON for %s.", response_type)
break break
else: else:
self.setViewPage("errored") self.setViewPage("errored")
self.resetDownload() self.resetDownload()
return elif reply.operation() == QNetworkAccessManager.PutOperation:
else:
# Ignore any operation that is not a get operation # Ignore any operation that is not a get operation
pass pass
@ -716,7 +691,13 @@ class Toolbox(QObject, Extension):
self.setDownloadProgress(new_progress) self.setDownloadProgress(new_progress)
if bytes_sent == bytes_total: if bytes_sent == bytes_total:
self.setIsDownloading(False) self.setIsDownloading(False)
cast(QNetworkReply, self._download_reply).downloadProgress.disconnect(self._onDownloadProgress) self._download_reply = cast(QNetworkReply, self._download_reply)
self._download_reply.downloadProgress.disconnect(self._onDownloadProgress)
# Check if the download was sucessfull
if self._download_reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) != 200:
Logger.log("w", "Failed to download package. The following error was returned: %s", json.loads(bytes(self._download_reply.readAll()).decode("utf-8")))
return
# Must not delete the temporary file on Windows # Must not delete the temporary file on Windows
self._temp_plugin_file = tempfile.NamedTemporaryFile(mode = "w+b", suffix = ".curapackage", delete = False) self._temp_plugin_file = tempfile.NamedTemporaryFile(mode = "w+b", suffix = ".curapackage", delete = False)
file_path = self._temp_plugin_file.name file_path = self._temp_plugin_file.name
@ -726,10 +707,10 @@ class Toolbox(QObject, Extension):
self._onDownloadComplete(file_path) self._onDownloadComplete(file_path)
def _onDownloadComplete(self, file_path: str) -> None: def _onDownloadComplete(self, file_path: str) -> None:
Logger.log("i", "Marketplace: Download complete.") Logger.log("i", "Download complete.")
package_info = self._package_manager.getPackageInfo(file_path) package_info = self._package_manager.getPackageInfo(file_path)
if not package_info: if not package_info:
Logger.log("w", "Marketplace: Package file [%s] was not a valid CuraPackage.", file_path) Logger.log("w", "Package file [%s] was not a valid CuraPackage.", file_path)
return return
license_content = self._package_manager.getPackageLicense(file_path) license_content = self._package_manager.getPackageLicense(file_path)
@ -738,7 +719,6 @@ class Toolbox(QObject, Extension):
return return
self.install(file_path) self.install(file_path)
return
# Getter & Setters for Properties: # Getter & Setters for Properties:
# -------------------------------------------------------------------------- # --------------------------------------------------------------------------
@ -761,8 +741,9 @@ class Toolbox(QObject, Extension):
return self._is_downloading return self._is_downloading
def setActivePackage(self, package: Dict[str, Any]) -> None: def setActivePackage(self, package: Dict[str, Any]) -> None:
self._active_package = package if self._active_package != package:
self.activePackageChanged.emit() self._active_package = package
self.activePackageChanged.emit()
## The active package is the package that is currently being downloaded ## The active package is the package that is currently being downloaded
@pyqtProperty(QObject, fset = setActivePackage, notify = activePackageChanged) @pyqtProperty(QObject, fset = setActivePackage, notify = activePackageChanged)
@ -770,16 +751,18 @@ class Toolbox(QObject, Extension):
return self._active_package return self._active_package
def setViewCategory(self, category: str = "plugin") -> None: def setViewCategory(self, category: str = "plugin") -> None:
self._view_category = category if self._view_category != category:
self.viewChanged.emit() self._view_category = category
self.viewChanged.emit()
@pyqtProperty(str, fset = setViewCategory, notify = viewChanged) @pyqtProperty(str, fset = setViewCategory, notify = viewChanged)
def viewCategory(self) -> str: def viewCategory(self) -> str:
return self._view_category return self._view_category
def setViewPage(self, page: str = "overview") -> None: def setViewPage(self, page: str = "overview") -> None:
self._view_page = page if self._view_page != page:
self.viewChanged.emit() self._view_page = page
self.viewChanged.emit()
@pyqtProperty(str, fset = setViewPage, notify = viewChanged) @pyqtProperty(str, fset = setViewPage, notify = viewChanged)
def viewPage(self) -> str: def viewPage(self) -> str:
@ -787,48 +770,48 @@ class Toolbox(QObject, Extension):
# Exposed Models: # Exposed Models:
# -------------------------------------------------------------------------- # --------------------------------------------------------------------------
@pyqtProperty(QObject, notify = metadataChanged) @pyqtProperty(QObject, constant=True)
def authorsModel(self) -> AuthorsModel: def authorsModel(self) -> AuthorsModel:
return cast(AuthorsModel, self._models["authors"]) return cast(AuthorsModel, self._models["authors"])
@pyqtProperty(QObject, notify = metadataChanged) @pyqtProperty(QObject, constant=True)
def packagesModel(self) -> PackagesModel: def packagesModel(self) -> PackagesModel:
return cast(PackagesModel, self._models["packages"]) return cast(PackagesModel, self._models["packages"])
@pyqtProperty(QObject, notify = metadataChanged) @pyqtProperty(QObject, constant=True)
def pluginsShowcaseModel(self) -> PackagesModel: def pluginsShowcaseModel(self) -> PackagesModel:
return cast(PackagesModel, self._models["plugins_showcase"]) return self._plugins_showcase_model
@pyqtProperty(QObject, notify = metadataChanged) @pyqtProperty(QObject, constant=True)
def pluginsAvailableModel(self) -> PackagesModel: def pluginsAvailableModel(self) -> PackagesModel:
return cast(PackagesModel, self._models["plugins_available"]) return self._plugins_available_model
@pyqtProperty(QObject, notify = metadataChanged) @pyqtProperty(QObject, constant=True)
def pluginsInstalledModel(self) -> PackagesModel: def pluginsInstalledModel(self) -> PackagesModel:
return cast(PackagesModel, self._models["plugins_installed"]) return self._plugins_installed_model
@pyqtProperty(QObject, notify = metadataChanged) @pyqtProperty(QObject, constant=True)
def materialsShowcaseModel(self) -> AuthorsModel: def materialsShowcaseModel(self) -> AuthorsModel:
return cast(AuthorsModel, self._models["materials_showcase"]) return self._materials_showcase_model
@pyqtProperty(QObject, notify = metadataChanged) @pyqtProperty(QObject, constant=True)
def materialsAvailableModel(self) -> AuthorsModel: def materialsAvailableModel(self) -> AuthorsModel:
return cast(AuthorsModel, self._models["materials_available"]) return self._materials_available_model
@pyqtProperty(QObject, notify = metadataChanged) @pyqtProperty(QObject, constant=True)
def materialsInstalledModel(self) -> PackagesModel: def materialsInstalledModel(self) -> PackagesModel:
return cast(PackagesModel, self._models["materials_installed"]) return self._materials_installed_model
@pyqtProperty(QObject, notify=metadataChanged) @pyqtProperty(QObject, constant=True)
def materialsGenericModel(self) -> PackagesModel: def materialsGenericModel(self) -> PackagesModel:
return cast(PackagesModel, self._models["materials_generic"]) return self._materials_generic_model
# Filter Models: # Filter Models:
# -------------------------------------------------------------------------- # --------------------------------------------------------------------------
@pyqtSlot(str, str, str) @pyqtSlot(str, str, str)
def filterModelByProp(self, model_type: str, filter_type: str, parameter: str) -> None: def filterModelByProp(self, model_type: str, filter_type: str, parameter: str) -> None:
if not self._models[model_type]: if not self._models[model_type]:
Logger.log("w", "Marketplace: Couldn't filter %s model because it doesn't exist.", model_type) Logger.log("w", "Couldn't filter %s model because it doesn't exist.", model_type)
return return
self._models[model_type].setFilter({filter_type: parameter}) self._models[model_type].setFilter({filter_type: parameter})
self.filterChanged.emit() self.filterChanged.emit()
@ -836,7 +819,7 @@ class Toolbox(QObject, Extension):
@pyqtSlot(str, "QVariantMap") @pyqtSlot(str, "QVariantMap")
def setFilters(self, model_type: str, filter_dict: dict) -> None: def setFilters(self, model_type: str, filter_dict: dict) -> None:
if not self._models[model_type]: if not self._models[model_type]:
Logger.log("w", "Marketplace: Couldn't filter %s model because it doesn't exist.", model_type) Logger.log("w", "Couldn't filter %s model because it doesn't exist.", model_type)
return return
self._models[model_type].setFilter(filter_dict) self._models[model_type].setFilter(filter_dict)
self.filterChanged.emit() self.filterChanged.emit()
@ -844,21 +827,21 @@ class Toolbox(QObject, Extension):
@pyqtSlot(str) @pyqtSlot(str)
def removeFilters(self, model_type: str) -> None: def removeFilters(self, model_type: str) -> None:
if not self._models[model_type]: if not self._models[model_type]:
Logger.log("w", "Marketplace: Couldn't remove filters on %s model because it doesn't exist.", model_type) Logger.log("w", "Couldn't remove filters on %s model because it doesn't exist.", model_type)
return return
self._models[model_type].setFilter({}) self._models[model_type].setFilter({})
self.filterChanged.emit() self.filterChanged.emit()
# HACK(S): # HACK(S):
# -------------------------------------------------------------------------- # --------------------------------------------------------------------------
def buildMaterialsModels(self) -> None: def reBuildMaterialsModels(self) -> None:
self._metadata["materials_showcase"] = [] materials_showcase_metadata = []
self._metadata["materials_available"] = [] materials_available_metadata = []
self._metadata["materials_generic"] = [] materials_generic_metadata = []
processed_authors = [] # type: List[str] processed_authors = [] # type: List[str]
for item in self._metadata["packages"]: for item in self._server_response_data["packages"]:
if item["package_type"] == "material": if item["package_type"] == "material":
author = item["author"] author = item["author"]
@ -867,30 +850,29 @@ class Toolbox(QObject, Extension):
# Generic materials to be in the same section # Generic materials to be in the same section
if "generic" in item["tags"]: if "generic" in item["tags"]:
self._metadata["materials_generic"].append(item) materials_generic_metadata.append(item)
else: else:
if "showcase" in item["tags"]: if "showcase" in item["tags"]:
self._metadata["materials_showcase"].append(author) materials_showcase_metadata.append(author)
else: else:
self._metadata["materials_available"].append(author) materials_available_metadata.append(author)
processed_authors.append(author["author_id"]) processed_authors.append(author["author_id"])
self._models["materials_showcase"].setMetadata(self._metadata["materials_showcase"]) self._materials_showcase_model.setMetadata(materials_showcase_metadata)
self._models["materials_available"].setMetadata(self._metadata["materials_available"]) self._materials_available_model.setMetadata(materials_available_metadata)
self._models["materials_generic"].setMetadata(self._metadata["materials_generic"]) self._materials_generic_model.setMetadata(materials_generic_metadata)
def buildPluginsModels(self) -> None: def reBuildPluginsModels(self) -> None:
self._metadata["plugins_showcase"] = [] plugins_showcase_metadata = []
self._metadata["plugins_available"] = [] plugins_available_metadata = []
for item in self._metadata["packages"]: for item in self._server_response_data["packages"]:
if item["package_type"] == "plugin": if item["package_type"] == "plugin":
if "showcase" in item["tags"]: if "showcase" in item["tags"]:
self._metadata["plugins_showcase"].append(item) plugins_showcase_metadata.append(item)
else: else:
self._metadata["plugins_available"].append(item) plugins_available_metadata.append(item)
self._models["plugins_showcase"].setMetadata(self._metadata["plugins_showcase"]) self._plugins_showcase_model.setMetadata(plugins_showcase_metadata)
self._models["plugins_available"].setMetadata(self._metadata["plugins_available"]) self._plugins_available_model.setMetadata(plugins_available_metadata)

View file

@ -1,8 +1,8 @@
{ {
"name": "UFP Writer", "name": "UFP Writer",
"author": "Ultimaker B.V.", "author": "Ultimaker B.V.",
"version": "1.0.0", "version": "1.0.1",
"description": "Provides support for writing Ultimaker Format Packages.", "description": "Provides support for writing Ultimaker Format Packages.",
"api": 5, "api": "6.0",
"i18n-catalog": "cura" "i18n-catalog": "cura"
} }

View file

@ -2,7 +2,7 @@
"name": "UM3 Network Connection", "name": "UM3 Network Connection",
"author": "Ultimaker B.V.", "author": "Ultimaker B.V.",
"description": "Manages network connections to Ultimaker 3 printers.", "description": "Manages network connections to Ultimaker 3 printers.",
"version": "1.0.0", "version": "1.0.1",
"api": 5, "api": "6.0",
"i18n-catalog": "cura" "i18n-catalog": "cura"
} }

View file

@ -8,11 +8,13 @@ import UM 1.3 as UM
import Cura 1.0 as Cura import Cura 1.0 as Cura
Rectangle { Rectangle {
id: base
property var iconSource: null; property var iconSource: null;
color: "#0a0850" // TODO: Theme! color: "#0a0850" // TODO: Theme!
height: width; height: width;
radius: Math.round(0.5 * width); radius: Math.round(0.5 * width);
width: 24 * screenScaleFactor; width: 24 * screenScaleFactor;
property var enabled: true
UM.RecolorImage { UM.RecolorImage {
id: icon; id: icon;
@ -29,12 +31,18 @@ Rectangle {
MouseArea { MouseArea {
id: clickArea; id: clickArea;
anchors.fill: parent; anchors.fill: parent;
hoverEnabled: true; hoverEnabled: base.enabled
onClicked: { onClicked: {
if (OutputDevice.activeCameraUrl != "") { if (base.enabled)
OutputDevice.setActiveCameraUrl(""); {
} else { if (OutputDevice.activeCameraUrl != "")
OutputDevice.setActiveCameraUrl(modelData.cameraUrl); {
OutputDevice.setActiveCameraUrl("")
}
else
{
OutputDevice.setActiveCameraUrl(modelData.cameraUrl)
}
} }
} }
} }

View file

@ -1,110 +0,0 @@
// Copyright (c) 2018 Ultimaker B.V.
// Cura is released under the terms of the LGPLv3 or higher.
import QtQuick 2.3
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.3
import UM 1.3 as UM
import Cura 1.0 as Cura
Component {
Rectangle {
id: base;
property var shadowRadius: UM.Theme.getSize("monitor_shadow_radius").width;
property var cornerRadius: UM.Theme.getSize("monitor_corner_radius").width;
anchors.fill: parent;
color: UM.Theme.getColor("main_background");
visible: OutputDevice != null;
UM.I18nCatalog {
id: catalog;
name: "cura";
}
Label {
id: printingLabel;
anchors {
left: parent.left;
leftMargin: 4 * UM.Theme.getSize("default_margin").width;
margins: 2 * UM.Theme.getSize("default_margin").width;
right: parent.right;
top: parent.top;
}
color: UM.Theme.getColor("text");
elide: Text.ElideRight;
font: UM.Theme.getFont("large");
text: catalog.i18nc("@label", "Printing");
}
Label {
id: managePrintersLabel;
anchors {
bottom: printingLabel.bottom;
right: printerScrollView.right;
rightMargin: 4 * UM.Theme.getSize("default_margin").width;
}
color: UM.Theme.getColor("primary"); // "Cura Blue"
font: UM.Theme.getFont("default");
linkColor: UM.Theme.getColor("primary"); // "Cura Blue"
text: catalog.i18nc("@label link to connect manager", "Manage printers");
}
MouseArea {
anchors.fill: managePrintersLabel;
hoverEnabled: true;
onClicked: Cura.MachineManager.printerOutputDevices[0].openPrinterControlPanel();
onEntered: managePrintersLabel.font.underline = true;
onExited: managePrintersLabel.font.underline = false;
}
// Skeleton loading
Column {
id: skeletonLoader;
anchors {
left: parent.left;
leftMargin: UM.Theme.getSize("wide_margin").width;
right: parent.right;
rightMargin: UM.Theme.getSize("wide_margin").width;
top: printingLabel.bottom;
topMargin: UM.Theme.getSize("default_margin").height;
}
spacing: UM.Theme.getSize("default_margin").height - 10;
visible: printerList.count === 0;
PrinterCard {
printer: null;
}
PrinterCard {
printer: null;
}
}
// Actual content
ScrollView {
id: printerScrollView;
anchors {
bottom: parent.bottom;
left: parent.left;
right: parent.right;
top: printingLabel.bottom;
topMargin: UM.Theme.getSize("default_margin").height;
}
style: UM.Theme.styles.scrollview;
ListView {
id: printerList;
property var currentIndex: -1;
anchors {
fill: parent;
leftMargin: UM.Theme.getSize("wide_margin").width;
rightMargin: UM.Theme.getSize("wide_margin").width;
}
delegate: PrinterCard {
printer: modelData;
}
model: OutputDevice.printers;
spacing: UM.Theme.getSize("default_margin").height - 10;
}
}
}
}

View file

@ -1,247 +0,0 @@
// Copyright (c) 2018 Ultimaker B.V.
// Cura is released under the terms of the LGPLv3 or higher.
import QtQuick 2.2
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import UM 1.3 as UM
import Cura 1.0 as Cura
import QtGraphicalEffects 1.0
Component
{
Item
{
id: monitorFrame
property var emphasisColor: UM.Theme.getColor("setting_control_border_highlight")
property var cornerRadius: UM.Theme.getSize("monitor_corner_radius").width
height: maximumHeight
onVisibleChanged:
{
if (monitorFrame != null && !monitorFrame.visible)
{
OutputDevice.setActiveCameraUrl("")
}
}
width: maximumWidth
UM.I18nCatalog
{
id: catalog
name: "cura"
}
LinearGradient {
anchors.fill: parent
gradient: Gradient {
GradientStop {
position: 0.0
color: "#f6f6f6"
}
GradientStop {
position: 1.0
color: "#ffffff"
}
}
}
ScrollView
{
id: printers
anchors
{
left: queue.left
right: queue.right
top: parent.top
topMargin: 48 * screenScaleFactor // TODO: Theme!
}
height: 264 * screenScaleFactor // TODO: Theme!
Row
{
spacing: 60 * screenScaleFactor // TODO: Theme!
Repeater
{
model: OutputDevice.printers
MonitorPrinterCard
{
printer: modelData
}
}
}
}
Item
{
id: queue
width: Math.min(834 * screenScaleFactor, maximumWidth)
anchors {
bottom: parent.bottom
horizontalCenter: parent.horizontalCenter
top: printers.bottom
topMargin: 48 * screenScaleFactor // TODO: Theme!
}
Label
{
id: queuedLabel
anchors
{
left: queuedPrintJobs.left
top: parent.top
}
color: UM.Theme.getColor("text")
font: UM.Theme.getFont("large_nonbold")
text: catalog.i18nc("@label", "Queued")
}
Item
{
id: manageQueueLabel
anchors
{
right: queuedPrintJobs.right
verticalCenter: queuedLabel.verticalCenter
}
height: 18 * screenScaleFactor // TODO: Theme!
width: childrenRect.width
UM.RecolorImage
{
id: externalLinkIcon
anchors.verticalCenter: manageQueueLabel.verticalCenter
color: UM.Theme.getColor("primary")
source: "../svg/icons/external_link.svg"
width: 16 * screenScaleFactor // TODO: Theme! (Y U NO USE 18 LIKE ALL OTHER ICONS?!)
height: 16 * screenScaleFactor // TODO: Theme! (Y U NO USE 18 LIKE ALL OTHER ICONS?!)
}
Label
{
id: manageQueueText
anchors
{
left: externalLinkIcon.right
leftMargin: 6 * screenScaleFactor // TODO: Theme!
verticalCenter: externalLinkIcon.verticalCenter
}
color: UM.Theme.getColor("primary")
font: UM.Theme.getFont("default")
linkColor: UM.Theme.getColor("primary")
text: catalog.i18nc("@label link to connect manager", "Manage queue in Cura Connect")
}
}
MouseArea
{
anchors.fill: manageQueueLabel
hoverEnabled: true
onClicked: Cura.MachineManager.printerOutputDevices[0].openPrintJobControlPanel()
onEntered:
{
manageQueueText.font.underline = true
}
onExited:
{
manageQueueText.font.underline = false
}
}
Row
{
id: printJobQueueHeadings
anchors
{
left: queuedPrintJobs.left
leftMargin: 6 * screenScaleFactor // TODO: Theme!
top: queuedLabel.bottom
topMargin: 24 * screenScaleFactor // TODO: Theme!
}
spacing: 18 * screenScaleFactor // TODO: Theme!
Label
{
text: catalog.i18nc("@label", "Print jobs")
color: "#666666"
elide: Text.ElideRight
font: UM.Theme.getFont("medium") // 14pt, regular
anchors.verticalCenter: parent.verticalCenter
width: 284 * screenScaleFactor // TODO: Theme! (Should match column size)
// FIXED-LINE-HEIGHT:
height: 18 * screenScaleFactor // TODO: Theme!
verticalAlignment: Text.AlignVCenter
}
Label
{
text: catalog.i18nc("@label", "Total print time")
color: "#666666"
elide: Text.ElideRight
font: UM.Theme.getFont("medium") // 14pt, regular
anchors.verticalCenter: parent.verticalCenter
width: 216 * screenScaleFactor // TODO: Theme! (Should match column size)
// FIXED-LINE-HEIGHT:
height: 18 * screenScaleFactor // TODO: Theme!
verticalAlignment: Text.AlignVCenter
}
Label
{
text: catalog.i18nc("@label", "Waiting for")
color: "#666666"
elide: Text.ElideRight
font: UM.Theme.getFont("medium") // 14pt, regular
anchors.verticalCenter: parent.verticalCenter
width: 216 * screenScaleFactor // TODO: Theme! (Should match column size)
// FIXED-LINE-HEIGHT:
height: 18 * screenScaleFactor // TODO: Theme!
verticalAlignment: Text.AlignVCenter
}
}
ScrollView
{
id: queuedPrintJobs
anchors {
bottom: parent.bottom
horizontalCenter: parent.horizontalCenter
top: printJobQueueHeadings.bottom
topMargin: 12 * screenScaleFactor // TODO: Theme!
}
style: UM.Theme.styles.scrollview
visible: OutputDevice.receivedPrintJobs
width: parent.width
ListView
{
id: printJobList
anchors.fill: parent
delegate: MonitorPrintJobCard
{
anchors
{
left: parent.left
right: parent.right
}
printJob: modelData
}
model: OutputDevice.queuedPrintJobs
spacing: 6
}
}
}
PrinterVideoStream {
anchors.fill: parent
cameraUrl: OutputDevice.activeCameraUrl
visible: OutputDevice.activeCameraUrl != ""
}
}
}

View file

@ -57,7 +57,7 @@ Cura.MachineAction
spacing: UM.Theme.getSize("default_margin").height spacing: UM.Theme.getSize("default_margin").height
SystemPalette { id: palette } SystemPalette { id: palette }
UM.I18nCatalog { id: catalog; name: "cura" } UM.I18nCatalog { id: catalog; name:"cura" }
Label Label
{ {
id: pageTitle id: pageTitle

View file

@ -16,9 +16,9 @@ Item
property bool expanded: false property bool expanded: false
property var borderWidth: 1 property var borderWidth: 1
property color borderColor: "#EAEAEC" property color borderColor: "#CCCCCC"
property color headerBackgroundColor: "white" property color headerBackgroundColor: "white"
property color headerHoverColor: "#f5f5f5" property color headerHoverColor: "#e8f2fc"
property color drawerBackgroundColor: "white" property color drawerBackgroundColor: "white"
property alias headerItem: header.children property alias headerItem: header.children
property alias drawerItem: drawer.children property alias drawerItem: drawer.children

View file

@ -1,12 +0,0 @@
// Copyright (c) 2018 Ultimaker B.V.
// Cura is released under the terms of the LGPLv3 or higher.
import QtQuick 2.3
import QtQuick.Controls 2.0
import UM 1.3 as UM
Rectangle {
color: UM.Theme.getColor("monitor_lining_light"); // TODO: Maybe theme separately? Maybe not.
height: UM.Theme.getSize("default_lining").height;
width: parent.width;
}

View file

@ -0,0 +1,251 @@
// Copyright (c) 2018 Ultimaker B.V.
// Cura is released under the terms of the LGPLv3 or higher.
import QtQuick 2.3
import QtQuick.Controls 2.0
import QtGraphicalEffects 1.0
import UM 1.3 as UM
Item
{
id: base
property var currentIndex: 0
property var tileWidth: 834 * screenScaleFactor // TODO: Theme!
property var tileHeight: 216 * screenScaleFactor // TODO: Theme!
property var tileSpacing: 60 * screenScaleFactor // TODO: Theme!
property var maxOffset: (OutputDevice.printers.length - 1) * (tileWidth + tileSpacing)
height: centerSection.height
width: maximumWidth
Item
{
id: leftHint
anchors
{
right: leftButton.left
rightMargin: 12 * screenScaleFactor // TODO: Theme!
left: parent.left
}
height: parent.height
z: 10
LinearGradient
{
anchors.fill: parent
start: Qt.point(0, 0)
end: Qt.point(leftHint.width, 0)
gradient: Gradient
{
GradientStop
{
position: 0.0
color: "#fff6f6f6" // TODO: Theme!
}
GradientStop
{
position: 1.0
color: "#66f6f6f6" // TODO: Theme!
}
}
}
MouseArea
{
anchors.fill: parent
onClicked: navigateTo(currentIndex - 1)
}
}
Button
{
id: leftButton
anchors
{
verticalCenter: parent.verticalCenter
right: centerSection.left
rightMargin: 12 * screenScaleFactor // TODO: Theme!
}
width: 36 * screenScaleFactor // TODO: Theme!
height: 72 * screenScaleFactor // TODO: Theme!
visible: currentIndex > 0
hoverEnabled: true
z: 10
onClicked: navigateTo(currentIndex - 1)
background: Rectangle
{
color: leftButton.hovered ? "#e8f2fc" : "#ffffff" // TODO: Theme!
border.width: 1 * screenScaleFactor // TODO: Theme!
border.color: "#cccccc" // TODO: Theme!
radius: 2 * screenScaleFactor // TODO: Theme!
}
contentItem: Item
{
anchors.fill: parent
UM.RecolorImage
{
anchors.centerIn: parent
width: 18 // TODO: Theme!
height: width // TODO: Theme!
sourceSize.width: width // TODO: Theme!
sourceSize.height: width // TODO: Theme!
color: "#152950" // TODO: Theme!
source: UM.Theme.getIcon("arrow_left")
}
}
}
Item
{
id: centerSection
anchors
{
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
width: tileWidth
height: tiles.height
z: 1
Row
{
id: tiles
height: childrenRect.height
width: 5 * tileWidth + 4 * tileSpacing // TODO: Theme!
x: 0
z: 0
Behavior on x
{
NumberAnimation
{
duration: 200
easing.type: Easing.InOutCubic
}
}
spacing: 60 * screenScaleFactor // TODO: Theme!
Repeater
{
model: OutputDevice.printers
MonitorPrinterCard
{
printer: modelData
enabled: model.index == currentIndex
}
}
}
}
Button
{
id: rightButton
anchors
{
verticalCenter: parent.verticalCenter
left: centerSection.right
leftMargin: 12 * screenScaleFactor // TODO: Theme!
}
width: 36 * screenScaleFactor // TODO: Theme!
height: 72 * screenScaleFactor // TODO: Theme!
z: 10
visible: currentIndex < OutputDevice.printers.length - 1
onClicked: navigateTo(currentIndex + 1)
hoverEnabled: true
background: Rectangle
{
color: rightButton.hovered ? "#e8f2fc" : "#ffffff" // TODO: Theme!
border.width: 1 * screenScaleFactor // TODO: Theme!
border.color: "#cccccc" // TODO: Theme!
radius: 2 * screenScaleFactor // TODO: Theme!
}
contentItem: Item
{
anchors.fill: parent
UM.RecolorImage
{
anchors.centerIn: parent
width: 18 // TODO: Theme!
height: width // TODO: Theme!
sourceSize.width: width // TODO: Theme!
sourceSize.height: width // TODO: Theme!
color: "#152950" // TODO: Theme!
source: UM.Theme.getIcon("arrow_right")
}
}
}
Item
{
id: rightHint
anchors
{
left: rightButton.right
leftMargin: 12 * screenScaleFactor // TODO: Theme!
right: parent.right
}
height: centerSection.height
z: 10
LinearGradient
{
anchors.fill: parent
start: Qt.point(0, 0)
end: Qt.point(rightHint.width, 0)
gradient: Gradient
{
GradientStop
{
position: 0.0
color: "#66f6f6f6" // TODO: Theme!
}
GradientStop
{
position: 1.0
color: "#fff6f6f6" // TODO: Theme!
}
}
}
MouseArea
{
anchors.fill: parent
onClicked: navigateTo(currentIndex + 1)
}
}
Item
{
id: navigationDots
anchors
{
horizontalCenter: centerSection.horizontalCenter
top: centerSection.bottom
topMargin: 36 * screenScaleFactor // TODO: Theme!
}
Row
{
spacing: 8 * screenScaleFactor // TODO: Theme!
Repeater
{
model: OutputDevice.printers
Button
{
background: Rectangle
{
color: model.index == currentIndex ? "#777777" : "#d8d8d8" // TODO: Theme!
radius: Math.floor(width / 2)
width: 12 * screenScaleFactor // TODO: Theme!
height: width // TODO: Theme!
}
onClicked: navigateTo(model.index)
}
}
}
}
function navigateTo( i ) {
if (i >= 0 && i < OutputDevice.printers.length)
{
tiles.x = -1 * i * (tileWidth + tileSpacing)
currentIndex = i
}
}
}

View file

@ -0,0 +1,142 @@
// Copyright (c) 2018 Ultimaker B.V.
// Cura is released under the terms of the LGPLv3 or higher.
import QtQuick 2.3
import QtQuick.Controls 1.4
import QtQuick.Layouts 1.3
import QtQuick.Dialogs 1.2
import UM 1.3 as UM
UM.Dialog
{
id: overrideConfirmationDialog
property var printer: null
minimumWidth: screenScaleFactor * 640;
minimumHeight: screenScaleFactor * 320;
width: minimumWidth
height: minimumHeight
title: catalog.i18nc("@title:window", "Configuration Changes")
rightButtons:
[
Button
{
id: overrideButton
anchors.margins: UM.Theme.getSize("default_margin").width
text: catalog.i18nc("@action:button", "Override")
onClicked:
{
OutputDevice.forceSendJob(printer.activePrintJob.key)
overrideConfirmationDialog.close()
}
},
Button
{
id: cancelButton
anchors.margins: UM.Theme.getSize("default_margin").width
text: catalog.i18nc("@action:button", "Cancel")
onClicked:
{
overrideConfirmationDialog.reject()
}
}
]
Label
{
anchors
{
fill: parent
margins: 36 * screenScaleFactor // TODO: Theme!
bottomMargin: 56 * screenScaleFactor // TODO: Theme!
}
wrapMode: Text.WordWrap
text:
{
if (!printer.activePrintJob)
{
return ""
}
var topLine
if (materialsAreKnown(printer.activePrintJob))
{
topLine = catalog.i18ncp("@label", "The assigned printer, %1, requires the following configuration change:", "The assigned printer, %1, requires the following configuration changes:", printer.activePrintJob.configurationChanges.length).arg(printer.name)
}
else
{
topLine = catalog.i18nc("@label", "The printer %1 is assigned, but the job contains an unknown material configuration.").arg(printer.name)
}
var result = "<p>" + topLine +"</p>\n\n"
for (var i = 0; i < printer.activePrintJob.configurationChanges.length; i++)
{
var change = printer.activePrintJob.configurationChanges[i]
var text
switch (change.typeOfChange)
{
case "material_change":
text = catalog.i18nc("@label", "Change material %1 from %2 to %3.").arg(change.index + 1).arg(change.originName).arg(change.targetName)
break
case "material_insert":
text = catalog.i18nc("@label", "Load %3 as material %1 (This cannot be overridden).").arg(change.index + 1).arg(change.targetName)
break
case "print_core_change":
text = catalog.i18nc("@label", "Change print core %1 from %2 to %3.").arg(change.index + 1).arg(change.originName).arg(change.targetName)
break
case "buildplate_change":
text = catalog.i18nc("@label", "Change build plate to %1 (This cannot be overridden).").arg(formatBuildPlateType(change.target_name))
break
default:
text = "unknown"
}
result += "<p><b>" + text + "</b></p>\n\n"
}
var bottomLine = catalog.i18nc("@label", "Override will use the specified settings with the existing printer configuration. This may result in a failed print.")
result += "<p>" + bottomLine + "</p>\n\n"
return result
}
}
// Utils
function formatPrintJobName(name)
{
var extensions = [ ".gcode.gz", ".gz", ".gcode", ".ufp" ]
for (var i = 0; i < extensions.length; i++)
{
var extension = extensions[i]
if (name.slice(-extension.length) === extension)
{
name = name.substring(0, name.length - extension.length)
}
}
return name;
}
function materialsAreKnown(job)
{
var conf0 = job.configuration[0]
if (conf0 && !conf0.material.material)
{
return false
}
var conf1 = job.configuration[1]
if (conf1 && !conf1.material.material)
{
return false
}
return true
}
function formatBuildPlateType(buildPlateType)
{
var translationText = ""
switch (buildPlateType) {
case "glass":
translationText = catalog.i18nc("@label", "Glass")
break
case "aluminum":
translationText = catalog.i18nc("@label", "Aluminum")
break
default:
translationText = null
}
return translationText
}
}

View file

@ -26,6 +26,7 @@ Item
ExpandableCard ExpandableCard
{ {
borderColor: printJob.configurationChanges.length !== 0 ? "#f5a623" : "#CCCCCC" // TODO: Theme!
headerItem: Row headerItem: Row
{ {
height: 48 * screenScaleFactor // TODO: Theme! height: 48 * screenScaleFactor // TODO: Theme!
@ -96,6 +97,7 @@ Item
return "" return ""
} }
visible: printJob visible: printJob
width: 120 * screenScaleFactor // TODO: Theme!
// FIXED-LINE-HEIGHT: // FIXED-LINE-HEIGHT:
height: 18 * screenScaleFactor // TODO: Theme! height: 18 * screenScaleFactor // TODO: Theme!

View file

@ -23,7 +23,7 @@ Item
anchors.fill: parent anchors.fill: parent
opacity: opacity:
{ {
if (printJob && (printJob.state == "error" || !printJob.isActive)) if (printJob && (printJob.state == "error" || printJob.configurationChanges.length > 0 || !printJob.isActive))
{ {
return 0.5 return 0.5
} }
@ -60,6 +60,14 @@ Item
height: 0.5 * printJobPreview.height height: 0.5 * printJobPreview.height
source: source:
{ {
if (!printJob)
{
return ""
}
if (printJob.configurationChanges.length > 0)
{
return "../svg/warning-icon.svg"
}
switch(printJob.state) switch(printJob.state)
{ {
case "error": case "error":
@ -75,6 +83,7 @@ Item
default: default:
return "" return ""
} }
return ""
} }
sourceSize sourceSize
{ {

View file

@ -55,7 +55,7 @@ Item
left: progressBar.right left: progressBar.right
leftMargin: 18 * screenScaleFactor // TODO: Theme! leftMargin: 18 * screenScaleFactor // TODO: Theme!
} }
text: Math.round(printJob.progress * 100) + "%" text: printJob ? Math.round(printJob.progress * 100) + "%" : "0%"
color: printJob && printJob.isActive ? "#374355" : "#babac1" // TODO: Theme! color: printJob && printJob.isActive ? "#374355" : "#babac1" // TODO: Theme!
width: contentWidth width: contentWidth
font: UM.Theme.getFont("medium") // 14pt, regular font: UM.Theme.getFont("medium") // 14pt, regular
@ -88,6 +88,8 @@ Item
return catalog.i18nc("@label:status", "Aborted") return catalog.i18nc("@label:status", "Aborted")
} }
return catalog.i18nc("@label:status", "Finished") return catalog.i18nc("@label:status", "Finished")
case "finished":
return catalog.i18nc("@label:status", "Finished")
case "sent_to_printer": case "sent_to_printer":
return catalog.i18nc("@label:status", "Preparing...") return catalog.i18nc("@label:status", "Preparing...")
case "pre_print": case "pre_print":

View file

@ -3,6 +3,7 @@
import QtQuick 2.3 import QtQuick 2.3
import QtQuick.Controls 2.0 import QtQuick.Controls 2.0
import QtQuick.Dialogs 1.1
import UM 1.3 as UM import UM 1.3 as UM
/** /**
@ -24,8 +25,13 @@ Item
property var borderSize: 1 * screenScaleFactor // TODO: Theme, and remove from here property var borderSize: 1 * screenScaleFactor // TODO: Theme, and remove from here
// If the printer card's controls are enabled. This is used by the carousel
// to prevent opening the context menu or camera while the printer card is not
// "in focus"
property var enabled: true
width: 834 * screenScaleFactor // TODO: Theme! width: 834 * screenScaleFactor // TODO: Theme!
height: 216 * screenScaleFactor // TODO: Theme! height: childrenRect.height
// Printer portion // Printer portion
Rectangle Rectangle
@ -33,7 +39,7 @@ Item
id: printerInfo id: printerInfo
border border
{ {
color: "#EAEAEC" // TODO: Theme! color: "#CCCCCC" // TODO: Theme!
width: borderSize // TODO: Remove once themed width: borderSize // TODO: Remove once themed
} }
color: "white" // TODO: Theme! color: "white" // TODO: Theme!
@ -66,7 +72,7 @@ Item
{ {
verticalCenter: parent.verticalCenter verticalCenter: parent.verticalCenter
} }
width: 216 * screenScaleFactor // TODO: Theme! width: 180 * screenScaleFactor // TODO: Theme!
height: printerNameLabel.height + printerFamilyPill.height + 6 * screenScaleFactor // TODO: Theme! height: printerNameLabel.height + printerFamilyPill.height + 6 * screenScaleFactor // TODO: Theme!
Label Label
@ -123,6 +129,7 @@ Item
printJob: printer.activePrintJob printJob: printer.activePrintJob
width: 36 * screenScaleFactor // TODO: Theme! width: 36 * screenScaleFactor // TODO: Theme!
height: 36 * screenScaleFactor // TODO: Theme! height: 36 * screenScaleFactor // TODO: Theme!
enabled: base.enabled
} }
CameraButton CameraButton
{ {
@ -135,6 +142,7 @@ Item
bottomMargin: 20 * screenScaleFactor // TODO: Theme! bottomMargin: 20 * screenScaleFactor // TODO: Theme!
} }
iconSource: "../svg/icons/camera.svg" iconSource: "../svg/icons/camera.svg"
enabled: base.enabled
} }
} }
@ -150,7 +158,7 @@ Item
} }
border border
{ {
color: "#EAEAEC" // TODO: Theme! color: printer.activePrintJob && printer.activePrintJob.configurationChanges.length > 0 ? "#f5a623" : "#CCCCCC" // TODO: Theme!
width: borderSize // TODO: Remove once themed width: borderSize // TODO: Remove once themed
} }
color: "white" // TODO: Theme! color: "white" // TODO: Theme!
@ -185,14 +193,15 @@ Item
} }
if (printer && printer.state == "unreachable") if (printer && printer.state == "unreachable")
{ {
return catalog.i18nc("@label:status", "Unreachable") return catalog.i18nc("@label:status", "Unavailable")
} }
if (printer && printer.state == "idle") if (printer && !printer.activePrintJob && printer.state == "idle")
{ {
return catalog.i18nc("@label:status", "Idle") return catalog.i18nc("@label:status", "Idle")
} }
return "" return ""
} }
visible: text !== ""
} }
Item Item
@ -218,7 +227,7 @@ Item
{ {
verticalCenter: parent.verticalCenter verticalCenter: parent.verticalCenter
} }
width: 216 * screenScaleFactor // TODO: Theme! width: 180 * screenScaleFactor // TODO: Theme!
height: printerNameLabel.height + printerFamilyPill.height + 6 * screenScaleFactor // TODO: Theme! height: printerNameLabel.height + printerFamilyPill.height + 6 * screenScaleFactor // TODO: Theme!
visible: printer.activePrintJob visible: printer.activePrintJob
@ -247,7 +256,7 @@ Item
} }
color: printer.activePrintJob && printer.activePrintJob.isActive ? "#53657d" : "#babac1" // TODO: Theme! color: printer.activePrintJob && printer.activePrintJob.isActive ? "#53657d" : "#babac1" // TODO: Theme!
elide: Text.ElideRight elide: Text.ElideRight
font: UM.Theme.getFont("very_small") // 12pt, regular font: UM.Theme.getFont("default") // 12pt, regular
text: printer.activePrintJob ? printer.activePrintJob.owner : "Anonymous" // TODO: I18N text: printer.activePrintJob ? printer.activePrintJob.owner : "Anonymous" // TODO: I18N
width: parent.width width: parent.width
@ -264,8 +273,67 @@ Item
verticalCenter: parent.verticalCenter verticalCenter: parent.verticalCenter
} }
printJob: printer.activePrintJob printJob: printer.activePrintJob
visible: printer.activePrintJob visible: printer.activePrintJob && printer.activePrintJob.configurationChanges.length === 0
}
Label
{
anchors
{
verticalCenter: parent.verticalCenter
}
font: UM.Theme.getFont("default")
text: "Requires configuration changes"
visible: printer.activePrintJob && printer.activePrintJob.configurationChanges.length > 0
// FIXED-LINE-HEIGHT:
height: 18 * screenScaleFactor // TODO: Theme!
verticalAlignment: Text.AlignVCenter
} }
} }
Button
{
id: detailsButton
anchors
{
verticalCenter: parent.verticalCenter
right: parent.right
rightMargin: 18 * screenScaleFactor // TODO: Theme!
}
background: Rectangle
{
color: "#d8d8d8" // TODO: Theme!
radius: 2 * screenScaleFactor // Todo: Theme!
Rectangle
{
anchors.fill: parent
anchors.bottomMargin: 2 * screenScaleFactor // TODO: Theme!
color: detailsButton.hovered ? "#e4e4e4" : "#f0f0f0" // TODO: Theme!
radius: 2 * screenScaleFactor // Todo: Theme!
}
}
contentItem: Label
{
anchors.fill: parent
anchors.bottomMargin: 2 * screenScaleFactor // TODO: Theme!
color: "#1e66d7" // TODO: Theme!
font: UM.Theme.getFont("medium") // 14pt, regular
text: "Details" // TODO: I18NC!
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
height: 18 * screenScaleFactor // TODO: Theme!
}
implicitHeight: 32 * screenScaleFactor // TODO: Theme!
implicitWidth: 96 * screenScaleFactor // TODO: Theme!
visible: printer.activePrintJob && printer.activePrintJob.configurationChanges.length > 0
onClicked: base.enabled ? overrideConfirmationDialog.open() : {}
}
}
MonitorConfigOverrideDialog
{
id: overrideConfirmationDialog
printer: base.printer
} }
} }

View file

@ -12,7 +12,19 @@ import UM 1.2 as UM
Item Item
{ {
// The printer name // The printer name
property alias text: printerNameLabel.text; property var text: ""
property var tagText: {
switch(text) {
case "Ultimaker 3":
return "UM 3"
case "Ultimaker 3 Extended":
return "UM 3 EXT"
case "Ultimaker S5":
return "UM S5"
default:
return text
}
}
implicitHeight: 18 * screenScaleFactor // TODO: Theme! implicitHeight: 18 * screenScaleFactor // TODO: Theme!
implicitWidth: printerNameLabel.contentWidth + 12 // TODO: Theme! implicitWidth: printerNameLabel.contentWidth + 12 // TODO: Theme!
@ -28,7 +40,7 @@ Item
id: printerNameLabel id: printerNameLabel
anchors.centerIn: parent anchors.centerIn: parent
color: "#535369" // TODO: Theme! color: "#535369" // TODO: Theme!
text: "" text: tagText
font.pointSize: 10 font.pointSize: 10
} }
} }

View file

@ -0,0 +1,167 @@
// Copyright (c) 2018 Ultimaker B.V.
// Cura is released under the terms of the LGPLv3 or higher.
import QtQuick 2.2
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import UM 1.3 as UM
import Cura 1.0 as Cura
/**
* This component contains the print job queue, extracted from the primary
* MonitorStage.qml file not for reusability but simply to keep it lean and more
* readable.
*/
Item
{
Label
{
id: queuedLabel
anchors
{
left: queuedPrintJobs.left
top: parent.top
}
color: UM.Theme.getColor("text")
font: UM.Theme.getFont("large_nonbold")
text: catalog.i18nc("@label", "Queued")
}
Item
{
id: manageQueueLabel
anchors
{
right: queuedPrintJobs.right
verticalCenter: queuedLabel.verticalCenter
}
height: 18 * screenScaleFactor // TODO: Theme!
width: childrenRect.width
UM.RecolorImage
{
id: externalLinkIcon
anchors.verticalCenter: manageQueueLabel.verticalCenter
color: UM.Theme.getColor("primary")
source: "../svg/icons/external_link.svg"
width: 16 * screenScaleFactor // TODO: Theme! (Y U NO USE 18 LIKE ALL OTHER ICONS?!)
height: 16 * screenScaleFactor // TODO: Theme! (Y U NO USE 18 LIKE ALL OTHER ICONS?!)
}
Label
{
id: manageQueueText
anchors
{
left: externalLinkIcon.right
leftMargin: 6 * screenScaleFactor // TODO: Theme!
verticalCenter: externalLinkIcon.verticalCenter
}
color: UM.Theme.getColor("primary")
font: UM.Theme.getFont("default") // 12pt, regular
linkColor: UM.Theme.getColor("primary")
text: catalog.i18nc("@label link to connect manager", "Manage queue in Cura Connect")
}
}
MouseArea
{
anchors.fill: manageQueueLabel
hoverEnabled: true
onClicked: Cura.MachineManager.printerOutputDevices[0].openPrintJobControlPanel()
onEntered:
{
manageQueueText.font.underline = true
}
onExited:
{
manageQueueText.font.underline = false
}
}
Row
{
id: printJobQueueHeadings
anchors
{
left: queuedPrintJobs.left
leftMargin: 6 * screenScaleFactor // TODO: Theme!
top: queuedLabel.bottom
topMargin: 24 * screenScaleFactor // TODO: Theme!
}
spacing: 18 * screenScaleFactor // TODO: Theme!
Label
{
text: catalog.i18nc("@label", "Print jobs")
color: "#666666"
elide: Text.ElideRight
font: UM.Theme.getFont("medium") // 14pt, regular
anchors.verticalCenter: parent.verticalCenter
width: 284 * screenScaleFactor // TODO: Theme! (Should match column size)
// FIXED-LINE-HEIGHT:
height: 18 * screenScaleFactor // TODO: Theme!
verticalAlignment: Text.AlignVCenter
}
Label
{
text: catalog.i18nc("@label", "Total print time")
color: "#666666"
elide: Text.ElideRight
font: UM.Theme.getFont("medium") // 14pt, regular
anchors.verticalCenter: parent.verticalCenter
width: 216 * screenScaleFactor // TODO: Theme! (Should match column size)
// FIXED-LINE-HEIGHT:
height: 18 * screenScaleFactor // TODO: Theme!
verticalAlignment: Text.AlignVCenter
}
Label
{
text: catalog.i18nc("@label", "Waiting for")
color: "#666666"
elide: Text.ElideRight
font: UM.Theme.getFont("medium") // 14pt, regular
anchors.verticalCenter: parent.verticalCenter
width: 216 * screenScaleFactor // TODO: Theme! (Should match column size)
// FIXED-LINE-HEIGHT:
height: 18 * screenScaleFactor // TODO: Theme!
verticalAlignment: Text.AlignVCenter
}
}
ScrollView
{
id: queuedPrintJobs
anchors
{
bottom: parent.bottom
horizontalCenter: parent.horizontalCenter
top: printJobQueueHeadings.bottom
topMargin: 12 * screenScaleFactor // TODO: Theme!
}
style: UM.Theme.styles.scrollview
visible: OutputDevice.receivedPrintJobs
width: parent.width
ListView
{
id: printJobList
anchors.fill: parent
delegate: MonitorPrintJobCard
{
anchors
{
left: parent.left
right: parent.right
}
printJob: modelData
}
model: OutputDevice.queuedPrintJobs
spacing: 6 // TODO: Theme!
}
}
}

Some files were not shown because too many files have changed in this diff Show more