mirror of
https://github.com/Ultimaker/Cura.git
synced 2025-07-25 07:33:57 -06:00
Merge branch 'master' of https://github.com/Ultimaker/Cura
This commit is contained in:
commit
e6f37975b3
511 changed files with 3654 additions and 1870 deletions
|
@ -187,7 +187,7 @@ class BuildVolume(SceneNode):
|
||||||
self._grid_shader.setUniformValue("u_gridColor1", Color(*theme.getColor("buildplate_grid_minor").getRgb()))
|
self._grid_shader.setUniformValue("u_gridColor1", Color(*theme.getColor("buildplate_grid_minor").getRgb()))
|
||||||
|
|
||||||
renderer.queueNode(self, mode = RenderBatch.RenderMode.Lines)
|
renderer.queueNode(self, mode = RenderBatch.RenderMode.Lines)
|
||||||
renderer.queueNode(self, mesh = self._origin_mesh)
|
renderer.queueNode(self, mesh = self._origin_mesh, backface_cull = True)
|
||||||
renderer.queueNode(self, mesh = self._grid_mesh, shader = self._grid_shader, backface_cull = True)
|
renderer.queueNode(self, mesh = self._grid_mesh, shader = self._grid_shader, backface_cull = True)
|
||||||
if self._disallowed_area_mesh:
|
if self._disallowed_area_mesh:
|
||||||
renderer.queueNode(self, mesh = self._disallowed_area_mesh, shader = self._shader, transparent = True, backface_cull = True, sort = -9)
|
renderer.queueNode(self, mesh = self._disallowed_area_mesh, shader = self._shader, transparent = True, backface_cull = True, sort = -9)
|
||||||
|
|
|
@ -100,7 +100,6 @@ from PyQt5.QtWidgets import QMessageBox
|
||||||
from PyQt5.QtQml import qmlRegisterUncreatableType, qmlRegisterSingletonType, qmlRegisterType
|
from PyQt5.QtQml import qmlRegisterUncreatableType, qmlRegisterSingletonType, qmlRegisterType
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
import os.path
|
|
||||||
import numpy
|
import numpy
|
||||||
import copy
|
import copy
|
||||||
import os
|
import os
|
||||||
|
@ -133,20 +132,21 @@ class CuraApplication(QtApplication):
|
||||||
QmlFiles = Resources.UserType + 1
|
QmlFiles = Resources.UserType + 1
|
||||||
Firmware = Resources.UserType + 2
|
Firmware = Resources.UserType + 2
|
||||||
QualityInstanceContainer = Resources.UserType + 3
|
QualityInstanceContainer = Resources.UserType + 3
|
||||||
MaterialInstanceContainer = Resources.UserType + 4
|
QualityChangesInstanceContainer = Resources.UserType + 4
|
||||||
VariantInstanceContainer = Resources.UserType + 5
|
MaterialInstanceContainer = Resources.UserType + 5
|
||||||
UserInstanceContainer = Resources.UserType + 6
|
VariantInstanceContainer = Resources.UserType + 6
|
||||||
MachineStack = Resources.UserType + 7
|
UserInstanceContainer = Resources.UserType + 7
|
||||||
ExtruderStack = Resources.UserType + 8
|
MachineStack = Resources.UserType + 8
|
||||||
DefinitionChangesContainer = Resources.UserType + 9
|
ExtruderStack = Resources.UserType + 9
|
||||||
SettingVisibilityPreset = Resources.UserType + 10
|
DefinitionChangesContainer = Resources.UserType + 10
|
||||||
|
SettingVisibilityPreset = Resources.UserType + 11
|
||||||
|
|
||||||
Q_ENUMS(ResourceTypes)
|
Q_ENUMS(ResourceTypes)
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
self._boot_loading_time = time.time()
|
self._boot_loading_time = time.time()
|
||||||
# this list of dir names will be used by UM to detect an old cura directory
|
# this list of dir names will be used by UM to detect an old cura directory
|
||||||
for dir_name in ["extruders", "machine_instances", "materials", "plugins", "quality", "user", "variants"]:
|
for dir_name in ["extruders", "machine_instances", "materials", "plugins", "quality", "quality_changes", "user", "variants"]:
|
||||||
Resources.addExpectedDirNameInData(dir_name)
|
Resources.addExpectedDirNameInData(dir_name)
|
||||||
|
|
||||||
Resources.addSearchPath(os.path.join(QtApplication.getInstallPrefix(), "share", "cura", "resources"))
|
Resources.addSearchPath(os.path.join(QtApplication.getInstallPrefix(), "share", "cura", "resources"))
|
||||||
|
@ -182,6 +182,7 @@ class CuraApplication(QtApplication):
|
||||||
|
|
||||||
## Add the 4 types of profiles to storage.
|
## Add the 4 types of profiles to storage.
|
||||||
Resources.addStorageType(self.ResourceTypes.QualityInstanceContainer, "quality")
|
Resources.addStorageType(self.ResourceTypes.QualityInstanceContainer, "quality")
|
||||||
|
Resources.addStorageType(self.ResourceTypes.QualityChangesInstanceContainer, "quality_changes")
|
||||||
Resources.addStorageType(self.ResourceTypes.VariantInstanceContainer, "variants")
|
Resources.addStorageType(self.ResourceTypes.VariantInstanceContainer, "variants")
|
||||||
Resources.addStorageType(self.ResourceTypes.MaterialInstanceContainer, "materials")
|
Resources.addStorageType(self.ResourceTypes.MaterialInstanceContainer, "materials")
|
||||||
Resources.addStorageType(self.ResourceTypes.UserInstanceContainer, "user")
|
Resources.addStorageType(self.ResourceTypes.UserInstanceContainer, "user")
|
||||||
|
@ -191,7 +192,7 @@ class CuraApplication(QtApplication):
|
||||||
Resources.addStorageType(self.ResourceTypes.SettingVisibilityPreset, "setting_visibility")
|
Resources.addStorageType(self.ResourceTypes.SettingVisibilityPreset, "setting_visibility")
|
||||||
|
|
||||||
ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.QualityInstanceContainer, "quality")
|
ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.QualityInstanceContainer, "quality")
|
||||||
ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.QualityInstanceContainer, "quality_changes")
|
ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.QualityChangesInstanceContainer, "quality_changes")
|
||||||
ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.VariantInstanceContainer, "variant")
|
ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.VariantInstanceContainer, "variant")
|
||||||
ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.MaterialInstanceContainer, "material")
|
ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.MaterialInstanceContainer, "material")
|
||||||
ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.UserInstanceContainer, "user")
|
ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.UserInstanceContainer, "user")
|
||||||
|
@ -205,7 +206,7 @@ class CuraApplication(QtApplication):
|
||||||
|
|
||||||
UM.VersionUpgradeManager.VersionUpgradeManager.getInstance().setCurrentVersions(
|
UM.VersionUpgradeManager.VersionUpgradeManager.getInstance().setCurrentVersions(
|
||||||
{
|
{
|
||||||
("quality_changes", InstanceContainer.Version * 1000000 + self.SettingVersion): (self.ResourceTypes.QualityInstanceContainer, "application/x-uranium-instancecontainer"),
|
("quality_changes", InstanceContainer.Version * 1000000 + self.SettingVersion): (self.ResourceTypes.QualityChangesInstanceContainer, "application/x-uranium-instancecontainer"),
|
||||||
("machine_stack", ContainerStack.Version * 1000000 + self.SettingVersion): (self.ResourceTypes.MachineStack, "application/x-cura-globalstack"),
|
("machine_stack", ContainerStack.Version * 1000000 + self.SettingVersion): (self.ResourceTypes.MachineStack, "application/x-cura-globalstack"),
|
||||||
("extruder_train", ContainerStack.Version * 1000000 + self.SettingVersion): (self.ResourceTypes.ExtruderStack, "application/x-cura-extruderstack"),
|
("extruder_train", ContainerStack.Version * 1000000 + self.SettingVersion): (self.ResourceTypes.ExtruderStack, "application/x-cura-extruderstack"),
|
||||||
("preferences", Preferences.Version * 1000000 + self.SettingVersion): (Resources.Preferences, "application/x-uranium-preferences"),
|
("preferences", Preferences.Version * 1000000 + self.SettingVersion): (Resources.Preferences, "application/x-uranium-preferences"),
|
||||||
|
@ -241,6 +242,13 @@ class CuraApplication(QtApplication):
|
||||||
tray_icon_name = "cura-icon-32.png",
|
tray_icon_name = "cura-icon-32.png",
|
||||||
**kwargs)
|
**kwargs)
|
||||||
|
|
||||||
|
# Initialize the package manager to remove and install scheduled packages.
|
||||||
|
from cura.CuraPackageManager import CuraPackageManager
|
||||||
|
self._cura_package_manager = CuraPackageManager(self)
|
||||||
|
self._cura_package_manager.initialize()
|
||||||
|
|
||||||
|
self.initialize()
|
||||||
|
|
||||||
# FOR TESTING ONLY
|
# FOR TESTING ONLY
|
||||||
if kwargs["parsed_command_line"].get("trigger_early_crash", False):
|
if kwargs["parsed_command_line"].get("trigger_early_crash", False):
|
||||||
assert not "This crash is triggered by the trigger_early_crash command line argument."
|
assert not "This crash is triggered by the trigger_early_crash command line argument."
|
||||||
|
@ -252,21 +260,33 @@ class CuraApplication(QtApplication):
|
||||||
self.setWindowIcon(QIcon(Resources.getPath(Resources.Images, "cura-icon.png")))
|
self.setWindowIcon(QIcon(Resources.getPath(Resources.Images, "cura-icon.png")))
|
||||||
|
|
||||||
self.setRequiredPlugins([
|
self.setRequiredPlugins([
|
||||||
|
# Misc.:
|
||||||
|
"ConsoleLogger",
|
||||||
"CuraEngineBackend",
|
"CuraEngineBackend",
|
||||||
"UserAgreement",
|
"UserAgreement",
|
||||||
"SolidView",
|
|
||||||
"SimulationView",
|
|
||||||
"STLReader",
|
|
||||||
"SelectionTool",
|
|
||||||
"CameraTool",
|
|
||||||
"GCodeWriter",
|
|
||||||
"LocalFileOutputDevice",
|
|
||||||
"TranslateTool",
|
|
||||||
"FileLogger",
|
"FileLogger",
|
||||||
"XmlMaterialProfile",
|
"XmlMaterialProfile",
|
||||||
"PluginBrowser",
|
"Toolbox",
|
||||||
"PrepareStage",
|
"PrepareStage",
|
||||||
"MonitorStage"
|
"MonitorStage",
|
||||||
|
"LocalFileOutputDevice",
|
||||||
|
|
||||||
|
# Views:
|
||||||
|
"SimpleView",
|
||||||
|
"SimulationView",
|
||||||
|
"SolidView",
|
||||||
|
|
||||||
|
# Readers & Writers:
|
||||||
|
"GCodeWriter",
|
||||||
|
"STLReader",
|
||||||
|
|
||||||
|
# Tools:
|
||||||
|
"CameraTool",
|
||||||
|
"MirrorTool",
|
||||||
|
"RotateTool",
|
||||||
|
"ScaleTool",
|
||||||
|
"SelectionTool",
|
||||||
|
"TranslateTool"
|
||||||
])
|
])
|
||||||
self._physics = None
|
self._physics = None
|
||||||
self._volume = None
|
self._volume = None
|
||||||
|
@ -388,8 +408,6 @@ class CuraApplication(QtApplication):
|
||||||
self.globalContainerStackChanged.connect(self._onGlobalContainerChanged)
|
self.globalContainerStackChanged.connect(self._onGlobalContainerChanged)
|
||||||
self._onGlobalContainerChanged()
|
self._onGlobalContainerChanged()
|
||||||
|
|
||||||
self._plugin_registry.addSupportedPluginExtension("curaplugin", "Cura Plugin")
|
|
||||||
|
|
||||||
self.getCuraSceneController().setActiveBuildPlate(0) # Initialize
|
self.getCuraSceneController().setActiveBuildPlate(0) # Initialize
|
||||||
|
|
||||||
self._quality_profile_drop_down_menu_model = None
|
self._quality_profile_drop_down_menu_model = None
|
||||||
|
@ -397,7 +415,6 @@ class CuraApplication(QtApplication):
|
||||||
|
|
||||||
CuraApplication.Created = True
|
CuraApplication.Created = True
|
||||||
|
|
||||||
|
|
||||||
def _onEngineCreated(self):
|
def _onEngineCreated(self):
|
||||||
self._engine.addImageProvider("camera", CameraImageProvider.CameraImageProvider())
|
self._engine.addImageProvider("camera", CameraImageProvider.CameraImageProvider())
|
||||||
|
|
||||||
|
@ -526,7 +543,9 @@ class CuraApplication(QtApplication):
|
||||||
self._plugins_loaded = True
|
self._plugins_loaded = True
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def addCommandLineOptions(self, parser, parsed_command_line = {}):
|
def addCommandLineOptions(cls, parser, parsed_command_line = None):
|
||||||
|
if parsed_command_line is None:
|
||||||
|
parsed_command_line = {}
|
||||||
super().addCommandLineOptions(parser, parsed_command_line = parsed_command_line)
|
super().addCommandLineOptions(parser, parsed_command_line = parsed_command_line)
|
||||||
parser.add_argument("file", nargs="*", help="Files to load after starting the application.")
|
parser.add_argument("file", nargs="*", help="Files to load after starting the application.")
|
||||||
parser.add_argument("--single-instance", action="store_true", default=False)
|
parser.add_argument("--single-instance", action="store_true", default=False)
|
||||||
|
@ -583,7 +602,10 @@ class CuraApplication(QtApplication):
|
||||||
# This should be called directly before creating an instance of CuraApplication.
|
# This should be called directly before creating an instance of CuraApplication.
|
||||||
# \returns \type{bool} True if the whole Cura app should continue running.
|
# \returns \type{bool} True if the whole Cura app should continue running.
|
||||||
@classmethod
|
@classmethod
|
||||||
def preStartUp(cls, parser = None, parsed_command_line = {}):
|
def preStartUp(cls, parser = None, parsed_command_line = None):
|
||||||
|
if parsed_command_line is None:
|
||||||
|
parsed_command_line = {}
|
||||||
|
|
||||||
# Peek the arguments and look for the 'single-instance' flag.
|
# Peek the arguments and look for the 'single-instance' flag.
|
||||||
if not parser:
|
if not parser:
|
||||||
parser = argparse.ArgumentParser(prog = "cura", add_help = False) # pylint: disable=bad-whitespace
|
parser = argparse.ArgumentParser(prog = "cura", add_help = False) # pylint: disable=bad-whitespace
|
||||||
|
@ -700,7 +722,6 @@ class CuraApplication(QtApplication):
|
||||||
self._post_start_timer.timeout.connect(self._onPostStart)
|
self._post_start_timer.timeout.connect(self._onPostStart)
|
||||||
self._post_start_timer.start()
|
self._post_start_timer.start()
|
||||||
|
|
||||||
Logger.log("d", "Booting Cura took %s seconds", time.time() - self._boot_loading_time)
|
|
||||||
self.exec_()
|
self.exec_()
|
||||||
|
|
||||||
def _onPostStart(self):
|
def _onPostStart(self):
|
||||||
|
@ -789,6 +810,10 @@ class CuraApplication(QtApplication):
|
||||||
self._extruder_manager = ExtruderManager.createExtruderManager()
|
self._extruder_manager = ExtruderManager.createExtruderManager()
|
||||||
return self._extruder_manager
|
return self._extruder_manager
|
||||||
|
|
||||||
|
@pyqtSlot(result = QObject)
|
||||||
|
def getCuraPackageManager(self, *args):
|
||||||
|
return self._cura_package_manager
|
||||||
|
|
||||||
def getVariantManager(self, *args):
|
def getVariantManager(self, *args):
|
||||||
return self._variant_manager
|
return self._variant_manager
|
||||||
|
|
||||||
|
|
360
cura/CuraPackageManager.py
Normal file
360
cura/CuraPackageManager.py
Normal file
|
@ -0,0 +1,360 @@
|
||||||
|
# Copyright (c) 2018 Ultimaker B.V.
|
||||||
|
# Cura is released under the terms of the LGPLv3 or higher.
|
||||||
|
|
||||||
|
from typing import Optional
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
import zipfile
|
||||||
|
import tempfile
|
||||||
|
|
||||||
|
from PyQt5.QtCore import pyqtSlot, QObject, pyqtSignal
|
||||||
|
|
||||||
|
from UM.Application import Application
|
||||||
|
from UM.Logger import Logger
|
||||||
|
from UM.Resources import Resources
|
||||||
|
from UM.Version import Version
|
||||||
|
|
||||||
|
class CuraPackageManager(QObject):
|
||||||
|
|
||||||
|
# The prefix that's added to all files for an installed package to avoid naming conflicts with user created
|
||||||
|
# files.
|
||||||
|
PREFIX_PLACE_HOLDER = "-CP;"
|
||||||
|
|
||||||
|
def __init__(self, parent = None):
|
||||||
|
super().__init__(parent)
|
||||||
|
|
||||||
|
self._application = parent
|
||||||
|
self._container_registry = self._application.getContainerRegistry()
|
||||||
|
self._plugin_registry = self._application.getPluginRegistry()
|
||||||
|
|
||||||
|
# JSON file that keeps track of all installed packages.
|
||||||
|
self._package_management_file_path = os.path.join(os.path.abspath(Resources.getDataStoragePath()),
|
||||||
|
"packages.json")
|
||||||
|
self._installed_package_dict = {} # a dict of all installed packages
|
||||||
|
self._to_remove_package_set = set() # a set of packages that need to be removed at the next start
|
||||||
|
self._to_install_package_dict = {} # a dict of packages that need to be installed at the next start
|
||||||
|
|
||||||
|
installedPackagesChanged = pyqtSignal() # Emitted whenever the installed packages collection have been changed.
|
||||||
|
|
||||||
|
def initialize(self):
|
||||||
|
self._loadManagementData()
|
||||||
|
self._removeAllScheduledPackages()
|
||||||
|
self._installAllScheduledPackages()
|
||||||
|
|
||||||
|
# (for initialize) Loads the package management file if exists
|
||||||
|
def _loadManagementData(self) -> None:
|
||||||
|
if not os.path.exists(self._package_management_file_path):
|
||||||
|
Logger.log("i", "Package management file %s doesn't exist, do nothing", self._package_management_file_path)
|
||||||
|
return
|
||||||
|
|
||||||
|
# Need to use the file lock here to prevent concurrent I/O from other processes/threads
|
||||||
|
container_registry = self._application.getContainerRegistry()
|
||||||
|
with container_registry.lockFile():
|
||||||
|
with open(self._package_management_file_path, "r", encoding = "utf-8") as f:
|
||||||
|
management_dict = json.load(f, encoding = "utf-8")
|
||||||
|
|
||||||
|
self._installed_package_dict = management_dict.get("installed", {})
|
||||||
|
self._to_remove_package_set = set(management_dict.get("to_remove", []))
|
||||||
|
self._to_install_package_dict = management_dict.get("to_install", {})
|
||||||
|
|
||||||
|
Logger.log("i", "Package management file %s is loaded", self._package_management_file_path)
|
||||||
|
|
||||||
|
def _saveManagementData(self) -> None:
|
||||||
|
# Need to use the file lock here to prevent concurrent I/O from other processes/threads
|
||||||
|
container_registry = self._application.getContainerRegistry()
|
||||||
|
with container_registry.lockFile():
|
||||||
|
with open(self._package_management_file_path, "w", encoding = "utf-8") as f:
|
||||||
|
data_dict = {"installed": self._installed_package_dict,
|
||||||
|
"to_remove": list(self._to_remove_package_set),
|
||||||
|
"to_install": self._to_install_package_dict}
|
||||||
|
data_dict["to_remove"] = list(data_dict["to_remove"])
|
||||||
|
json.dump(data_dict, f)
|
||||||
|
Logger.log("i", "Package management file %s is saved", self._package_management_file_path)
|
||||||
|
|
||||||
|
# (for initialize) Removes all packages that have been scheduled to be removed.
|
||||||
|
def _removeAllScheduledPackages(self) -> None:
|
||||||
|
for package_id in self._to_remove_package_set:
|
||||||
|
self._purgePackage(package_id)
|
||||||
|
self._to_remove_package_set.clear()
|
||||||
|
self._saveManagementData()
|
||||||
|
|
||||||
|
# (for initialize) Installs all packages that have been scheduled to be installed.
|
||||||
|
def _installAllScheduledPackages(self) -> None:
|
||||||
|
for package_id, installation_package_data in self._to_install_package_dict.items():
|
||||||
|
self._installPackage(installation_package_data)
|
||||||
|
self._to_install_package_dict.clear()
|
||||||
|
self._saveManagementData()
|
||||||
|
|
||||||
|
# Checks the given package is installed. If so, return a dictionary that contains the package's information.
|
||||||
|
def getInstalledPackageInfo(self, package_id: str) -> Optional[dict]:
|
||||||
|
if package_id in self._to_remove_package_set:
|
||||||
|
return None
|
||||||
|
|
||||||
|
if package_id in self._to_install_package_dict:
|
||||||
|
package_info = self._to_install_package_dict[package_id]["package_info"]
|
||||||
|
package_info["is_bundled"] = False
|
||||||
|
return package_info
|
||||||
|
|
||||||
|
if package_id in self._installed_package_dict:
|
||||||
|
package_info = self._installed_package_dict.get(package_id)
|
||||||
|
package_info["is_bundled"] = False
|
||||||
|
return package_info
|
||||||
|
|
||||||
|
for section, packages in self.getAllInstalledPackagesInfo().items():
|
||||||
|
for package in packages:
|
||||||
|
if package["package_id"] == package_id:
|
||||||
|
package_info = package
|
||||||
|
return package_info
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
def getAllInstalledPackagesInfo(self) -> dict:
|
||||||
|
installed_package_id_set = set(self._installed_package_dict.keys()) | set(self._to_install_package_dict.keys())
|
||||||
|
installed_package_id_set = installed_package_id_set.difference(self._to_remove_package_set)
|
||||||
|
|
||||||
|
managed_package_id_set = set(installed_package_id_set) | self._to_remove_package_set
|
||||||
|
|
||||||
|
# TODO: For absolutely no reason, this function seems to run in a loop
|
||||||
|
# even though no loop is ever called with it.
|
||||||
|
|
||||||
|
# map of <package_type> -> <package_id> -> <package_info>
|
||||||
|
installed_packages_dict = {}
|
||||||
|
for package_id in installed_package_id_set:
|
||||||
|
if package_id in Application.getInstance().getRequiredPlugins():
|
||||||
|
continue
|
||||||
|
if package_id in self._to_install_package_dict:
|
||||||
|
package_info = self._to_install_package_dict[package_id]["package_info"]
|
||||||
|
else:
|
||||||
|
package_info = self._installed_package_dict[package_id]
|
||||||
|
package_info["is_bundled"] = False
|
||||||
|
|
||||||
|
package_type = package_info["package_type"]
|
||||||
|
if package_type not in installed_packages_dict:
|
||||||
|
installed_packages_dict[package_type] = []
|
||||||
|
installed_packages_dict[package_type].append( package_info )
|
||||||
|
|
||||||
|
# We also need to get information from the plugin registry such as if a plugin is active
|
||||||
|
package_info["is_active"] = self._plugin_registry.isActivePlugin(package_id)
|
||||||
|
|
||||||
|
# Also get all bundled plugins
|
||||||
|
all_metadata = self._plugin_registry.getAllMetaData()
|
||||||
|
for item in all_metadata:
|
||||||
|
plugin_package_info = self.__convertPluginMetadataToPackageMetadata(item)
|
||||||
|
# Only gather the bundled plugins here.
|
||||||
|
package_id = plugin_package_info["package_id"]
|
||||||
|
if package_id in managed_package_id_set:
|
||||||
|
continue
|
||||||
|
if package_id in Application.getInstance().getRequiredPlugins():
|
||||||
|
continue
|
||||||
|
|
||||||
|
plugin_package_info["is_bundled"] = True if plugin_package_info["author"]["display_name"] == "Ultimaker B.V." else False
|
||||||
|
plugin_package_info["is_active"] = self._plugin_registry.isActivePlugin(package_id)
|
||||||
|
package_type = "plugin"
|
||||||
|
if package_type not in installed_packages_dict:
|
||||||
|
installed_packages_dict[package_type] = []
|
||||||
|
installed_packages_dict[package_type].append( plugin_package_info )
|
||||||
|
|
||||||
|
return installed_packages_dict
|
||||||
|
|
||||||
|
def __convertPluginMetadataToPackageMetadata(self, plugin_metadata: dict) -> dict:
|
||||||
|
package_metadata = {
|
||||||
|
"package_id": plugin_metadata["id"],
|
||||||
|
"package_type": "plugin",
|
||||||
|
"display_name": plugin_metadata["plugin"]["name"],
|
||||||
|
"description": plugin_metadata["plugin"].get("description"),
|
||||||
|
"package_version": plugin_metadata["plugin"]["version"],
|
||||||
|
"cura_version": int(plugin_metadata["plugin"]["api"]),
|
||||||
|
"website": "",
|
||||||
|
"author_id": plugin_metadata["plugin"].get("author", "UnknownID"),
|
||||||
|
"author": {
|
||||||
|
"author_id": plugin_metadata["plugin"].get("author", "UnknownID"),
|
||||||
|
"display_name": plugin_metadata["plugin"].get("author", ""),
|
||||||
|
"email": "",
|
||||||
|
"website": "",
|
||||||
|
},
|
||||||
|
"tags": ["plugin"],
|
||||||
|
}
|
||||||
|
return package_metadata
|
||||||
|
|
||||||
|
# Checks if the given package is installed.
|
||||||
|
def isPackageInstalled(self, package_id: str) -> bool:
|
||||||
|
return self.getInstalledPackageInfo(package_id) is not None
|
||||||
|
|
||||||
|
# Schedules the given package file to be installed upon the next start.
|
||||||
|
@pyqtSlot(str)
|
||||||
|
def installPackage(self, filename: str) -> None:
|
||||||
|
# Get package information
|
||||||
|
package_info = self.getPackageInfo(filename)
|
||||||
|
package_id = package_info["package_id"]
|
||||||
|
|
||||||
|
has_changes = False
|
||||||
|
# Check the delayed installation and removal lists first
|
||||||
|
if package_id in self._to_remove_package_set:
|
||||||
|
self._to_remove_package_set.remove(package_id)
|
||||||
|
has_changes = True
|
||||||
|
|
||||||
|
# Check if it is installed
|
||||||
|
installed_package_info = self.getInstalledPackageInfo(package_info["package_id"])
|
||||||
|
to_install_package = installed_package_info is None # Install if the package has not been installed
|
||||||
|
if installed_package_info is not None:
|
||||||
|
# Compare versions and only schedule the installation if the given package is newer
|
||||||
|
new_version = package_info["package_version"]
|
||||||
|
installed_version = installed_package_info["package_version"]
|
||||||
|
if Version(new_version) > Version(installed_version):
|
||||||
|
Logger.log("i", "Package [%s] version [%s] is newer than the installed version [%s], update it.",
|
||||||
|
package_id, new_version, installed_version)
|
||||||
|
to_install_package = True
|
||||||
|
|
||||||
|
if to_install_package:
|
||||||
|
Logger.log("i", "Package [%s] version [%s] is scheduled to be installed.",
|
||||||
|
package_id, package_info["package_version"])
|
||||||
|
# Copy the file to cache dir so we don't need to rely on the original file to be present
|
||||||
|
package_cache_dir = os.path.join(os.path.abspath(Resources.getCacheStoragePath()), "cura_packages")
|
||||||
|
if not os.path.exists(package_cache_dir):
|
||||||
|
os.makedirs(package_cache_dir, exist_ok=True)
|
||||||
|
|
||||||
|
target_file_path = os.path.join(package_cache_dir, package_id + ".curapackage")
|
||||||
|
shutil.copy2(filename, target_file_path)
|
||||||
|
|
||||||
|
self._to_install_package_dict[package_id] = {"package_info": package_info,
|
||||||
|
"filename": target_file_path}
|
||||||
|
has_changes = True
|
||||||
|
|
||||||
|
self._saveManagementData()
|
||||||
|
if has_changes:
|
||||||
|
self.installedPackagesChanged.emit()
|
||||||
|
|
||||||
|
# Schedules the given package to be removed upon the next start.
|
||||||
|
@pyqtSlot(str)
|
||||||
|
def removePackage(self, package_id: str) -> None:
|
||||||
|
# Check the delayed installation and removal lists first
|
||||||
|
if not self.isPackageInstalled(package_id):
|
||||||
|
Logger.log("i", "Attempt to remove package [%s] that is not installed, do nothing.", package_id)
|
||||||
|
return
|
||||||
|
|
||||||
|
# Remove from the delayed installation list if present
|
||||||
|
if package_id in self._to_install_package_dict:
|
||||||
|
del self._to_install_package_dict[package_id]
|
||||||
|
|
||||||
|
# Schedule for a delayed removal:
|
||||||
|
self._to_remove_package_set.add(package_id)
|
||||||
|
|
||||||
|
self._saveManagementData()
|
||||||
|
self.installedPackagesChanged.emit()
|
||||||
|
|
||||||
|
# Removes everything associated with the given package ID.
|
||||||
|
def _purgePackage(self, package_id: str) -> None:
|
||||||
|
# Iterate through all directories in the data storage directory and look for sub-directories that belong to
|
||||||
|
# the package we need to remove, that is the sub-dirs with the package_id as names, and remove all those dirs.
|
||||||
|
data_storage_dir = os.path.abspath(Resources.getDataStoragePath())
|
||||||
|
|
||||||
|
for root, dir_names, _ in os.walk(data_storage_dir):
|
||||||
|
for dir_name in dir_names:
|
||||||
|
package_dir = os.path.join(root, dir_name, package_id)
|
||||||
|
if os.path.exists(package_dir):
|
||||||
|
Logger.log("i", "Removing '%s' for package [%s]", package_dir, package_id)
|
||||||
|
shutil.rmtree(package_dir)
|
||||||
|
break
|
||||||
|
|
||||||
|
# Installs all files associated with the given package.
|
||||||
|
def _installPackage(self, installation_package_data: dict):
|
||||||
|
package_info = installation_package_data["package_info"]
|
||||||
|
filename = installation_package_data["filename"]
|
||||||
|
|
||||||
|
package_id = package_info["package_id"]
|
||||||
|
|
||||||
|
if not os.path.exists(filename):
|
||||||
|
Logger.log("w", "Package [%s] file '%s' is missing, cannot install this package", package_id, filename)
|
||||||
|
return
|
||||||
|
|
||||||
|
Logger.log("i", "Installing package [%s] from file [%s]", package_id, filename)
|
||||||
|
|
||||||
|
# If it's installed, remove it first and then install
|
||||||
|
if package_id in self._installed_package_dict:
|
||||||
|
self._purgePackage(package_id)
|
||||||
|
|
||||||
|
# Install the package
|
||||||
|
archive = zipfile.ZipFile(filename, "r")
|
||||||
|
|
||||||
|
temp_dir = tempfile.TemporaryDirectory()
|
||||||
|
archive.extractall(temp_dir.name)
|
||||||
|
|
||||||
|
from cura.CuraApplication import CuraApplication
|
||||||
|
installation_dirs_dict = {
|
||||||
|
"materials": Resources.getStoragePath(CuraApplication.ResourceTypes.MaterialInstanceContainer),
|
||||||
|
"quality": Resources.getStoragePath(CuraApplication.ResourceTypes.QualityInstanceContainer),
|
||||||
|
"plugins": os.path.abspath(Resources.getStoragePath(Resources.Plugins)),
|
||||||
|
}
|
||||||
|
|
||||||
|
for sub_dir_name, installation_root_dir in installation_dirs_dict.items():
|
||||||
|
src_dir_path = os.path.join(temp_dir.name, "files", sub_dir_name)
|
||||||
|
dst_dir_path = os.path.join(installation_root_dir, package_id)
|
||||||
|
|
||||||
|
if not os.path.exists(src_dir_path):
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Need to rename the container files so they don't get ID conflicts
|
||||||
|
to_rename_files = sub_dir_name not in ("plugins",)
|
||||||
|
self.__installPackageFiles(package_id, src_dir_path, dst_dir_path, need_to_rename_files= to_rename_files)
|
||||||
|
|
||||||
|
archive.close()
|
||||||
|
|
||||||
|
# Remove the file
|
||||||
|
os.remove(filename)
|
||||||
|
|
||||||
|
def __installPackageFiles(self, package_id: str, src_dir: str, dst_dir: str, need_to_rename_files: bool = True) -> None:
|
||||||
|
shutil.move(src_dir, dst_dir)
|
||||||
|
|
||||||
|
# Rename files if needed
|
||||||
|
if not need_to_rename_files:
|
||||||
|
return
|
||||||
|
for root, _, file_names in os.walk(dst_dir):
|
||||||
|
for filename in file_names:
|
||||||
|
new_filename = self.PREFIX_PLACE_HOLDER + package_id + "-" + filename
|
||||||
|
old_file_path = os.path.join(root, filename)
|
||||||
|
new_file_path = os.path.join(root, new_filename)
|
||||||
|
os.rename(old_file_path, new_file_path)
|
||||||
|
|
||||||
|
# Gets package information from the given file.
|
||||||
|
def getPackageInfo(self, filename: str) -> dict:
|
||||||
|
archive = zipfile.ZipFile(filename, "r")
|
||||||
|
try:
|
||||||
|
# All information is in package.json
|
||||||
|
with archive.open("package.json", "r") as f:
|
||||||
|
package_info_dict = json.loads(f.read().decode("utf-8"))
|
||||||
|
return package_info_dict
|
||||||
|
except Exception as e:
|
||||||
|
raise RuntimeError("Could not get package information from file '%s': %s" % (filename, e))
|
||||||
|
finally:
|
||||||
|
archive.close()
|
||||||
|
|
||||||
|
# Gets the license file content if present in the given package file.
|
||||||
|
# Returns None if there is no license file found.
|
||||||
|
def getPackageLicense(self, filename: str) -> Optional[str]:
|
||||||
|
license_string = None
|
||||||
|
archive = zipfile.ZipFile(filename)
|
||||||
|
try:
|
||||||
|
# Go through all the files and use the first successful read as the result
|
||||||
|
for file_info in archive.infolist():
|
||||||
|
if file_info.is_dir() or not file_info.filename.startswith("files/"):
|
||||||
|
continue
|
||||||
|
|
||||||
|
filename_parts = os.path.basename(file_info.filename.lower()).split(".")
|
||||||
|
stripped_filename = filename_parts[0]
|
||||||
|
if stripped_filename in ("license", "licence"):
|
||||||
|
Logger.log("i", "Found potential license file '%s'", file_info.filename)
|
||||||
|
try:
|
||||||
|
with archive.open(file_info.filename, "r") as f:
|
||||||
|
data = f.read()
|
||||||
|
license_string = data.decode("utf-8")
|
||||||
|
break
|
||||||
|
except:
|
||||||
|
Logger.logException("e", "Failed to load potential license file '%s' as text file.",
|
||||||
|
file_info.filename)
|
||||||
|
license_string = None
|
||||||
|
except Exception as e:
|
||||||
|
raise RuntimeError("Could not get package license from file '%s': %s" % (filename, e))
|
||||||
|
finally:
|
||||||
|
archive.close()
|
||||||
|
return license_string
|
0
cura/ReaderWriters/__init__.py
Normal file
0
cura/ReaderWriters/__init__.py
Normal file
|
@ -2,7 +2,6 @@
|
||||||
# Cura is released under the terms of the LGPLv3 or higher.
|
# Cura is released under the terms of the LGPLv3 or higher.
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import os.path
|
|
||||||
import re
|
import re
|
||||||
import configparser
|
import configparser
|
||||||
|
|
||||||
|
@ -29,7 +28,7 @@ from . import GlobalStack
|
||||||
|
|
||||||
from cura.CuraApplication import CuraApplication
|
from cura.CuraApplication import CuraApplication
|
||||||
from cura.Machines.QualityManager import getMachineDefinitionIDForQualitySearch
|
from cura.Machines.QualityManager import getMachineDefinitionIDForQualitySearch
|
||||||
from cura.ProfileReader import NoProfileException
|
from cura.ReaderWriters.ProfileReader import NoProfileException
|
||||||
|
|
||||||
from UM.i18n import i18nCatalog
|
from UM.i18n import i18nCatalog
|
||||||
catalog = i18nCatalog("cura")
|
catalog = i18nCatalog("cura")
|
||||||
|
@ -676,7 +675,7 @@ class CuraContainerRegistry(ContainerRegistry):
|
||||||
return extruder_stack
|
return extruder_stack
|
||||||
|
|
||||||
def _findQualityChangesContainerInCuraFolder(self, name):
|
def _findQualityChangesContainerInCuraFolder(self, name):
|
||||||
quality_changes_dir = Resources.getPath(CuraApplication.ResourceTypes.QualityInstanceContainer)
|
quality_changes_dir = Resources.getPath(CuraApplication.ResourceTypes.QualityChangesInstanceContainer)
|
||||||
|
|
||||||
instance_container = None
|
instance_container = None
|
||||||
|
|
||||||
|
|
14
cura_app.py
14
cura_app.py
|
@ -46,11 +46,15 @@ import faulthandler
|
||||||
if Platform.isLinux(): # Needed for platform.linux_distribution, which is not available on Windows and OSX
|
if Platform.isLinux(): # Needed for platform.linux_distribution, which is not available on Windows and OSX
|
||||||
# For Ubuntu: https://bugs.launchpad.net/ubuntu/+source/python-qt4/+bug/941826
|
# For Ubuntu: https://bugs.launchpad.net/ubuntu/+source/python-qt4/+bug/941826
|
||||||
linux_distro_name = platform.linux_distribution()[0].lower()
|
linux_distro_name = platform.linux_distribution()[0].lower()
|
||||||
# TODO: Needs a "if X11_GFX == 'nvidia'" here. The workaround is only needed on Ubuntu+NVidia drivers. Other drivers are not affected, but fine with this fix.
|
# The workaround is only needed on Ubuntu+NVidia drivers. Other drivers are not affected, but fine with this fix.
|
||||||
import ctypes
|
try:
|
||||||
from ctypes.util import find_library
|
import ctypes
|
||||||
libGL = find_library("GL")
|
from ctypes.util import find_library
|
||||||
ctypes.CDLL(libGL, ctypes.RTLD_GLOBAL)
|
libGL = find_library("GL")
|
||||||
|
ctypes.CDLL(libGL, ctypes.RTLD_GLOBAL)
|
||||||
|
except:
|
||||||
|
# GLES-only systems (e.g. ARM Mali) do not have libGL, ignore error
|
||||||
|
pass
|
||||||
|
|
||||||
# When frozen, i.e. installer version, don't let PYTHONPATH mess up the search path for DLLs.
|
# When frozen, i.e. installer version, don't let PYTHONPATH mess up the search path for DLLs.
|
||||||
if Platform.isWindows() and hasattr(sys, "frozen"):
|
if Platform.isWindows() and hasattr(sys, "frozen"):
|
||||||
|
|
|
@ -6,7 +6,7 @@ from UM.PluginRegistry import PluginRegistry
|
||||||
from UM.Logger import Logger
|
from UM.Logger import Logger
|
||||||
from UM.Settings.ContainerFormatError import ContainerFormatError
|
from UM.Settings.ContainerFormatError import ContainerFormatError
|
||||||
from UM.Settings.InstanceContainer import InstanceContainer # The new profile to make.
|
from UM.Settings.InstanceContainer import InstanceContainer # The new profile to make.
|
||||||
from cura.ProfileReader import ProfileReader
|
from cura.ReaderWriters.ProfileReader import ProfileReader
|
||||||
|
|
||||||
import zipfile
|
import zipfile
|
||||||
|
|
||||||
|
|
|
@ -3,8 +3,7 @@
|
||||||
# Uranium is released under the terms of the LGPLv3 or higher.
|
# Uranium is released under the terms of the LGPLv3 or higher.
|
||||||
|
|
||||||
from UM.Logger import Logger
|
from UM.Logger import Logger
|
||||||
from UM.SaveFile import SaveFile
|
from cura.ReaderWriters.ProfileWriter import ProfileWriter
|
||||||
from cura.ProfileWriter import ProfileWriter
|
|
||||||
import zipfile
|
import zipfile
|
||||||
|
|
||||||
## Writes profiles to Cura's own profile format with config files.
|
## Writes profiles to Cura's own profile format with config files.
|
||||||
|
|
|
@ -3,11 +3,10 @@
|
||||||
|
|
||||||
import gzip
|
import gzip
|
||||||
|
|
||||||
from io import TextIOWrapper
|
|
||||||
|
|
||||||
from UM.Mesh.MeshReader import MeshReader #The class we're extending/implementing.
|
from UM.Mesh.MeshReader import MeshReader #The class we're extending/implementing.
|
||||||
from UM.PluginRegistry import PluginRegistry
|
from UM.PluginRegistry import PluginRegistry
|
||||||
|
|
||||||
|
|
||||||
## A file reader that reads gzipped g-code.
|
## A file reader that reads gzipped g-code.
|
||||||
#
|
#
|
||||||
# If you're zipping g-code, you might as well use gzip!
|
# If you're zipping g-code, you might as well use gzip!
|
||||||
|
|
|
@ -1,21 +1,24 @@
|
||||||
# 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 UM.i18n import i18nCatalog
|
||||||
|
from UM.Platform import Platform
|
||||||
|
|
||||||
from . import GCodeGzReader
|
from . import GCodeGzReader
|
||||||
|
|
||||||
from UM.i18n import i18nCatalog
|
|
||||||
i18n_catalog = i18nCatalog("cura")
|
i18n_catalog = i18nCatalog("cura")
|
||||||
|
|
||||||
def getMetaData():
|
def getMetaData():
|
||||||
|
file_extension = "gz" if Platform.isOSX() else "gcode.gz"
|
||||||
return {
|
return {
|
||||||
"mesh_reader": [
|
"mesh_reader": [
|
||||||
{
|
{
|
||||||
"extension": "gcode.gz",
|
"extension": file_extension,
|
||||||
"description": i18n_catalog.i18nc("@item:inlistbox", "Compressed G-code File")
|
"description": i18n_catalog.i18nc("@item:inlistbox", "Compressed G-code File")
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
def register(app):
|
def register(app):
|
||||||
app.addNonSliceableExtension(".gcode.gz")
|
app.addNonSliceableExtension(".gz")
|
||||||
return { "mesh_reader": GCodeGzReader.GCodeGzReader() }
|
return { "mesh_reader": GCodeGzReader.GCodeGzReader() }
|
||||||
|
|
|
@ -1,16 +1,19 @@
|
||||||
# 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 UM.i18n import i18nCatalog
|
||||||
|
from UM.Platform import Platform
|
||||||
|
|
||||||
from . import GCodeGzWriter
|
from . import GCodeGzWriter
|
||||||
|
|
||||||
from UM.i18n import i18nCatalog
|
|
||||||
catalog = i18nCatalog("cura")
|
catalog = i18nCatalog("cura")
|
||||||
|
|
||||||
def getMetaData():
|
def getMetaData():
|
||||||
|
file_extension = "gz" if Platform.isOSX() else "gcode.gz"
|
||||||
return {
|
return {
|
||||||
"mesh_writer": {
|
"mesh_writer": {
|
||||||
"output": [{
|
"output": [{
|
||||||
"extension": "gcode.gz",
|
"extension": file_extension,
|
||||||
"description": catalog.i18nc("@item:inlistbox", "Compressed G-code File"),
|
"description": catalog.i18nc("@item:inlistbox", "Compressed G-code File"),
|
||||||
"mime_type": "application/gzip",
|
"mime_type": "application/gzip",
|
||||||
"mode": GCodeGzWriter.GCodeGzWriter.OutputMode.BinaryMode
|
"mode": GCodeGzWriter.GCodeGzWriter.OutputMode.BinaryMode
|
||||||
|
|
|
@ -10,7 +10,7 @@ from UM.Logger import Logger
|
||||||
from UM.i18n import i18nCatalog
|
from UM.i18n import i18nCatalog
|
||||||
catalog = i18nCatalog("cura")
|
catalog = i18nCatalog("cura")
|
||||||
|
|
||||||
from cura.ProfileReader import ProfileReader, NoProfileException
|
from cura.ReaderWriters.ProfileReader import ProfileReader, NoProfileException
|
||||||
|
|
||||||
## A class that reads profile data from g-code files.
|
## A class that reads profile data from g-code files.
|
||||||
#
|
#
|
||||||
|
|
|
@ -12,8 +12,7 @@ from UM.Logger import Logger # Logging errors.
|
||||||
from UM.PluginRegistry import PluginRegistry # For getting the path to this plugin's directory.
|
from UM.PluginRegistry import PluginRegistry # For getting the path to this plugin's directory.
|
||||||
from UM.Settings.ContainerRegistry import ContainerRegistry #To create unique profile IDs.
|
from UM.Settings.ContainerRegistry import ContainerRegistry #To create unique profile IDs.
|
||||||
from UM.Settings.InstanceContainer import InstanceContainer # The new profile to make.
|
from UM.Settings.InstanceContainer import InstanceContainer # The new profile to make.
|
||||||
from cura.ProfileReader import ProfileReader # The plug-in type to implement.
|
from cura.ReaderWriters.ProfileReader import ProfileReader # The plug-in type to implement.
|
||||||
from cura.Settings.ExtruderManager import ExtruderManager #To get the current extruder definition.
|
|
||||||
|
|
||||||
|
|
||||||
## A plugin that reads profile data from legacy Cura versions.
|
## A plugin that reads profile data from legacy Cura versions.
|
||||||
|
|
|
@ -1,389 +0,0 @@
|
||||||
# Copyright (c) 2017 Ultimaker B.V.
|
|
||||||
# PluginBrowser is released under the terms of the LGPLv3 or higher.
|
|
||||||
|
|
||||||
from PyQt5.QtCore import QUrl, QObject, pyqtProperty, pyqtSignal, pyqtSlot
|
|
||||||
from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkRequest, QNetworkReply
|
|
||||||
|
|
||||||
from UM.Application import Application
|
|
||||||
from UM.Logger import Logger
|
|
||||||
from UM.PluginError import PluginNotFoundError
|
|
||||||
from UM.PluginRegistry import PluginRegistry
|
|
||||||
from UM.Qt.Bindings.PluginsModel import PluginsModel
|
|
||||||
from UM.Extension import Extension
|
|
||||||
from UM.i18n import i18nCatalog
|
|
||||||
|
|
||||||
from UM.Version import Version
|
|
||||||
from UM.Message import Message
|
|
||||||
|
|
||||||
import json
|
|
||||||
import os
|
|
||||||
import tempfile
|
|
||||||
import platform
|
|
||||||
import zipfile
|
|
||||||
|
|
||||||
from cura.CuraApplication import CuraApplication
|
|
||||||
|
|
||||||
i18n_catalog = i18nCatalog("cura")
|
|
||||||
|
|
||||||
class PluginBrowser(QObject, Extension):
|
|
||||||
def __init__(self, parent=None):
|
|
||||||
super().__init__(parent)
|
|
||||||
|
|
||||||
self._api_version = 4
|
|
||||||
self._api_url = "http://software.ultimaker.com/cura/v%s/" % self._api_version
|
|
||||||
|
|
||||||
self._plugin_list_request = None
|
|
||||||
self._download_plugin_request = None
|
|
||||||
|
|
||||||
self._download_plugin_reply = None
|
|
||||||
|
|
||||||
self._network_manager = None
|
|
||||||
self._plugin_registry = Application.getInstance().getPluginRegistry()
|
|
||||||
|
|
||||||
self._plugins_metadata = []
|
|
||||||
self._plugins_model = None
|
|
||||||
|
|
||||||
# Can be 'installed' or 'available'
|
|
||||||
self._view = "available"
|
|
||||||
|
|
||||||
self._restart_required = False
|
|
||||||
|
|
||||||
self._dialog = None
|
|
||||||
self._restartDialog = None
|
|
||||||
self._download_progress = 0
|
|
||||||
|
|
||||||
self._is_downloading = False
|
|
||||||
|
|
||||||
self._request_header = [b"User-Agent",
|
|
||||||
str.encode("%s/%s (%s %s)" % (Application.getInstance().getApplicationName(),
|
|
||||||
Application.getInstance().getVersion(),
|
|
||||||
platform.system(),
|
|
||||||
platform.machine(),
|
|
||||||
)
|
|
||||||
)
|
|
||||||
]
|
|
||||||
|
|
||||||
# Installed plugins are really installed after reboot. In order to
|
|
||||||
# prevent the user from downloading the same file over and over again,
|
|
||||||
# we keep track of the upgraded plugins.
|
|
||||||
|
|
||||||
# NOTE: This will be depreciated in favor of the 'status' system.
|
|
||||||
self._newly_installed_plugin_ids = []
|
|
||||||
self._newly_uninstalled_plugin_ids = []
|
|
||||||
|
|
||||||
self._plugin_statuses = {} # type: Dict[str, str]
|
|
||||||
|
|
||||||
# variables for the license agreement dialog
|
|
||||||
self._license_dialog_plugin_name = ""
|
|
||||||
self._license_dialog_license_content = ""
|
|
||||||
self._license_dialog_plugin_file_location = ""
|
|
||||||
self._restart_dialog_message = ""
|
|
||||||
|
|
||||||
showLicenseDialog = pyqtSignal()
|
|
||||||
showRestartDialog = pyqtSignal()
|
|
||||||
pluginsMetadataChanged = pyqtSignal()
|
|
||||||
onDownloadProgressChanged = pyqtSignal()
|
|
||||||
onIsDownloadingChanged = pyqtSignal()
|
|
||||||
restartRequiredChanged = pyqtSignal()
|
|
||||||
viewChanged = pyqtSignal()
|
|
||||||
|
|
||||||
@pyqtSlot(result = str)
|
|
||||||
def getLicenseDialogPluginName(self):
|
|
||||||
return self._license_dialog_plugin_name
|
|
||||||
|
|
||||||
@pyqtSlot(result = str)
|
|
||||||
def getLicenseDialogPluginFileLocation(self):
|
|
||||||
return self._license_dialog_plugin_file_location
|
|
||||||
|
|
||||||
@pyqtSlot(result = str)
|
|
||||||
def getLicenseDialogLicenseContent(self):
|
|
||||||
return self._license_dialog_license_content
|
|
||||||
|
|
||||||
@pyqtSlot(result = str)
|
|
||||||
def getRestartDialogMessage(self):
|
|
||||||
return self._restart_dialog_message
|
|
||||||
|
|
||||||
def openLicenseDialog(self, plugin_name, license_content, plugin_file_location):
|
|
||||||
self._license_dialog_plugin_name = plugin_name
|
|
||||||
self._license_dialog_license_content = license_content
|
|
||||||
self._license_dialog_plugin_file_location = plugin_file_location
|
|
||||||
self.showLicenseDialog.emit()
|
|
||||||
|
|
||||||
def openRestartDialog(self, message):
|
|
||||||
self._restart_dialog_message = message
|
|
||||||
self.showRestartDialog.emit()
|
|
||||||
|
|
||||||
@pyqtProperty(bool, notify = onIsDownloadingChanged)
|
|
||||||
def isDownloading(self):
|
|
||||||
return self._is_downloading
|
|
||||||
|
|
||||||
@pyqtSlot()
|
|
||||||
def browsePlugins(self):
|
|
||||||
self._createNetworkManager()
|
|
||||||
self.requestPluginList()
|
|
||||||
|
|
||||||
if not self._dialog:
|
|
||||||
self._dialog = self._createDialog("PluginBrowser.qml")
|
|
||||||
self._dialog.show()
|
|
||||||
|
|
||||||
@pyqtSlot()
|
|
||||||
def requestPluginList(self):
|
|
||||||
Logger.log("i", "Requesting plugin list")
|
|
||||||
url = QUrl(self._api_url + "plugins")
|
|
||||||
self._plugin_list_request = QNetworkRequest(url)
|
|
||||||
self._plugin_list_request.setRawHeader(*self._request_header)
|
|
||||||
self._network_manager.get(self._plugin_list_request)
|
|
||||||
|
|
||||||
def _createDialog(self, qml_name):
|
|
||||||
Logger.log("d", "Creating dialog [%s]", qml_name)
|
|
||||||
path = os.path.join(PluginRegistry.getInstance().getPluginPath(self.getPluginId()), qml_name)
|
|
||||||
dialog = Application.getInstance().createQmlComponent(path, {"manager": self})
|
|
||||||
return dialog
|
|
||||||
|
|
||||||
def setIsDownloading(self, is_downloading):
|
|
||||||
if self._is_downloading != is_downloading:
|
|
||||||
self._is_downloading = is_downloading
|
|
||||||
self.onIsDownloadingChanged.emit()
|
|
||||||
|
|
||||||
def _onDownloadPluginProgress(self, bytes_sent, bytes_total):
|
|
||||||
if bytes_total > 0:
|
|
||||||
new_progress = bytes_sent / bytes_total * 100
|
|
||||||
self.setDownloadProgress(new_progress)
|
|
||||||
if new_progress == 100.0:
|
|
||||||
self.setIsDownloading(False)
|
|
||||||
self._download_plugin_reply.downloadProgress.disconnect(self._onDownloadPluginProgress)
|
|
||||||
|
|
||||||
# must not delete the temporary file on Windows
|
|
||||||
self._temp_plugin_file = tempfile.NamedTemporaryFile(mode = "w+b", suffix = ".curaplugin", delete = False)
|
|
||||||
location = self._temp_plugin_file.name
|
|
||||||
|
|
||||||
# write first and close, otherwise on Windows, it cannot read the file
|
|
||||||
self._temp_plugin_file.write(self._download_plugin_reply.readAll())
|
|
||||||
self._temp_plugin_file.close()
|
|
||||||
|
|
||||||
self._checkPluginLicenseOrInstall(location)
|
|
||||||
return
|
|
||||||
|
|
||||||
## Checks if the downloaded plugin ZIP file contains a license file or not.
|
|
||||||
# If it does, it will show a popup dialog displaying the license to the user. The plugin will be installed if the
|
|
||||||
# user accepts the license.
|
|
||||||
# If there is no license file, the plugin will be directory installed.
|
|
||||||
def _checkPluginLicenseOrInstall(self, file_path):
|
|
||||||
with zipfile.ZipFile(file_path, "r") as zip_ref:
|
|
||||||
plugin_id = None
|
|
||||||
for file in zip_ref.infolist():
|
|
||||||
if file.filename.endswith("/"):
|
|
||||||
plugin_id = file.filename.strip("/")
|
|
||||||
break
|
|
||||||
|
|
||||||
if plugin_id is None:
|
|
||||||
msg = i18n_catalog.i18nc("@info:status", "Failed to get plugin ID from <filename>{0}</filename>", file_path)
|
|
||||||
msg_title = i18n_catalog.i18nc("@info:tile", "Warning")
|
|
||||||
self._progress_message = Message(msg, lifetime=0, dismissable=False, title = msg_title)
|
|
||||||
return
|
|
||||||
|
|
||||||
# find a potential license file
|
|
||||||
plugin_root_dir = plugin_id + "/"
|
|
||||||
license_file = None
|
|
||||||
for f in zip_ref.infolist():
|
|
||||||
# skip directories (with file_size = 0) and files not in the plugin directory
|
|
||||||
if f.file_size == 0 or not f.filename.startswith(plugin_root_dir):
|
|
||||||
continue
|
|
||||||
file_name = os.path.basename(f.filename).lower()
|
|
||||||
file_base_name, file_ext = os.path.splitext(file_name)
|
|
||||||
if file_base_name in ["license", "licence"]:
|
|
||||||
license_file = f.filename
|
|
||||||
break
|
|
||||||
|
|
||||||
# show a dialog for user to read and accept/decline the license
|
|
||||||
if license_file is not None:
|
|
||||||
Logger.log("i", "Found license file for plugin [%s], showing the license dialog to the user", plugin_id)
|
|
||||||
license_content = zip_ref.read(license_file).decode('utf-8')
|
|
||||||
self.openLicenseDialog(plugin_id, license_content, file_path)
|
|
||||||
return
|
|
||||||
|
|
||||||
# there is no license file, directly install the plugin
|
|
||||||
self.installPlugin(file_path)
|
|
||||||
|
|
||||||
@pyqtSlot(str)
|
|
||||||
def installPlugin(self, file_path):
|
|
||||||
# Ensure that it starts with a /, as otherwise it doesn't work on windows.
|
|
||||||
if not file_path.startswith("/"):
|
|
||||||
location = "/" + file_path
|
|
||||||
else:
|
|
||||||
location = file_path
|
|
||||||
|
|
||||||
result = PluginRegistry.getInstance().installPlugin("file://" + location)
|
|
||||||
|
|
||||||
self._newly_installed_plugin_ids.append(result["id"])
|
|
||||||
self.pluginsMetadataChanged.emit()
|
|
||||||
|
|
||||||
self.openRestartDialog(result["message"])
|
|
||||||
self._restart_required = True
|
|
||||||
self.restartRequiredChanged.emit()
|
|
||||||
# Application.getInstance().messageBox(i18n_catalog.i18nc("@window:title", "Plugin browser"), result["message"])
|
|
||||||
|
|
||||||
@pyqtSlot(str)
|
|
||||||
def removePlugin(self, plugin_id):
|
|
||||||
result = PluginRegistry.getInstance().uninstallPlugin(plugin_id)
|
|
||||||
|
|
||||||
self._newly_uninstalled_plugin_ids.append(result["id"])
|
|
||||||
self.pluginsMetadataChanged.emit()
|
|
||||||
|
|
||||||
self._restart_required = True
|
|
||||||
self.restartRequiredChanged.emit()
|
|
||||||
|
|
||||||
Application.getInstance().messageBox(i18n_catalog.i18nc("@window:title", "Plugin browser"), result["message"])
|
|
||||||
|
|
||||||
@pyqtSlot(str)
|
|
||||||
def enablePlugin(self, plugin_id):
|
|
||||||
self._plugin_registry.enablePlugin(plugin_id)
|
|
||||||
self.pluginsMetadataChanged.emit()
|
|
||||||
Logger.log("i", "%s was set as 'active'", id)
|
|
||||||
|
|
||||||
@pyqtSlot(str)
|
|
||||||
def disablePlugin(self, plugin_id):
|
|
||||||
self._plugin_registry.disablePlugin(plugin_id)
|
|
||||||
self.pluginsMetadataChanged.emit()
|
|
||||||
Logger.log("i", "%s was set as 'deactive'", id)
|
|
||||||
|
|
||||||
@pyqtProperty(int, notify = onDownloadProgressChanged)
|
|
||||||
def downloadProgress(self):
|
|
||||||
return self._download_progress
|
|
||||||
|
|
||||||
def setDownloadProgress(self, progress):
|
|
||||||
if progress != self._download_progress:
|
|
||||||
self._download_progress = progress
|
|
||||||
self.onDownloadProgressChanged.emit()
|
|
||||||
|
|
||||||
@pyqtSlot(str)
|
|
||||||
def downloadAndInstallPlugin(self, url):
|
|
||||||
Logger.log("i", "Attempting to download & install plugin from %s", url)
|
|
||||||
url = QUrl(url)
|
|
||||||
self._download_plugin_request = QNetworkRequest(url)
|
|
||||||
self._download_plugin_request.setRawHeader(*self._request_header)
|
|
||||||
self._download_plugin_reply = self._network_manager.get(self._download_plugin_request)
|
|
||||||
self.setDownloadProgress(0)
|
|
||||||
self.setIsDownloading(True)
|
|
||||||
self._download_plugin_reply.downloadProgress.connect(self._onDownloadPluginProgress)
|
|
||||||
|
|
||||||
@pyqtSlot()
|
|
||||||
def cancelDownload(self):
|
|
||||||
Logger.log("i", "user cancelled the download of a plugin")
|
|
||||||
self._download_plugin_reply.abort()
|
|
||||||
self._download_plugin_reply.downloadProgress.disconnect(self._onDownloadPluginProgress)
|
|
||||||
self._download_plugin_reply = None
|
|
||||||
self._download_plugin_request = None
|
|
||||||
|
|
||||||
self.setDownloadProgress(0)
|
|
||||||
self.setIsDownloading(False)
|
|
||||||
|
|
||||||
@pyqtSlot(str)
|
|
||||||
def setView(self, view):
|
|
||||||
self._view = view
|
|
||||||
self.viewChanged.emit()
|
|
||||||
self.pluginsMetadataChanged.emit()
|
|
||||||
|
|
||||||
@pyqtProperty(QObject, notify=pluginsMetadataChanged)
|
|
||||||
def pluginsModel(self):
|
|
||||||
self._plugins_model = PluginsModel(None, self._view)
|
|
||||||
# self._plugins_model.update()
|
|
||||||
|
|
||||||
# Check each plugin the registry for matching plugin from server
|
|
||||||
# metadata, and if found, compare the versions. Higher version sets
|
|
||||||
# 'can_upgrade' to 'True':
|
|
||||||
for plugin in self._plugins_model.items:
|
|
||||||
if self._checkCanUpgrade(plugin["id"], plugin["version"]):
|
|
||||||
plugin["can_upgrade"] = True
|
|
||||||
|
|
||||||
for item in self._plugins_metadata:
|
|
||||||
if item["id"] == plugin["id"]:
|
|
||||||
plugin["update_url"] = item["file_location"]
|
|
||||||
|
|
||||||
return self._plugins_model
|
|
||||||
|
|
||||||
def _checkCanUpgrade(self, plugin_id, version):
|
|
||||||
if not self._plugin_registry.isInstalledPlugin(plugin_id):
|
|
||||||
return False
|
|
||||||
|
|
||||||
try:
|
|
||||||
plugin_object = self._plugin_registry.getPluginObject(plugin_id)
|
|
||||||
except PluginNotFoundError:
|
|
||||||
# CURA-5287
|
|
||||||
# At this point, we know that this plugin is installed because it passed the previous check, but we cannot
|
|
||||||
# get the PluginObject. This means there is a bug in the plugin or something. So, we always allow to upgrade
|
|
||||||
# this plugin and hopefully that fixes it.
|
|
||||||
Logger.log("w", "Could not find plugin %s", plugin_id)
|
|
||||||
return True
|
|
||||||
|
|
||||||
# Scan plugin server data for plugin with the given id:
|
|
||||||
for plugin in self._plugins_metadata:
|
|
||||||
if plugin_id == plugin["id"]:
|
|
||||||
reg_version = Version(plugin_object.getVersion())
|
|
||||||
new_version = Version(plugin["version"])
|
|
||||||
if new_version > reg_version:
|
|
||||||
Logger.log("i", "%s has an update available: %s", plugin["id"], plugin["version"])
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
def _onRequestFinished(self, reply):
|
|
||||||
reply_url = reply.url().toString()
|
|
||||||
if reply.error() == QNetworkReply.TimeoutError:
|
|
||||||
Logger.log("w", "Got a timeout.")
|
|
||||||
# Reset everything.
|
|
||||||
self.setDownloadProgress(0)
|
|
||||||
self.setIsDownloading(False)
|
|
||||||
if self._download_plugin_reply:
|
|
||||||
self._download_plugin_reply.downloadProgress.disconnect(self._onDownloadPluginProgress)
|
|
||||||
self._download_plugin_reply.abort()
|
|
||||||
self._download_plugin_reply = None
|
|
||||||
return
|
|
||||||
elif reply.error() == QNetworkReply.HostNotFoundError:
|
|
||||||
Logger.log("w", "Unable to reach server.")
|
|
||||||
return
|
|
||||||
|
|
||||||
if reply.operation() == QNetworkAccessManager.GetOperation:
|
|
||||||
if reply_url == self._api_url + "plugins":
|
|
||||||
try:
|
|
||||||
json_data = json.loads(bytes(reply.readAll()).decode("utf-8"))
|
|
||||||
|
|
||||||
# Add metadata to the manager:
|
|
||||||
self._plugins_metadata = json_data
|
|
||||||
self._plugin_registry.addExternalPlugins(self._plugins_metadata)
|
|
||||||
self.pluginsMetadataChanged.emit()
|
|
||||||
except json.decoder.JSONDecodeError:
|
|
||||||
Logger.log("w", "Received an invalid print job state message: Not valid JSON.")
|
|
||||||
return
|
|
||||||
else:
|
|
||||||
# Ignore any operation that is not a get operation
|
|
||||||
pass
|
|
||||||
|
|
||||||
def _onNetworkAccesibleChanged(self, accessible):
|
|
||||||
if accessible == 0:
|
|
||||||
self.setDownloadProgress(0)
|
|
||||||
self.setIsDownloading(False)
|
|
||||||
if self._download_plugin_reply:
|
|
||||||
self._download_plugin_reply.downloadProgress.disconnect(self._onDownloadPluginProgress)
|
|
||||||
self._download_plugin_reply.abort()
|
|
||||||
self._download_plugin_reply = None
|
|
||||||
|
|
||||||
def _createNetworkManager(self):
|
|
||||||
if self._network_manager:
|
|
||||||
self._network_manager.finished.disconnect(self._onRequestFinished)
|
|
||||||
self._network_manager.networkAccessibleChanged.disconnect(self._onNetworkAccesibleChanged)
|
|
||||||
|
|
||||||
self._network_manager = QNetworkAccessManager()
|
|
||||||
self._network_manager.finished.connect(self._onRequestFinished)
|
|
||||||
self._network_manager.networkAccessibleChanged.connect(self._onNetworkAccesibleChanged)
|
|
||||||
|
|
||||||
@pyqtProperty(bool, notify = restartRequiredChanged)
|
|
||||||
def restartRequired(self):
|
|
||||||
return self._restart_required
|
|
||||||
|
|
||||||
@pyqtProperty(str, notify = viewChanged)
|
|
||||||
def viewing(self):
|
|
||||||
return self._view
|
|
||||||
|
|
||||||
@pyqtSlot()
|
|
||||||
def restart(self):
|
|
||||||
CuraApplication.getInstance().windowClosed()
|
|
|
@ -1,369 +0,0 @@
|
||||||
// Copyright (c) 2017 Ultimaker B.V.
|
|
||||||
// PluginBrowser is released under the terms of the LGPLv3 or higher.
|
|
||||||
|
|
||||||
import QtQuick 2.2
|
|
||||||
import QtQuick.Dialogs 1.1
|
|
||||||
import QtQuick.Window 2.2
|
|
||||||
import QtQuick.Controls 1.4
|
|
||||||
import QtQuick.Controls.Styles 1.4
|
|
||||||
|
|
||||||
// TODO: Switch to QtQuick.Controls 2.x and remove QtQuick.Controls.Styles
|
|
||||||
|
|
||||||
import UM 1.1 as UM
|
|
||||||
|
|
||||||
Window {
|
|
||||||
id: base
|
|
||||||
|
|
||||||
title: catalog.i18nc("@title:tab", "Plugins");
|
|
||||||
modality: Qt.ApplicationModal
|
|
||||||
width: 800 * screenScaleFactor
|
|
||||||
height: 640 * screenScaleFactor
|
|
||||||
minimumWidth: 350 * screenScaleFactor
|
|
||||||
minimumHeight: 350 * screenScaleFactor
|
|
||||||
color: UM.Theme.getColor("sidebar")
|
|
||||||
|
|
||||||
Item {
|
|
||||||
id: view
|
|
||||||
anchors {
|
|
||||||
fill: parent
|
|
||||||
leftMargin: UM.Theme.getSize("default_margin").width
|
|
||||||
rightMargin: UM.Theme.getSize("default_margin").width
|
|
||||||
topMargin: UM.Theme.getSize("default_margin").height
|
|
||||||
bottomMargin: UM.Theme.getSize("default_margin").height
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
id: topBar
|
|
||||||
width: parent.width
|
|
||||||
color: "transparent"
|
|
||||||
height: childrenRect.height
|
|
||||||
|
|
||||||
Row {
|
|
||||||
spacing: 12
|
|
||||||
height: childrenRect.height
|
|
||||||
width: childrenRect.width
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
|
|
||||||
Button {
|
|
||||||
text: "Install"
|
|
||||||
style: ButtonStyle {
|
|
||||||
background: Rectangle {
|
|
||||||
color: "transparent"
|
|
||||||
implicitWidth: 96
|
|
||||||
implicitHeight: 48
|
|
||||||
Rectangle {
|
|
||||||
visible: manager.viewing == "available" ? true : false
|
|
||||||
color: UM.Theme.getColor("primary")
|
|
||||||
anchors.bottom: parent.bottom
|
|
||||||
width: parent.width
|
|
||||||
height: 3
|
|
||||||
}
|
|
||||||
}
|
|
||||||
label: Text {
|
|
||||||
text: control.text
|
|
||||||
color: UM.Theme.getColor("text")
|
|
||||||
font {
|
|
||||||
pixelSize: 15
|
|
||||||
}
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
}
|
|
||||||
}
|
|
||||||
onClicked: manager.setView("available")
|
|
||||||
}
|
|
||||||
|
|
||||||
Button {
|
|
||||||
text: "Manage"
|
|
||||||
style: ButtonStyle {
|
|
||||||
background: Rectangle {
|
|
||||||
color: "transparent"
|
|
||||||
implicitWidth: 96
|
|
||||||
implicitHeight: 48
|
|
||||||
Rectangle {
|
|
||||||
visible: manager.viewing == "installed" ? true : false
|
|
||||||
color: UM.Theme.getColor("primary")
|
|
||||||
anchors.bottom: parent.bottom
|
|
||||||
width: parent.width
|
|
||||||
height: 3
|
|
||||||
}
|
|
||||||
}
|
|
||||||
label: Text {
|
|
||||||
text: control.text
|
|
||||||
color: UM.Theme.getColor("text")
|
|
||||||
font {
|
|
||||||
pixelSize: 15
|
|
||||||
}
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
}
|
|
||||||
}
|
|
||||||
onClicked: manager.setView("installed")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Scroll view breaks in QtQuick.Controls 2.x
|
|
||||||
ScrollView {
|
|
||||||
id: installedPluginList
|
|
||||||
width: parent.width
|
|
||||||
height: 400
|
|
||||||
|
|
||||||
anchors {
|
|
||||||
top: topBar.bottom
|
|
||||||
topMargin: UM.Theme.getSize("default_margin").height
|
|
||||||
bottom: bottomBar.top
|
|
||||||
bottomMargin: UM.Theme.getSize("default_margin").height
|
|
||||||
}
|
|
||||||
|
|
||||||
frameVisible: true
|
|
||||||
|
|
||||||
ListView {
|
|
||||||
id: pluginList
|
|
||||||
property var activePlugin
|
|
||||||
property var filter: "installed"
|
|
||||||
|
|
||||||
anchors.fill: parent
|
|
||||||
|
|
||||||
model: manager.pluginsModel
|
|
||||||
delegate: PluginEntry {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
id: bottomBar
|
|
||||||
width: parent.width
|
|
||||||
height: childrenRect.height
|
|
||||||
color: "transparent"
|
|
||||||
anchors.bottom: parent.bottom
|
|
||||||
|
|
||||||
Label {
|
|
||||||
visible: manager.restartRequired
|
|
||||||
text: "You will need to restart Cura before changes in plugins have effect."
|
|
||||||
height: 30
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
}
|
|
||||||
Button {
|
|
||||||
id: restartChangedButton
|
|
||||||
text: "Quit Cura"
|
|
||||||
anchors.right: closeButton.left
|
|
||||||
anchors.rightMargin: UM.Theme.getSize("default_margin").width
|
|
||||||
visible: manager.restartRequired
|
|
||||||
iconName: "dialog-restart"
|
|
||||||
onClicked: manager.restart()
|
|
||||||
style: ButtonStyle {
|
|
||||||
background: Rectangle {
|
|
||||||
implicitWidth: 96
|
|
||||||
implicitHeight: 30
|
|
||||||
color: UM.Theme.getColor("primary")
|
|
||||||
}
|
|
||||||
label: Text {
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
color: UM.Theme.getColor("button_text")
|
|
||||||
font {
|
|
||||||
pixelSize: 13
|
|
||||||
bold: true
|
|
||||||
}
|
|
||||||
text: control.text
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Button {
|
|
||||||
id: closeButton
|
|
||||||
text: catalog.i18nc("@action:button", "Close")
|
|
||||||
iconName: "dialog-close"
|
|
||||||
onClicked: {
|
|
||||||
if ( manager.isDownloading ) {
|
|
||||||
manager.cancelDownload()
|
|
||||||
}
|
|
||||||
base.close();
|
|
||||||
}
|
|
||||||
anchors.right: parent.right
|
|
||||||
style: ButtonStyle {
|
|
||||||
background: Rectangle {
|
|
||||||
color: "transparent"
|
|
||||||
implicitWidth: 96
|
|
||||||
implicitHeight: 30
|
|
||||||
border {
|
|
||||||
width: 1
|
|
||||||
color: UM.Theme.getColor("lining")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
label: Text {
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
color: UM.Theme.getColor("text")
|
|
||||||
text: control.text
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
UM.I18nCatalog { id: catalog; name: "cura" }
|
|
||||||
|
|
||||||
Connections {
|
|
||||||
target: manager
|
|
||||||
onShowLicenseDialog: {
|
|
||||||
licenseDialog.pluginName = manager.getLicenseDialogPluginName();
|
|
||||||
licenseDialog.licenseContent = manager.getLicenseDialogLicenseContent();
|
|
||||||
licenseDialog.pluginFileLocation = manager.getLicenseDialogPluginFileLocation();
|
|
||||||
licenseDialog.show();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
UM.Dialog {
|
|
||||||
id: licenseDialog
|
|
||||||
title: catalog.i18nc("@title:window", "Plugin License Agreement")
|
|
||||||
|
|
||||||
minimumWidth: UM.Theme.getSize("license_window_minimum").width
|
|
||||||
minimumHeight: UM.Theme.getSize("license_window_minimum").height
|
|
||||||
width: minimumWidth
|
|
||||||
height: minimumHeight
|
|
||||||
|
|
||||||
property var pluginName;
|
|
||||||
property var licenseContent;
|
|
||||||
property var pluginFileLocation;
|
|
||||||
|
|
||||||
Item
|
|
||||||
{
|
|
||||||
anchors.fill: parent
|
|
||||||
|
|
||||||
Label
|
|
||||||
{
|
|
||||||
id: licenseTitle
|
|
||||||
anchors.top: parent.top
|
|
||||||
anchors.left: parent.left
|
|
||||||
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?")
|
|
||||||
wrapMode: Text.Wrap
|
|
||||||
}
|
|
||||||
|
|
||||||
TextArea
|
|
||||||
{
|
|
||||||
id: licenseText
|
|
||||||
anchors.top: licenseTitle.bottom
|
|
||||||
anchors.bottom: parent.bottom
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.topMargin: UM.Theme.getSize("default_margin").height
|
|
||||||
readOnly: true
|
|
||||||
text: licenseDialog.licenseContent != null ? licenseDialog.licenseContent : ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
rightButtons: [
|
|
||||||
Button
|
|
||||||
{
|
|
||||||
id: acceptButton
|
|
||||||
anchors.margins: UM.Theme.getSize("default_margin").width
|
|
||||||
text: catalog.i18nc("@action:button", "Accept")
|
|
||||||
onClicked:
|
|
||||||
{
|
|
||||||
licenseDialog.close();
|
|
||||||
manager.installPlugin(licenseDialog.pluginFileLocation);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Button
|
|
||||||
{
|
|
||||||
id: declineButton
|
|
||||||
anchors.margins: UM.Theme.getSize("default_margin").width
|
|
||||||
text: catalog.i18nc("@action:button", "Decline")
|
|
||||||
onClicked:
|
|
||||||
{
|
|
||||||
licenseDialog.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
Connections {
|
|
||||||
target: manager
|
|
||||||
onShowRestartDialog: {
|
|
||||||
restartDialog.message = manager.getRestartDialogMessage();
|
|
||||||
restartDialog.show();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Window {
|
|
||||||
id: restartDialog
|
|
||||||
// title: catalog.i18nc("@title:tab", "Plugins");
|
|
||||||
width: 360 * screenScaleFactor
|
|
||||||
height: 120 * screenScaleFactor
|
|
||||||
minimumWidth: 360 * screenScaleFactor
|
|
||||||
minimumHeight: 120 * screenScaleFactor
|
|
||||||
color: UM.Theme.getColor("sidebar")
|
|
||||||
property var message;
|
|
||||||
|
|
||||||
Text {
|
|
||||||
id: message
|
|
||||||
anchors {
|
|
||||||
left: parent.left
|
|
||||||
leftMargin: UM.Theme.getSize("default_margin").width
|
|
||||||
top: parent.top
|
|
||||||
topMargin: UM.Theme.getSize("default_margin").height
|
|
||||||
}
|
|
||||||
text: restartDialog.message != null ? restartDialog.message : ""
|
|
||||||
}
|
|
||||||
Button {
|
|
||||||
id: laterButton
|
|
||||||
text: "Later"
|
|
||||||
onClicked: restartDialog.close();
|
|
||||||
anchors {
|
|
||||||
left: parent.left
|
|
||||||
leftMargin: UM.Theme.getSize("default_margin").width
|
|
||||||
bottom: parent.bottom
|
|
||||||
bottomMargin: UM.Theme.getSize("default_margin").height
|
|
||||||
}
|
|
||||||
style: ButtonStyle {
|
|
||||||
background: Rectangle {
|
|
||||||
color: "transparent"
|
|
||||||
implicitWidth: 96
|
|
||||||
implicitHeight: 30
|
|
||||||
border {
|
|
||||||
width: 1
|
|
||||||
color: UM.Theme.getColor("lining")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
label: Text {
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
color: UM.Theme.getColor("text")
|
|
||||||
text: control.text
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Button {
|
|
||||||
id: restartButton
|
|
||||||
text: "Quit Cura"
|
|
||||||
anchors {
|
|
||||||
right: parent.right
|
|
||||||
rightMargin: UM.Theme.getSize("default_margin").width
|
|
||||||
bottom: parent.bottom
|
|
||||||
bottomMargin: UM.Theme.getSize("default_margin").height
|
|
||||||
}
|
|
||||||
onClicked: manager.restart()
|
|
||||||
style: ButtonStyle {
|
|
||||||
background: Rectangle {
|
|
||||||
implicitWidth: 96
|
|
||||||
implicitHeight: 30
|
|
||||||
color: UM.Theme.getColor("primary")
|
|
||||||
}
|
|
||||||
label: Text {
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
color: UM.Theme.getColor("button_text")
|
|
||||||
font {
|
|
||||||
pixelSize: 13
|
|
||||||
bold: true
|
|
||||||
}
|
|
||||||
text: control.text
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,486 +0,0 @@
|
||||||
// Copyright (c) 2018 Ultimaker B.V.
|
|
||||||
// PluginBrowser is released under the terms of the LGPLv3 or higher.
|
|
||||||
|
|
||||||
import QtQuick 2.2
|
|
||||||
import QtQuick.Dialogs 1.1
|
|
||||||
import QtQuick.Window 2.2
|
|
||||||
import QtQuick.Controls 1.4
|
|
||||||
import QtQuick.Controls.Styles 1.4
|
|
||||||
|
|
||||||
// TODO: Switch to QtQuick.Controls 2.x and remove QtQuick.Controls.Styles
|
|
||||||
|
|
||||||
import UM 1.1 as UM
|
|
||||||
|
|
||||||
Component {
|
|
||||||
id: pluginDelegate
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
|
|
||||||
// Don't show required plugins as they can't be managed anyway:
|
|
||||||
height: !model.required ? 84 : 0
|
|
||||||
visible: !model.required ? true : false
|
|
||||||
color: "transparent"
|
|
||||||
anchors {
|
|
||||||
left: parent.left
|
|
||||||
leftMargin: UM.Theme.getSize("default_margin").width
|
|
||||||
right: parent.right
|
|
||||||
rightMargin: UM.Theme.getSize("default_margin").width
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Bottom border:
|
|
||||||
Rectangle {
|
|
||||||
color: UM.Theme.getColor("lining")
|
|
||||||
width: parent.width
|
|
||||||
height: 1
|
|
||||||
anchors.bottom: parent.bottom
|
|
||||||
}
|
|
||||||
|
|
||||||
// Plugin info
|
|
||||||
Column {
|
|
||||||
id: pluginInfo
|
|
||||||
|
|
||||||
property var color: model.enabled ? UM.Theme.getColor("text") : UM.Theme.getColor("lining")
|
|
||||||
|
|
||||||
// Styling:
|
|
||||||
height: parent.height
|
|
||||||
anchors {
|
|
||||||
left: parent.left
|
|
||||||
top: parent.top
|
|
||||||
topMargin: UM.Theme.getSize("default_margin").height
|
|
||||||
right: authorInfo.left
|
|
||||||
rightMargin: UM.Theme.getSize("default_margin").width
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Label {
|
|
||||||
text: model.name
|
|
||||||
width: parent.width
|
|
||||||
height: 24
|
|
||||||
wrapMode: Text.WordWrap
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
font {
|
|
||||||
pixelSize: 13
|
|
||||||
bold: true
|
|
||||||
}
|
|
||||||
color: pluginInfo.color
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Text {
|
|
||||||
text: model.description
|
|
||||||
width: parent.width
|
|
||||||
height: 36
|
|
||||||
clip: true
|
|
||||||
wrapMode: Text.WordWrap
|
|
||||||
color: pluginInfo.color
|
|
||||||
elide: Text.ElideRight
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Author info
|
|
||||||
Column {
|
|
||||||
id: authorInfo
|
|
||||||
width: 192
|
|
||||||
height: parent.height
|
|
||||||
anchors {
|
|
||||||
top: parent.top
|
|
||||||
topMargin: UM.Theme.getSize("default_margin").height
|
|
||||||
right: pluginActions.left
|
|
||||||
rightMargin: UM.Theme.getSize("default_margin").width
|
|
||||||
}
|
|
||||||
|
|
||||||
Label {
|
|
||||||
text: "<a href=\"mailto:"+model.author_email+"?Subject=Cura: "+model.name+"\">"+model.author+"</a>"
|
|
||||||
width: parent.width
|
|
||||||
height: 24
|
|
||||||
wrapMode: Text.WordWrap
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
horizontalAlignment: Text.AlignLeft
|
|
||||||
onLinkActivated: Qt.openUrlExternally("mailto:"+model.author_email+"?Subject=Cura: "+model.name+" Plugin")
|
|
||||||
color: model.enabled ? UM.Theme.getColor("text") : UM.Theme.getColor("lining")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Plugin actions
|
|
||||||
Row {
|
|
||||||
id: pluginActions
|
|
||||||
|
|
||||||
width: 96
|
|
||||||
height: parent.height
|
|
||||||
anchors {
|
|
||||||
top: parent.top
|
|
||||||
right: parent.right
|
|
||||||
topMargin: UM.Theme.getSize("default_margin").height
|
|
||||||
}
|
|
||||||
layoutDirection: Qt.RightToLeft
|
|
||||||
spacing: UM.Theme.getSize("default_margin").width
|
|
||||||
|
|
||||||
// For 3rd-Party Plugins:
|
|
||||||
Button {
|
|
||||||
id: installButton
|
|
||||||
text: {
|
|
||||||
if ( manager.isDownloading && pluginList.activePlugin == model ) {
|
|
||||||
return catalog.i18nc( "@action:button", "Cancel" );
|
|
||||||
} else {
|
|
||||||
if (model.can_upgrade) {
|
|
||||||
return catalog.i18nc("@action:button", "Update");
|
|
||||||
}
|
|
||||||
return catalog.i18nc("@action:button", "Install");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
enabled:
|
|
||||||
{
|
|
||||||
if ( manager.isDownloading )
|
|
||||||
{
|
|
||||||
return pluginList.activePlugin == model ? true : false
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
opacity: enabled ? 1.0 : 0.5
|
|
||||||
visible: model.external && ((model.status !== "installed") || model.can_upgrade)
|
|
||||||
style: ButtonStyle {
|
|
||||||
background: Rectangle {
|
|
||||||
implicitWidth: 96
|
|
||||||
implicitHeight: 30
|
|
||||||
color: "transparent"
|
|
||||||
border {
|
|
||||||
width: 1
|
|
||||||
color: UM.Theme.getColor("lining")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
label: Label {
|
|
||||||
text: control.text
|
|
||||||
color: UM.Theme.getColor("text")
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
}
|
|
||||||
}
|
|
||||||
onClicked: {
|
|
||||||
if ( manager.isDownloading && pluginList.activePlugin == model ) {
|
|
||||||
manager.cancelDownload();
|
|
||||||
} else {
|
|
||||||
pluginList.activePlugin = model;
|
|
||||||
if ( model.can_upgrade ) {
|
|
||||||
manager.downloadAndInstallPlugin( model.update_url );
|
|
||||||
} else {
|
|
||||||
manager.downloadAndInstallPlugin( model.file_location );
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Button {
|
|
||||||
id: removeButton
|
|
||||||
text: "Uninstall"
|
|
||||||
visible: model.can_uninstall && model.status == "installed"
|
|
||||||
enabled: !manager.isDownloading
|
|
||||||
style: ButtonStyle {
|
|
||||||
background: Rectangle {
|
|
||||||
implicitWidth: 96
|
|
||||||
implicitHeight: 30
|
|
||||||
color: "transparent"
|
|
||||||
border {
|
|
||||||
width: 1
|
|
||||||
color: UM.Theme.getColor("lining")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
label: Text {
|
|
||||||
text: control.text
|
|
||||||
color: UM.Theme.getColor("text")
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
}
|
|
||||||
}
|
|
||||||
onClicked: manager.removePlugin( model.id )
|
|
||||||
}
|
|
||||||
|
|
||||||
// For Ultimaker Plugins:
|
|
||||||
Button {
|
|
||||||
id: enableButton
|
|
||||||
text: "Enable"
|
|
||||||
visible: !model.external && model.enabled == false
|
|
||||||
style: ButtonStyle {
|
|
||||||
background: Rectangle {
|
|
||||||
implicitWidth: 96
|
|
||||||
implicitHeight: 30
|
|
||||||
color: "transparent"
|
|
||||||
border {
|
|
||||||
width: 1
|
|
||||||
color: UM.Theme.getColor("lining")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
label: Text {
|
|
||||||
text: control.text
|
|
||||||
color: UM.Theme.getColor("text")
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
}
|
|
||||||
}
|
|
||||||
onClicked: {
|
|
||||||
manager.enablePlugin(model.id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Button {
|
|
||||||
id: disableButton
|
|
||||||
text: "Disable"
|
|
||||||
visible: !model.external && model.enabled == true
|
|
||||||
style: ButtonStyle {
|
|
||||||
background: Rectangle {
|
|
||||||
implicitWidth: 96
|
|
||||||
implicitHeight: 30
|
|
||||||
color: "transparent"
|
|
||||||
border {
|
|
||||||
width: 1
|
|
||||||
color: UM.Theme.getColor("lining")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
label: Text {
|
|
||||||
text: control.text
|
|
||||||
color: UM.Theme.getColor("text")
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
}
|
|
||||||
}
|
|
||||||
onClicked: {
|
|
||||||
manager.disablePlugin(model.id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
Rectangle {
|
|
||||||
id: removeControls
|
|
||||||
visible: model.status == "installed" && model.enabled
|
|
||||||
width: 96
|
|
||||||
height: 30
|
|
||||||
color: "transparent"
|
|
||||||
Button {
|
|
||||||
id: removeButton
|
|
||||||
text: "Disable"
|
|
||||||
enabled: {
|
|
||||||
if ( manager.isDownloading && pluginList.activePlugin == model ) {
|
|
||||||
return false;
|
|
||||||
} else if ( model.required ) {
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
onClicked: {
|
|
||||||
manager.disablePlugin(model.id);
|
|
||||||
}
|
|
||||||
style: ButtonStyle {
|
|
||||||
background: Rectangle {
|
|
||||||
color: "white"
|
|
||||||
implicitWidth: 96
|
|
||||||
implicitHeight: 30
|
|
||||||
border {
|
|
||||||
width: 1
|
|
||||||
color: UM.Theme.getColor("lining")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
label: Text {
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
color: "grey"
|
|
||||||
text: control.text
|
|
||||||
horizontalAlignment: Text.AlignLeft
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Button {
|
|
||||||
id: removeDropDown
|
|
||||||
property bool open: false
|
|
||||||
UM.RecolorImage {
|
|
||||||
anchors.centerIn: parent
|
|
||||||
height: 10
|
|
||||||
width: 10
|
|
||||||
source: UM.Theme.getIcon("arrow_bottom")
|
|
||||||
color: "grey"
|
|
||||||
}
|
|
||||||
enabled: {
|
|
||||||
if ( manager.isDownloading && pluginList.activePlugin == model ) {
|
|
||||||
return false;
|
|
||||||
} else if ( model.required ) {
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
anchors.right: parent.right
|
|
||||||
style: ButtonStyle {
|
|
||||||
background: Rectangle {
|
|
||||||
color: "transparent"
|
|
||||||
implicitWidth: 30
|
|
||||||
implicitHeight: 30
|
|
||||||
}
|
|
||||||
label: Text {
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
color: "grey"
|
|
||||||
text: control.text
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// For the disable option:
|
|
||||||
// onClicked: pluginList.model.setEnabled(model.id, checked)
|
|
||||||
|
|
||||||
onClicked: {
|
|
||||||
if ( !removeDropDown.open ) {
|
|
||||||
removeDropDown.open = true
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
removeDropDown.open = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
id: divider
|
|
||||||
width: 1
|
|
||||||
height: parent.height
|
|
||||||
anchors.right: removeDropDown.left
|
|
||||||
color: UM.Theme.getColor("lining")
|
|
||||||
}
|
|
||||||
|
|
||||||
Column {
|
|
||||||
id: options
|
|
||||||
anchors {
|
|
||||||
top: removeButton.bottom
|
|
||||||
left: parent.left
|
|
||||||
right: parent.right
|
|
||||||
}
|
|
||||||
height: childrenRect.height
|
|
||||||
visible: removeDropDown.open
|
|
||||||
|
|
||||||
Button {
|
|
||||||
id: disableButton
|
|
||||||
text: "Remove"
|
|
||||||
height: 30
|
|
||||||
width: parent.width
|
|
||||||
onClicked: {
|
|
||||||
removeDropDown.open = false;
|
|
||||||
manager.removePlugin( model.id );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
/*
|
|
||||||
Button {
|
|
||||||
id: enableButton
|
|
||||||
visible: !model.enabled && model.status == "installed"
|
|
||||||
onClicked: manager.enablePlugin( model.id );
|
|
||||||
|
|
||||||
text: "Enable"
|
|
||||||
style: ButtonStyle {
|
|
||||||
background: Rectangle {
|
|
||||||
color: "transparent"
|
|
||||||
implicitWidth: 96
|
|
||||||
implicitHeight: 30
|
|
||||||
border {
|
|
||||||
width: 1
|
|
||||||
color: UM.Theme.getColor("lining")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
label: Text {
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
color: UM.Theme.getColor("text")
|
|
||||||
text: control.text
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Button {
|
|
||||||
id: updateButton
|
|
||||||
visible: model.status == "installed" && model.can_upgrade && model.enabled
|
|
||||||
// visible: model.already_installed
|
|
||||||
text: {
|
|
||||||
// If currently downloading:
|
|
||||||
if ( manager.isDownloading && pluginList.activePlugin == model ) {
|
|
||||||
return catalog.i18nc( "@action:button", "Cancel" );
|
|
||||||
} else {
|
|
||||||
return catalog.i18nc("@action:button", "Update");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
style: ButtonStyle {
|
|
||||||
background: Rectangle {
|
|
||||||
color: UM.Theme.getColor("primary")
|
|
||||||
implicitWidth: 96
|
|
||||||
implicitHeight: 30
|
|
||||||
// radius: 4
|
|
||||||
}
|
|
||||||
label: Text {
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
color: "white"
|
|
||||||
text: control.text
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Button {
|
|
||||||
id: externalControls
|
|
||||||
visible: model.status == "available" ? true : false
|
|
||||||
text: {
|
|
||||||
// If currently downloading:
|
|
||||||
if ( manager.isDownloading && pluginList.activePlugin == model ) {
|
|
||||||
return catalog.i18nc( "@action:button", "Cancel" );
|
|
||||||
} else {
|
|
||||||
return catalog.i18nc("@action:button", "Install");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
onClicked: {
|
|
||||||
if ( manager.isDownloading && pluginList.activePlugin == model ) {
|
|
||||||
manager.cancelDownload();
|
|
||||||
} else {
|
|
||||||
pluginList.activePlugin = model;
|
|
||||||
manager.downloadAndInstallPlugin( model.file_location );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
style: ButtonStyle {
|
|
||||||
background: Rectangle {
|
|
||||||
color: "transparent"
|
|
||||||
implicitWidth: 96
|
|
||||||
implicitHeight: 30
|
|
||||||
border {
|
|
||||||
width: 1
|
|
||||||
color: UM.Theme.getColor("lining")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
label: Text {
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
color: "grey"
|
|
||||||
text: control.text
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
ProgressBar {
|
|
||||||
id: progressbar
|
|
||||||
minimumValue: 0;
|
|
||||||
maximumValue: 100
|
|
||||||
anchors.left: installButton.left
|
|
||||||
anchors.right: installButton.right
|
|
||||||
anchors.top: installButton.bottom
|
|
||||||
anchors.topMargin: 4
|
|
||||||
value: manager.isDownloading ? manager.downloadProgress : 0
|
|
||||||
visible: manager.isDownloading && pluginList.activePlugin == model
|
|
||||||
style: ProgressBarStyle {
|
|
||||||
background: Rectangle {
|
|
||||||
color: "lightgray"
|
|
||||||
implicitHeight: 6
|
|
||||||
}
|
|
||||||
progress: Rectangle {
|
|
||||||
color: UM.Theme.getColor("primary")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,12 +0,0 @@
|
||||||
# Copyright (c) 2017 Ultimaker B.V.
|
|
||||||
# PluginBrowser is released under the terms of the LGPLv3 or higher.
|
|
||||||
|
|
||||||
from . import PluginBrowser
|
|
||||||
|
|
||||||
|
|
||||||
def getMetaData():
|
|
||||||
return {}
|
|
||||||
|
|
||||||
|
|
||||||
def register(app):
|
|
||||||
return {"extension": PluginBrowser.PluginBrowser()}
|
|
|
@ -1,7 +0,0 @@
|
||||||
{
|
|
||||||
"name": "Plugin Browser",
|
|
||||||
"author": "Ultimaker B.V.",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"api": 4,
|
|
||||||
"description": "Find, manage and install new plugins."
|
|
||||||
}
|
|
|
@ -28,6 +28,13 @@ vertex =
|
||||||
}
|
}
|
||||||
|
|
||||||
fragment =
|
fragment =
|
||||||
|
#ifdef GL_ES
|
||||||
|
#ifdef GL_FRAGMENT_PRECISION_HIGH
|
||||||
|
precision highp float;
|
||||||
|
#else
|
||||||
|
precision mediump float;
|
||||||
|
#endif // GL_FRAGMENT_PRECISION_HIGH
|
||||||
|
#endif // GL_ES
|
||||||
varying lowp vec4 v_color;
|
varying lowp vec4 v_color;
|
||||||
varying float v_line_type;
|
varying float v_line_type;
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,13 @@ vertex =
|
||||||
}
|
}
|
||||||
|
|
||||||
fragment =
|
fragment =
|
||||||
|
#ifdef GL_ES
|
||||||
|
#ifdef GL_FRAGMENT_PRECISION_HIGH
|
||||||
|
precision highp float;
|
||||||
|
#else
|
||||||
|
precision mediump float;
|
||||||
|
#endif // GL_FRAGMENT_PRECISION_HIGH
|
||||||
|
#endif // GL_ES
|
||||||
varying lowp vec4 v_color;
|
varying lowp vec4 v_color;
|
||||||
varying float v_line_type;
|
varying float v_line_type;
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,13 @@ vertex =
|
||||||
}
|
}
|
||||||
|
|
||||||
fragment =
|
fragment =
|
||||||
|
#ifdef GL_ES
|
||||||
|
#ifdef GL_FRAGMENT_PRECISION_HIGH
|
||||||
|
precision highp float;
|
||||||
|
#else
|
||||||
|
precision mediump float;
|
||||||
|
#endif // GL_FRAGMENT_PRECISION_HIGH
|
||||||
|
#endif // GL_ES
|
||||||
uniform sampler2D u_layer0;
|
uniform sampler2D u_layer0;
|
||||||
uniform sampler2D u_layer1;
|
uniform sampler2D u_layer1;
|
||||||
uniform sampler2D u_layer2;
|
uniform sampler2D u_layer2;
|
||||||
|
|
12
plugins/Toolbox/__init__.py
Normal file
12
plugins/Toolbox/__init__.py
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
# Copyright (c) 2018 Ultimaker B.V.
|
||||||
|
# Toolbox is released under the terms of the LGPLv3 or higher.
|
||||||
|
|
||||||
|
from .src import Toolbox
|
||||||
|
|
||||||
|
|
||||||
|
def getMetaData():
|
||||||
|
return {}
|
||||||
|
|
||||||
|
|
||||||
|
def register(app):
|
||||||
|
return {"extension": Toolbox.Toolbox()}
|
7
plugins/Toolbox/plugin.json
Normal file
7
plugins/Toolbox/plugin.json
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"name": "Toolbox",
|
||||||
|
"author": "Ultimaker B.V.",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"api": 4,
|
||||||
|
"description": "Find, manage and install new Cura packages."
|
||||||
|
}
|
161
plugins/Toolbox/resources/images/logobot.svg
Normal file
161
plugins/Toolbox/resources/images/logobot.svg
Normal file
|
@ -0,0 +1,161 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 18.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg version="1.1" id="Layer_1" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns"
|
||||||
|
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="-122.4 196.9 50.3 74.1"
|
||||||
|
enable-background="new -122.4 196.9 50.3 74.1" xml:space="preserve">
|
||||||
|
<title>logo</title>
|
||||||
|
<desc>Created with Sketch.</desc>
|
||||||
|
<g id="homepage" sketch:type="MSPage">
|
||||||
|
<g id="Home-menu" transform="translate(-35.000000, -37.000000)" sketch:type="MSArtboardGroup">
|
||||||
|
<g id="hero-3" transform="translate(-792.000000, -68.000000)" sketch:type="MSLayerGroup">
|
||||||
|
<g id="Group-2">
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g id="Menu" sketch:type="MSLayerGroup">
|
||||||
|
<g id="logo" transform="translate(35.000000, 37.845203)" sketch:type="MSShapeGroup">
|
||||||
|
<g id="Robot" transform="translate(51.265823, 0.000000)">
|
||||||
|
<path id="Fill-23" fill="#000000" d="M-139.9,203.6c-0.3,0-0.6,0-0.9,0.1c-0.3,0.1-0.5,0.2-0.7,0.4c0,0,0,0,0,0c0,0,0,0,0,0
|
||||||
|
c-0.1,0.1-0.2,0.2-0.3,0.3c0,0,0,0,0,0c-0.1,0.1-0.1,0.2-0.2,0.3c0,0,0,0,0,0c0,0,0,0,0,0c-0.1,0.1-0.1,0.2-0.1,0.4
|
||||||
|
c0,0,0,0,0,0c0,0.1,0,0.3,0,0.4c0,0.3,0.1,0.6,0.2,0.9c0.1,0.3,0.3,0.5,0.5,0.7c0.2,0.2,0.5,0.4,0.7,0.5
|
||||||
|
c0.3,0.1,0.6,0.2,0.9,0.2c0.1,0,0.2,0,0.3,0c0,0,0.1,0,0.1,0c0.1,0,0.1,0,0.2,0c0,0,0.1,0,0.1,0c0.1,0,0.1,0,0.2-0.1
|
||||||
|
c0,0,0,0,0.1,0c0,0,0,0,0,0c0,0,0,0,0,0c0.3-0.1,0.5-0.2,0.7-0.4c0.1-0.1,0.2-0.2,0.3-0.3c0.1-0.1,0.2-0.3,0.2-0.4
|
||||||
|
c0.1-0.3,0.2-0.5,0.2-0.8s-0.1-0.6-0.2-0.9c-0.1-0.2-0.2-0.3-0.3-0.5c0,0,0,0,0,0c-0.1-0.1-0.1-0.1-0.2-0.2
|
||||||
|
c-0.1-0.1-0.2-0.2-0.3-0.3c-0.1-0.1-0.3-0.2-0.4-0.2C-139.3,203.7-139.6,203.6-139.9,203.6"/>
|
||||||
|
<path id="Fill-24" fill="#000000" d="M-138.4,211.3c-0.1-0.1-0.1-0.1-0.2-0.1c-0.1,0-0.2-0.1-0.3-0.1l-11.4-0.3
|
||||||
|
c-0.1,0-0.2,0-0.3,0c-0.1,0-0.1,0.1-0.2,0.1c0,0,0,0,0,0c0,0,0,0,0,0c-0.1,0.1-0.1,0.1-0.1,0.2c0,0,0,0,0,0c0,0,0,0,0,0
|
||||||
|
c0,0.1-0.1,0.1-0.1,0.2v0.7c0,0.1,0,0.2,0.1,0.3c0,0.1,0.1,0.1,0.2,0.2c0.1,0.1,0.1,0.1,0.2,0.1c0.1,0,0.2,0.1,0.3,0.1
|
||||||
|
l11.4,0.2c0.1,0,0.2,0,0.3,0c0.1,0,0.1-0.1,0.2-0.1c0.1-0.1,0.1-0.1,0.1-0.2c0-0.1,0.1-0.2,0.1-0.2v-0.6c0-0.1,0-0.2-0.1-0.2
|
||||||
|
C-138.3,211.5-138.4,211.4-138.4,211.3"/>
|
||||||
|
<path id="Fill-25" fill="#000000" d="M-151,207.2c0.2,0.2,0.5,0.4,0.9,0.6c0.3,0.1,0.7,0.2,1,0.2c0.1,0,0.2,0,0.4,0
|
||||||
|
c0,0,0.1,0,0.1,0c0.1,0,0.2,0,0.3-0.1c0,0,0,0,0,0c0.1,0,0.2,0,0.2-0.1c0,0,0,0,0,0c0.1,0,0.3-0.1,0.4-0.2c0,0,0,0,0,0
|
||||||
|
c0.1-0.1,0.3-0.2,0.4-0.3c0.2-0.2,0.4-0.5,0.6-0.8c0.1-0.3,0.2-0.6,0.2-0.9c0-0.3-0.1-0.7-0.2-1c-0.1-0.3-0.3-0.6-0.6-0.8
|
||||||
|
c-0.2-0.2-0.5-0.4-0.8-0.6c-0.3-0.1-0.7-0.2-1-0.2c-0.4,0-0.7,0-1,0.1c-0.3,0.1-0.6,0.3-0.8,0.4c0,0-0.1,0-0.1,0.1
|
||||||
|
c0,0-0.1,0.1-0.1,0.1c-0.1,0.1-0.1,0.1-0.2,0.2c-0.1,0.1-0.1,0.2-0.2,0.2c0,0.1-0.1,0.1-0.1,0.2c0,0,0,0,0,0c0,0,0,0,0,0
|
||||||
|
c-0.1,0.1-0.1,0.3-0.1,0.4c0,0,0,0,0,0c0,0.1-0.1,0.3-0.1,0.5c0,0.3,0.1,0.7,0.2,1C-151.4,206.7-151.3,207-151,207.2"/>
|
||||||
|
<path id="Fill-26" fill="#000000" d="M-139.8,235.5h0.9c0.6,0,2.1,0.3,2.1,0.3s-1.5,0.3-2.1,0.3h-0.9c0,0-0.1,0-0.1,0l-0.1,0.8
|
||||||
|
l-0.1-0.8c-0.7-0.1-1.7-0.2-1.7-0.2s1-0.2,1.7-0.2l-0.3-1.6l-3.2-0.1l-0.2,1.9l1.9,0.1l-1.9,0.1l-0.2,1.8h0.4
|
||||||
|
c0.9,0,3.1,0.3,3.1,0.3s-2.2,0.3-3.1,0.3h-0.4l-0.1,0.7l-0.1-0.7h-0.7c-0.9,0-3.1-0.3-3.1-0.3s2.2-0.3,3.1-0.3h0.7l-0.2-1.8
|
||||||
|
l-4.6,0.3l-0.4,1.9l-0.4-1.9l-3.6-0.4l3.6-0.4l0.4-1.8l-5.1-0.2l9.8-0.4l-0.1-1.6l-3-0.2l-0.3,1.6l-0.3-1.6l-5.9-0.4l5.9-0.4
|
||||||
|
l0.4-1.7l0.3,1.6l3-0.2l0.2-2.1c-0.9-0.1-1.6-0.2-1.6-0.2s0.8-0.1,1.7-0.2l0.2-1.6h-0.6c-1.1,0-3.7-0.4-3.7-0.4
|
||||||
|
s2.6-0.4,3.7-0.4h0.7l0.1-1.2l0.1,1.2h0.6c1.1,0,3.8,0.4,3.8,0.4s-2.7,0.4-3.8,0.4h-0.5l0.2,1.5c0.4,0,0.8-0.1,1.1-0.1h1.5
|
||||||
|
c1,0,3.7,0.4,3.7,0.4c0,0-2.6,0.4-3.7,0.4h-1.5c-0.3,0-0.7,0-1.1-0.1l0.2,2l2.5,0.7l-2.5,0.7l-0.1,1.5l3.1-0.1l0.5-3.4l0.5,3.4
|
||||||
|
l4.2,0.5l-4.2,0.5L-139.8,235.5L-139.8,235.5L-139.8,235.5z M-150.9,227.5h1c0.7,0,2.4,0.3,2.4,0.3s-1.7,0.3-2.4,0.3h-1
|
||||||
|
c-0.7,0-2.3-0.3-2.3-0.3S-151.6,227.5-150.9,227.5L-150.9,227.5z M-137.4,230.5h0.1c0.4,0,1.3,0.3,1.3,0.3s-0.9,0.3-1.3,0.3
|
||||||
|
h-0.6c-0.4,0-1.3-0.3-1.3-0.3s0.9-0.3,1.3-0.3H-137.4L-137.4,230.5z M-131.8,221.6c0-0.1,0-0.1-0.1-0.2c0-0.1-0.1-0.1-0.1-0.2
|
||||||
|
c-0.1,0-0.1-0.1-0.2-0.1c-0.1,0-0.2,0-0.3,0l-25,0.1c-0.1,0-0.2,0-0.3,0c-0.1,0-0.2,0.1-0.2,0.1c-0.1,0.1-0.1,0.1-0.2,0.2
|
||||||
|
c0,0.1-0.1,0.2-0.1,0.2c-0.2,1.8-0.3,3.6-0.3,5.4c-0.1,1.8-0.1,3.6-0.1,5.4c0,1.8,0,3.6,0.1,5.4c0.1,1.8,0.2,3.6,0.3,5.4
|
||||||
|
c0,0.1,0,0.2,0.1,0.2c0,0.1,0.1,0.1,0.2,0.2c0.1,0.1,0.2,0.1,0.2,0.1c0.2,0.1,6.9,0.4,6.9,0.4c0.5-0.3,1-0.6,1.5-0.8
|
||||||
|
c0.5-0.2,1-0.4,1.6-0.6c0.3-0.1,0.5-0.2,0.8-0.2c0,0,0,0,0,0c0.2,0,0.3-0.1,0.5-0.1c0.1,0,0.2-0.1,0.3-0.1c0,0,0,0,0,0h0
|
||||||
|
c0,0,0,0,0.1,0c0.1,0,0.1,0,0.2,0c0,0,0,0,0,0c0,0,0,0,0.1,0c0.1,0,0.2,0,0.3,0c0.1,0,0.1,0,0.2,0c0.3,0,0.6-0.1,0.8-0.1
|
||||||
|
c0.3,0,0.5,0,0.8,0c0.1,0,0.1,0,0.2,0c0.2,0,0.4,0,0.6,0h0c0,0,0.1,0,0.1,0c0,0,0,0,0.1,0h0c0.1,0,0.3,0,0.4,0
|
||||||
|
c0.1,0,0.2,0,0.3,0c2,0.2,3.8,1.1,3.8,1.1c0.5-0.1,6-1.3,6.1-1.3c0.1,0,0.2-0.1,0.2-0.1c0.1-0.1,0.1-0.1,0.2-0.2
|
||||||
|
c0-0.1,0.1-0.1,0.1-0.2c0.1-1.7,0.2-3.3,0.3-5c0.1-1.7,0.1-3.3,0.1-5c0-1.7,0-3.3-0.1-5C-131.6,224.9-131.7,223.2-131.8,221.6
|
||||||
|
L-131.8,221.6z"/>
|
||||||
|
<path id="Fill-27" fill="#000000" d="M-149.4,233.6l0.4,1.8l4.5,0.3l-0.2-1.9L-149.4,233.6"/>
|
||||||
|
<path id="Fill-28" fill="#000000" d="M-142.3,260.2c-0.2,0.3-0.2,2.2-0.1,2.6c0.1,0.5,2.1,0.1,2.1,0.1s-1.1-2.1,0.7-2.7
|
||||||
|
c0.4-0.1,1.6-0.4,2.9-0.6c1.9-0.4,4.1-0.7,4.1-0.7s-1.1-0.3-2.7-0.1l-0.3,0l-0.3,0h0l-3.7,0.5
|
||||||
|
C-141.4,259.6-142,259.8-142.3,260.2"/>
|
||||||
|
<path id="Fill-29" fill="#000000" d="M-128.1,243.8v-1.5c0,0,0,0,0,0c0.1-0.1,0.1-0.1,0.2-0.2c0.1-0.1,0.3-0.2,0.4-0.3
|
||||||
|
c0.1-0.1,0.1-0.2,0.1-0.3c0-0.1,0-0.2-0.1-0.4c0-0.1-0.1-0.3-0.2-0.4c-0.1-0.1-0.2-0.3-0.3-0.3c0,0-0.1-0.1-0.1-0.1v-3.2
|
||||||
|
c0,0,0.1,0,0.1,0c0.3-0.1,0.6-0.2,0.8-0.4c0,0.1,0.1,0.1,0.1,0.2c0.2,0.3,0.5,0.6,0.7,1.1c0.2,0.4,0.4,0.9,0.5,1.5
|
||||||
|
C-125.8,239.5-125.2,243-128.1,243.8L-128.1,243.8z M-128.1,234.4c0.3-0.1,0.8-0.2,1.1-0.3c0.1,0,0.2-0.1,0.3-0.1v0.3
|
||||||
|
c-0.1,0-0.2,0.1-0.2,0.1c-0.4,0.2-0.9,0.4-1.2,0.5V234.4L-128.1,234.4z M-128.1,232.8c0.3,0,0.7-0.1,1.1-0.1
|
||||||
|
c0.1,0,0.3,0,0.4-0.1v0.3c-0.1,0-0.2,0.1-0.4,0.1c-0.4,0.1-0.8,0.2-1.1,0.3V232.8L-128.1,232.8z M-128.1,231.1
|
||||||
|
c0.4,0,1,0,1.5,0.1v0.2c-0.5,0.1-1,0.2-1.5,0.3V231.1L-128.1,231.1z M-128.1,229.4c0.3,0.1,0.7,0.1,1.1,0.2
|
||||||
|
c0.1,0,0.3,0.1,0.4,0.1v0.3c-0.1,0-0.3,0-0.4,0c-0.4,0-0.7,0-1,0V229.4L-128.1,229.4z M-128.1,227.6c0.4,0.1,0.8,0.2,1.2,0.4
|
||||||
|
c0.1,0,0.2,0.1,0.3,0.1v0.3c-0.1,0-0.2-0.1-0.4-0.1c-0.4-0.1-0.8-0.1-1.1-0.2V227.6L-128.1,227.6z M-128.1,221.4
|
||||||
|
C-128.1,221.4-128,221.4-128.1,221.4c0.5,0.3,1,1.6,1.3,2.2c0.3,0.6,0.4,1.3,0.4,2.1c0,0.1,0,0.1,0,0.2c0,0.1-0.1,0.1-0.1,0.1
|
||||||
|
c0,0-0.1,0.1-0.1,0.1c0,0-0.1,0-0.2,0c0,0-0.1,0-0.2,0s-0.1,0-0.2,0c0,0-0.1,0-0.1,0c-0.3,0-0.6,0-0.9,0L-128.1,221.4
|
||||||
|
L-128.1,221.4z M-129.9,221.4v22.7c0,0.3-0.1,0.6-0.2,0.9c-0.1,0.3-0.3,0.5-0.5,0.7c-0.2,0.2-0.5,0.4-0.7,0.5
|
||||||
|
c-0.3,0.1-0.6,0.2-0.9,0.2l-1.5,0.1c0,0,0,0,0,0l-0.1-0.3c-0.1-0.4-0.5-0.7-0.8-0.7l-1.6,0.1l-19,1.2c-0.4,0-0.8,0.3-0.9,0.7
|
||||||
|
l-0.4,0.9c0,0,0,0,0,0l-1.3,0.1c-0.4,0-0.7,0-1-0.1c-0.3-0.1-0.6-0.2-0.9-0.4c-0.2-0.2-0.4-0.4-0.6-0.7
|
||||||
|
c-0.1-0.3-0.2-0.6-0.2-0.9v-25c0-0.3,0.1-0.7,0.2-0.9c0.1-0.3,0.3-0.6,0.6-0.8c0.2-0.2,0.5-0.4,0.9-0.5
|
||||||
|
c0.3-0.1,0.6-0.2,0.9-0.2c0,0,0.1,0,0.1,0l1.2,0c0-0.1,0-0.1,0-0.2l0-0.4c0-0.4,0.3-0.7,0.7-0.7l11.4,0.2l1.1,0l2.7,0l1.7,0
|
||||||
|
l5.2,0.1c0.3,0,0.6,0.2,0.7,0.6c0,0.1,0,0.1,0,0.2l0,0.2c0,0,0,0.1,0,0.1l0.8,0c0.3,0,0.6,0.1,0.9,0.2c0.3,0.1,0.5,0.3,0.7,0.5
|
||||||
|
c0.2,0.2,0.4,0.4,0.5,0.7C-130,220.7-129.9,221-129.9,221.4L-129.9,221.4L-129.9,221.4z M-137.6,255.8v-6.4c0,0,0-0.1,0-0.1
|
||||||
|
s0,0,0-0.1c0.1,0,0.3,0,0.4,0l0,2.1c0.7-0.6,1.4-1.2,2.3-0.9c0.4,0.1,0.7,0.3,1,0.4v4.6L-137.6,255.8L-137.6,255.8z
|
||||||
|
M-141.1,258.1l5.5-0.6l1.7-0.2c0.3,0,0.5,0,0.7,0.1c0.2,0.1,0.4,0.2,0.6,0.4c0.2,0.2,0.3,0.4,0.4,0.7c0,0,0,0.1,0,0.1
|
||||||
|
c0.1,0.2,0.1,0.5,0.1,0.8v3c-0.4,0.1-1.1,0.1-1.8,0.3c-2.4,0.3-6.1,0.8-7.4,1c-2,0.3-1.9-0.5-1.9-0.5v-2.4c0-0.3,0-0.5,0.1-0.8
|
||||||
|
c0-0.1,0-0.1,0.1-0.2c0.1-0.3,0.2-0.6,0.4-0.8c0.2-0.2,0.4-0.4,0.6-0.6C-141.6,258.2-141.4,258.1-141.1,258.1L-141.1,258.1z
|
||||||
|
M-140.6,251.9c0.6,0.4,1.4,0.8,2.2,0.4l0.2-0.1l-0.4,3.7l-3.2,0.3v-6.4c0,0,0-0.1,0-0.2c0.1,0,0.2,0,0.4,0l0,3
|
||||||
|
C-141.2,252.2-140.8,251.7-140.6,251.9L-140.6,251.9z M-131.6,262.9c0,0,0.1,0,0.1,0c0,0,0.1,0,0.1,0.1c0,0,0.1,0.1,0.1,0.1
|
||||||
|
c0,0.1,0,0.1,0,0.2v0.4c0,0.1,0,0.1,0,0.2c0,0.1,0,0.1-0.1,0.2c0,0-0.1,0.1-0.1,0.1c0,0-0.1,0-0.1,0.1l-11.8,1.6
|
||||||
|
c-0.1,0-0.1,0-0.2,0c0,0-0.1,0-0.1-0.1c0,0-0.1-0.1-0.1-0.1c0-0.1,0-0.1,0-0.2v-0.5c0-0.1,0-0.1,0-0.2c0-0.1,0-0.1,0.1-0.2
|
||||||
|
c0,0,0.1-0.1,0.1-0.1c0,0,0.1-0.1,0.2-0.1l9.4-1.3l0.1,0l0.2,0l0.8-0.1l0.8-0.1L-131.6,262.9L-131.6,262.9z M-145.2,265.7
|
||||||
|
c0,0.1,0,0.1,0,0.2c0,0.1-0.1,0.1-0.1,0.2c0,0.1-0.1,0.1-0.1,0.1c0,0-0.1,0.1-0.2,0.1l-12.6,1.7c-0.4,0-0.5-0.5-0.3-1l-7.5-6.2
|
||||||
|
l8.2,5.7c0,0,0.1,0,0.3,0l11.9-1.6c0.1,0,0.1,0,0.2,0c0,0,0.1,0,0.1,0.1c0,0,0.1,0.1,0.1,0.1c0,0.1,0,0.1,0,0.2V265.7
|
||||||
|
L-145.2,265.7z M-165.1,245.8c-0.1,0-0.2,0-0.2,0.1c-0.1,0-0.2,0-0.3,0.1c-0.1,0-0.1,0-0.2,0c-0.1,0-0.1,0-0.2,0c0,0,0,0,0,0
|
||||||
|
c0,0,0,0,0,0c0,0,0,0,0,0c-0.1,0-0.2,0-0.3,0c-0.1,0-0.1,0-0.2,0h0c-0.1,0-0.2,0-0.3,0c-0.1,0-0.2,0-0.3,0c0,0,0,0,0,0
|
||||||
|
c0,0,0,0,0,0c-0.2,0-0.4-0.1-0.6-0.1c0,0-0.1,0-0.1,0c0,0,0,0,0,0c0,0-0.1,0-0.2,0c0,0-0.1,0-0.1-0.1c0,0-0.1,0-0.1,0
|
||||||
|
c0,0,0,0,0,0c-0.1,0-0.1,0-0.2-0.1c0,0,0,0,0,0c0,0,0,0,0,0c0,0-0.1,0-0.1,0c0,0,0,0,0,0h0c-0.1,0-0.1-0.1-0.2-0.1c0,0,0,0,0,0
|
||||||
|
c0,0-0.1-0.1-0.1-0.1c0,0,0,0,0,0l0,0c0,0-0.1,0-0.1-0.1c0,0,0,0-0.1,0c0,0,3.1-0.1,3.1-0.1s-1.3-0.8-1.6-1.1
|
||||||
|
c-0.5-0.5,0.7-2.9,0.8-3.2l-2.4-1.1l-0.5-0.2l0.6,0.1l2.7,0.3c-0.2-0.3-1.6-0.6-2.3-0.9c-0.3-0.1-0.4-0.2-0.3-0.3c0,0,0,0,0,0
|
||||||
|
c0.1,0,0.3,0,0.5,0.1c0.4,0,0.9,0,1.6,0c1.4,0,2.3-0.1,2.9-0.2c0,0,0,0.1,0.1,0.1c0.2,0.4,0.4,1,0.6,1.6
|
||||||
|
c0.2,0.6,0.4,1.2,0.4,1.8c0.1,0.6,0.1,1.2-0.1,1.7h0c-0.1,0-0.2,0-0.3,0c-0.1,0-0.2-0.1-0.4-0.1c-0.1,0-0.2-0.1-0.3-0.1
|
||||||
|
c0,0,0,0,0,0c0,0,0,0,0,0c-0.1,0-0.2-0.1-0.3-0.1c0,0,0-0.1,0-0.2c0-0.1,0-0.2-0.1-0.2c0-0.1,0-0.2-0.1-0.3
|
||||||
|
c0-0.1-0.1-0.2-0.1-0.2c0-0.1-0.1-0.2-0.1-0.3c-0.1-0.1-0.1-0.1-0.2-0.2c-0.1-0.1-0.1-0.1-0.2-0.1c-0.1,0-0.1,0-0.2,0
|
||||||
|
c-0.2,0-0.3,0.1-0.4,0.2c-0.1,0.1-0.3,0.3-0.4,0.4c-0.1,0.2-0.2,0.3-0.2,0.5c-0.1,0.2-0.1,0.3-0.1,0.4c0,0.1,0.1,0.2,0.2,0.3
|
||||||
|
c0.1,0.1,0.3,0.2,0.4,0.3c0.1,0.1,0.2,0.1,0.3,0.2c0.1,0,0.1,0.1,0.2,0.1c0.2,0.1,0.4,0.2,0.5,0.2c0,0.1,0,0.2,0,0.3
|
||||||
|
c0,0.1,0,0.2,0,0.4c0,0.1,0,0.2-0.1,0.3c0,0.1-0.1,0.2-0.2,0.2C-165,245.7-165.1,245.8-165.1,245.8L-165.1,245.8z
|
||||||
|
M-163.9,235.5l0,0.3c-0.1,0-0.1,0.1-0.2,0.1c-0.7,0.4-1.5,0.6-1.6,0.6c-0.1,0-0.7,0.1-1.2,0.1c-0.3,0-0.5,0-0.7,0
|
||||||
|
c-0.3,0-0.4,0-0.5,0c-0.1,0-0.8-0.1-1.4-0.3l0-0.4c0.6,0,1.3,0,1.4,0c0,0,0.2,0,0.5,0c0.2,0,0.4,0,0.7,0c0.6,0,1.1-0.1,1.2-0.1
|
||||||
|
c0.2,0,0.9-0.1,1.6-0.3C-164.1,235.5-164,235.5-163.9,235.5L-163.9,235.5z M-164.2,234.4c-0.7,0.2-1.4,0.4-1.5,0.4
|
||||||
|
c-0.1,0-0.6,0.1-1.1,0.1c-0.3,0-0.6,0-0.8,0c-0.2,0-0.3,0-0.3,0c-0.1,0-0.8-0.1-1.5-0.3c0,0-0.1,0-0.1,0l0-0.3c0,0,0.1,0,0.1,0
|
||||||
|
c0.7,0,1.4-0.1,1.5-0.1c0,0,0.2,0,0.3,0c0.2,0,0.5,0,0.8,0c0.5,0,1-0.1,1.1-0.1c0.1,0,0.8,0,1.5,0c0.1,0,0.2,0,0.3,0l0,0.3
|
||||||
|
C-164,234.3-164.1,234.4-164.2,234.4L-164.2,234.4z M-169.7,232.6c0.7-0.1,1.7-0.3,1.9-0.3l0.1,0l2,0c0.2,0,1.2,0.1,1.9,0.2
|
||||||
|
v0.2c-0.7,0.1-1.7,0.3-1.9,0.3l-2,0l-0.1,0c-0.2,0-1.2-0.1-1.9-0.2V232.6L-169.7,232.6z M-163.8,231v0.3c-0.1,0-0.2,0-0.4,0
|
||||||
|
c-0.7,0-1.5,0-1.6,0c-0.1,0-0.6,0-1.2,0c-0.3,0-0.5,0-0.7,0c-0.2,0-0.4,0-0.5,0c-0.2,0-0.9-0.1-1.6,0v-0.4
|
||||||
|
c0.7-0.2,1.4-0.3,1.6-0.4c0,0,0.2,0,0.5,0c0.2,0,0.5,0,0.7,0c0.6,0,1.1,0,1.2,0c0.2,0,0.9,0.1,1.6,0.4
|
||||||
|
C-164,230.9-163.9,230.9-163.8,231L-163.8,231z M-164.1,229.6c-0.7-0.2-1.6-0.2-1.7-0.2c-0.1,0-0.7-0.1-1.3-0.1
|
||||||
|
c-0.2,0-0.4,0-0.6,0c-0.4,0-0.6,0-0.7,0l-0.6,0c-0.2,0-0.5,0-0.7,0.1V229c0.2-0.1,0.5-0.1,0.7-0.2c0.3-0.1,0.6-0.1,0.7-0.1
|
||||||
|
c0.1,0,0.3,0,0.7,0c0.2,0,0.4,0,0.6,0c0.6,0,1.2,0.1,1.3,0.1c0.2,0,1,0.2,1.7,0.5c0.1,0,0.2,0.1,0.3,0.1v0.2
|
||||||
|
C-163.9,229.6-164,229.6-164.1,229.6L-164.1,229.6z M-170.2,226.5c0-0.1,0-0.4,0-0.4c0.2-1.6,4.1-6,4.1-6s1.7-0.5,2.4-0.5
|
||||||
|
c0.5,0,1,0.4,0.9,1.6c0,0.2-0.4,5-0.4,5c0,0,0,0.1-0.1,0.1c0,0-0.1,0.1-0.1,0.1c-0.7,0.6-5.9,0.6-6.4,0.5
|
||||||
|
C-170.1,226.9-170.2,226.7-170.2,226.5L-170.2,226.5z M-151.5,257.2l0-6.5c0,0,0.1,0,0.4,0l0,2.2c0.7-0.6,1.5-1.2,2.4-1
|
||||||
|
c0.5,0.1,0.9,0.3,1.3,0.6v4.4L-151.5,257.2L-151.5,257.2z M-154.7,253.4c0.6,0.5,1.4,0.9,2.3,0.4l0.4-0.2l-0.4,3.7l-3.5,0.4
|
||||||
|
v-5.6l0-1c0,0,0.2,0,0.5,0l0,3.1C-155.4,253.6-155,253.2-154.7,253.4L-154.7,253.4z M-158.1,262.2c0,0,0-0.3,0.1-0.4
|
||||||
|
c0-0.1,0-0.2,0.1-0.3c0.1-0.3,0.3-0.6,0.5-0.8c0.2-0.2,0.4-0.5,0.7-0.6c0.3-0.2,0.5-0.3,0.8-0.3l7.9-0.9c0.3,0,0.6,0,0.8,0.1
|
||||||
|
c0.2,0.1,0.5,0.2,0.7,0.4c0.2,0.2,0.3,0.4,0.4,0.7c0,0.1,0,0.1,0.1,0.2c0.1,0.2,0.1,0.5,0.1,0.7v3.2l-0.4,0l-10.1,1.3l-0.5,0.1
|
||||||
|
c0,0-1,0.1-1.2-0.6v-1.7c-0.4-0.3-6.4-5.2-6.4-5.2L-158.1,262.2L-158.1,262.2z M-158.8,206.5l1.2-0.1l0.5,0c0,0,0,0,0.1,0
|
||||||
|
c0,0,0,0,0.1,0c0.1,0,0.2,0,0.3,0.1c0.6,0.4,1,1.7,1,3.4c0,2-0.6,3.6-1.3,3.6v0h0c0,0,0,0,0,0h0c0,0,0,0,0,0h0l-1.3,0l-1.6,0.1
|
||||||
|
l-0.3,0c0,0,0,0,0.1,0c0.4-0.1,0.7-0.8,0.9-1.8c0.1-0.5,0.1-1.1,0.1-1.7c0-0.7-0.1-1.3-0.2-1.8c-0.2-1-0.6-1.6-0.9-1.6
|
||||||
|
L-158.8,206.5L-158.8,206.5z M-153.3,196.9c0.4-0.1,0.9-0.1,0.9,0.3c0,0.4,0,1.3,0,1.8h0c-0.3,0.1-0.6,0.3-0.9,0.4V196.9
|
||||||
|
L-153.3,196.9z M-151.5,200.9c0.3-0.1,0.7-0.2,1-0.1l8.4,0.5l2.8,0.2l0.6,0c0.3,0,0.7,0.1,1,0.2c0.3,0.1,0.6,0.3,0.8,0.5
|
||||||
|
c0.2,0.2,0.4,0.5,0.5,0.8c0.1,0.3,0.2,0.6,0.2,0.9v10.7c0,0.3-0.1,0.6-0.2,0.9c-0.1,0.3-0.3,0.5-0.5,0.7l-3.9-0.1l-11.7-0.2
|
||||||
|
c-0.2-0.2-0.3-0.4-0.4-0.6c-0.1-0.3-0.2-0.6-0.2-1v-11.2c0-0.3,0.1-0.7,0.2-0.9c0.1-0.3,0.3-0.5,0.6-0.7
|
||||||
|
C-152.2,201.2-151.9,201-151.5,200.9L-151.5,200.9z M-139.3,197c0.4-0.1,0.8-0.1,0.8,0.3c0,0.5,0,1.6,0,2.1c0,0,0,0,0,0l-0.8,0
|
||||||
|
V197L-139.3,197z M-172.7,226.4c-0.2,1.8,0.9,2.2,0.9,2.2v5.8c0,0.1,0,0.1,0,0.2l0,1.2c0,0,0,0.1,0,0.1v0.7
|
||||||
|
c0,0-0.1,0.1-0.1,0.1c-0.4,0.4-0.7,0.9-1,1.4c-0.4,0.7-0.6,1.3-0.7,2c-0.1,0.9-0.1,1.7,0.2,2.5c0.1,0.3,0.3,0.7,0.5,1
|
||||||
|
c0.9,1.5,2,2.5,3,3.2c1.8,1.3,5,1,5,1v6.7c-0.1,0.1-0.1,0.1-0.2,0.2c0,0-0.1,0.1-0.1,0.1c-0.2,0.2-0.3,0.3-0.5,0.5
|
||||||
|
c0,0-0.1,0.1-0.1,0.1c-0.2,0.2-0.3,0.5-0.4,0.7c0,0.1-0.1,0.1-0.1,0.2c-0.1,0.2-0.2,0.4-0.2,0.6c0,0.1,0,0.2-0.1,0.3
|
||||||
|
c0,0.1,0,0.1,0,0.2c0,0.2,0,0.4,0,0.6v2.1c0,0,0,0,0,0c0,0-0.1,0.1-0.1,0.1c-0.2,0.2-0.3,0.4-0.4,0.7c0,0,0,0.1,0,0.1
|
||||||
|
c-0.1,0.3-0.1,0.5-0.1,0.8v0.4c0,0.1,0,0.3,0,0.4c0,0.1,0.1,0.3,0.1,0.4c0.1,0.1,0.1,0.3,0.2,0.4c0.1,0.1,0.2,0.3,0.3,0.4
|
||||||
|
l6.1,5.5c0,0,0,0,0.1,0.1c0.3,0.2,0.6,0.4,0.9,0.5c0.3,0.1,0.7,0.1,1,0.1c0.1,0,0.2,0,0.3,0l12.9-1.8c0.4,0,0.7-0.2,1-0.3
|
||||||
|
c0.4,0.1,0.7,0.1,1.1,0l11.8-1.6c0.4-0.1,0.8-0.2,1.2-0.4c0.3-0.2,0.5-0.4,0.7-0.7c0.2-0.2,0.3-0.5,0.4-0.8
|
||||||
|
c0.1-0.3,0.1-0.5,0.1-0.8v-0.4c0-0.3-0.1-0.6-0.2-0.8c-0.1-0.3-0.3-0.5-0.5-0.8c0,0,0,0-0.1-0.1v-2.4c0-1.4-0.4-3.3-2.2-3.8
|
||||||
|
v-6.8c0.6-0.1,3.7-0.4,3.7-2.7c0.9-0.1,3.4-0.6,4.4-4.2c0.2-0.8,0.3-1.7,0.2-2.6c-0.1-0.7-0.3-1.4-0.6-2
|
||||||
|
c-0.2-0.5-0.5-1-0.9-1.4c-0.1-0.1-0.1-0.1-0.2-0.2v-8.3c0.2-0.1,0.8-0.4,0.8-1.7c0-0.8-1.9-5.4-2.5-6.1c-0.7-0.8-2-1.8-2.8-2.1
|
||||||
|
c-0.2-0.1-0.4-0.2-0.5-0.2c-0.6-0.2-1.2-0.3-1.8-0.3c-0.1-0.3-0.4-0.5-0.7-0.5l-1.1,0v-2.6c1.2-0.5,1.9-1.7,1.9-3.3
|
||||||
|
c0-0.7-0.1-2.5-1.2-3.4c-0.2-0.2-0.4-0.3-0.6-0.4v-2.5l0-0.2c0-0.6-0.1-1.1-0.3-1.6c-0.2-0.5-0.5-1-0.9-1.4
|
||||||
|
c-0.4-0.4-0.9-0.8-1.4-1c-0.3-0.2-0.7-0.3-1-0.3V197c0-0.5-0.9-1.3-3.8-0.3c0,0,0,0,0,0c-0.4,0.1-0.6,0.5-0.6,0.8v1.7l-8.2-0.5
|
||||||
|
c-0.4,0-0.8,0-1.2,0.1v-1.9c0-0.5-0.9-1.3-3.8-0.3c-0.4,0.1-0.7,0.5-0.7,0.8v3.9c0,0.1,0,0.3,0,0.3l-3.8,3.1
|
||||||
|
c-0.4,0-0.9,0.1-1.5,0.2c-1.9,0.4-2.6,3.1-2.6,5.3c0,2,0.5,3.5,1.3,4.4v2.3l-3.8,1.1C-167.3,218.3-172.5,224.6-172.7,226.4
|
||||||
|
L-172.7,226.4z"/>
|
||||||
|
<path id="Fill-30" fill="#000000" d="M-157,265.1c0.5,0,4.2-0.5,4.2-0.5s-0.8-0.2-0.5-1.6c0.2-1,2.1-1.4,3.4-1.7
|
||||||
|
c1.3-0.3,3.1-0.5,3.1-0.5s-1.1-0.3-2.7-0.1l-0.3,0l-0.3,0h0c0,0-5.7,0.7-7.1,1.3c-0.4,0.2-0.4,1.9-0.4,2.7
|
||||||
|
C-157.6,265.1-157.3,265.1-157,265.1"/>
|
||||||
|
<path id="Fill-31" fill="#000000" d="M-156,260.4c-0.6,0.1-1,0.9-1,0.9l5-0.7C-152.1,260.5-153.5,260.1-156,260.4"/>
|
||||||
|
<path id="Fill-32" fill="#000000" d="M-138.6,258.8c0,0-1.4-0.4-2.4-0.3c-0.9,0.1-1.2,1-1.2,1
|
||||||
|
C-141.7,259-138.7,258.8-138.6,258.8"/>
|
||||||
|
<path id="Fill-33" fill="#000000" d="M-165.5,226.1c0,0-1.8-1-1.5-1.6c0.5-0.9,1.9-0.6,2.4-1.2c0.8-1,0.6-2.5,0.6-2.5
|
||||||
|
s-1.7-0.3-2.2,0.3c-0.8,1-3,4.2-3.2,4.6C-170.2,226.7-165.5,226.1-165.5,226.1"/>
|
||||||
|
<path id="Fill-34" fill="#000000" d="M-157,212.9c0.2,0,0.5-0.6,0.7-1.7c0,0-0.1,0-0.1,0c-0.1-0.1-0.3-0.1-0.4-0.2
|
||||||
|
c-0.1,0-0.1-0.1-0.2-0.1c-0.2-0.2-0.2-0.4-0.4-0.6c-0.3-0.2-0.6-0.2-0.9-0.1c0,0-0.3,0.1-0.4-0.2c0,0.2,0,0.3,0,0.5
|
||||||
|
c0,0.5-0.1,1.1-0.2,1.4c-0.1,0.3-0.1,0.6-0.2,0.8c0,0.1-0.1,0.2-0.1,0.3L-157,212.9C-157.1,212.9-157.1,212.9-157,212.9"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 17 KiB |
93
plugins/Toolbox/resources/qml/Toolbox.qml
Normal file
93
plugins/Toolbox/resources/qml/Toolbox.qml
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
// Copyright (c) 2018 Ultimaker B.V.
|
||||||
|
// Toolbox is released under the terms of the LGPLv3 or higher.
|
||||||
|
|
||||||
|
import QtQuick 2.2
|
||||||
|
import QtQuick.Dialogs 1.1
|
||||||
|
import QtQuick.Window 2.2
|
||||||
|
import UM 1.1 as UM
|
||||||
|
|
||||||
|
Window
|
||||||
|
{
|
||||||
|
id: base
|
||||||
|
property var selection: null
|
||||||
|
title: catalog.i18nc("@title", "Toolbox")
|
||||||
|
modality: Qt.ApplicationModal
|
||||||
|
width: 720 * screenScaleFactor
|
||||||
|
height: 640 * screenScaleFactor
|
||||||
|
minimumWidth: 720 * screenScaleFactor
|
||||||
|
maximumWidth: 720 * screenScaleFactor
|
||||||
|
minimumHeight: 350 * screenScaleFactor
|
||||||
|
color: UM.Theme.getColor("sidebar")
|
||||||
|
UM.I18nCatalog
|
||||||
|
{
|
||||||
|
id: catalog
|
||||||
|
name:"cura"
|
||||||
|
}
|
||||||
|
Item
|
||||||
|
{
|
||||||
|
anchors.fill: parent
|
||||||
|
ToolboxHeader
|
||||||
|
{
|
||||||
|
id: header
|
||||||
|
}
|
||||||
|
Item
|
||||||
|
{
|
||||||
|
id: mainView
|
||||||
|
width: parent.width
|
||||||
|
z: -1
|
||||||
|
anchors
|
||||||
|
{
|
||||||
|
top: header.bottom
|
||||||
|
bottom: footer.top
|
||||||
|
}
|
||||||
|
// TODO: This could be improved using viewFilter instead of viewCategory
|
||||||
|
ToolboxLoadingPage
|
||||||
|
{
|
||||||
|
id: viewLoading
|
||||||
|
visible: toolbox.viewCategory != "installed" && toolbox.viewPage == "loading"
|
||||||
|
}
|
||||||
|
ToolboxDownloadsPage
|
||||||
|
{
|
||||||
|
id: viewDownloads
|
||||||
|
visible: toolbox.viewCategory != "installed" && toolbox.viewPage == "overview"
|
||||||
|
}
|
||||||
|
ToolboxDetailPage
|
||||||
|
{
|
||||||
|
id: viewDetail
|
||||||
|
visible: toolbox.viewCategory != "installed" && toolbox.viewPage == "detail"
|
||||||
|
}
|
||||||
|
ToolboxAuthorPage
|
||||||
|
{
|
||||||
|
id: viewAuthor
|
||||||
|
visible: toolbox.viewCategory != "installed" && toolbox.viewPage == "author"
|
||||||
|
}
|
||||||
|
ToolboxInstalledPage
|
||||||
|
{
|
||||||
|
id: installedPluginList
|
||||||
|
visible: toolbox.viewCategory == "installed"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ToolboxFooter
|
||||||
|
{
|
||||||
|
id: footer
|
||||||
|
visible: toolbox.restartRequired
|
||||||
|
height: toolbox.restartRequired ? UM.Theme.getSize("toolbox_footer").height : 0
|
||||||
|
}
|
||||||
|
// TODO: Clean this up:
|
||||||
|
Connections
|
||||||
|
{
|
||||||
|
target: toolbox
|
||||||
|
onShowLicenseDialog:
|
||||||
|
{
|
||||||
|
licenseDialog.pluginName = toolbox.getLicenseDialogPluginName();
|
||||||
|
licenseDialog.licenseContent = toolbox.getLicenseDialogLicenseContent();
|
||||||
|
licenseDialog.pluginFileLocation = toolbox.getLicenseDialogPluginFileLocation();
|
||||||
|
licenseDialog.show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ToolboxLicenseDialog
|
||||||
|
{
|
||||||
|
id: licenseDialog
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
138
plugins/Toolbox/resources/qml/ToolboxAuthorPage.qml
Normal file
138
plugins/Toolbox/resources/qml/ToolboxAuthorPage.qml
Normal file
|
@ -0,0 +1,138 @@
|
||||||
|
// Copyright (c) 2018 Ultimaker B.V.
|
||||||
|
// Toolbox 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.1 as UM
|
||||||
|
|
||||||
|
Item
|
||||||
|
{
|
||||||
|
id: page
|
||||||
|
property var details: base.selection
|
||||||
|
anchors.fill: parent
|
||||||
|
ToolboxBackColumn
|
||||||
|
{
|
||||||
|
id: sidebar
|
||||||
|
}
|
||||||
|
Rectangle
|
||||||
|
{
|
||||||
|
id: header
|
||||||
|
anchors
|
||||||
|
{
|
||||||
|
left: sidebar.right
|
||||||
|
right: parent.right
|
||||||
|
rightMargin: UM.Theme.getSize("wide_margin").width
|
||||||
|
}
|
||||||
|
height: UM.Theme.getSize("toolbox_detail_header").height
|
||||||
|
Image
|
||||||
|
{
|
||||||
|
id: thumbnail
|
||||||
|
width: UM.Theme.getSize("toolbox_thumbnail_medium").width
|
||||||
|
height: UM.Theme.getSize("toolbox_thumbnail_medium").height
|
||||||
|
fillMode: Image.PreserveAspectFit
|
||||||
|
source: details.icon_url || "../images/logobot.svg"
|
||||||
|
anchors
|
||||||
|
{
|
||||||
|
top: parent.top
|
||||||
|
left: parent.left
|
||||||
|
leftMargin: UM.Theme.getSize("wide_margin").width
|
||||||
|
topMargin: UM.Theme.getSize("wide_margin").height
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Label
|
||||||
|
{
|
||||||
|
id: title
|
||||||
|
anchors
|
||||||
|
{
|
||||||
|
top: thumbnail.top
|
||||||
|
left: thumbnail.right
|
||||||
|
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.name
|
||||||
|
font: UM.Theme.getFont("large")
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
|
width: parent.width
|
||||||
|
height: UM.Theme.getSize("toolbox_property_label").height
|
||||||
|
}
|
||||||
|
Label
|
||||||
|
{
|
||||||
|
id: description
|
||||||
|
text: details.description
|
||||||
|
anchors
|
||||||
|
{
|
||||||
|
top: title.bottom
|
||||||
|
left: title.left
|
||||||
|
topMargin: UM.Theme.getSize("default_margin").height
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Column
|
||||||
|
{
|
||||||
|
id: properties
|
||||||
|
anchors
|
||||||
|
{
|
||||||
|
top: description.bottom
|
||||||
|
left: description.left
|
||||||
|
topMargin: UM.Theme.getSize("default_margin").height
|
||||||
|
}
|
||||||
|
spacing: Math.floor(UM.Theme.getSize("narrow_margin").height)
|
||||||
|
width: childrenRect.width
|
||||||
|
Label
|
||||||
|
{
|
||||||
|
text: catalog.i18nc("@label", "Contact") + ":"
|
||||||
|
font: UM.Theme.getFont("very_small")
|
||||||
|
color: UM.Theme.getColor("text_medium")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Column
|
||||||
|
{
|
||||||
|
id: values
|
||||||
|
anchors
|
||||||
|
{
|
||||||
|
top: description.bottom
|
||||||
|
left: properties.right
|
||||||
|
leftMargin: UM.Theme.getSize("default_margin").width
|
||||||
|
topMargin: UM.Theme.getSize("default_margin").height
|
||||||
|
}
|
||||||
|
spacing: Math.floor(UM.Theme.getSize("narrow_margin").height)
|
||||||
|
Label
|
||||||
|
{
|
||||||
|
text:
|
||||||
|
{
|
||||||
|
if (details.email)
|
||||||
|
{
|
||||||
|
return "<a href=\"mailto:"+details.email+"\">"+details.name+"</a>"
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return "<a href=\""+details.website+"\">"+details.name+"</a>"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
font: UM.Theme.getFont("very_small")
|
||||||
|
color: UM.Theme.getColor("text")
|
||||||
|
onLinkActivated: Qt.openUrlExternally(link)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Rectangle
|
||||||
|
{
|
||||||
|
color: UM.Theme.getColor("lining")
|
||||||
|
width: parent.width
|
||||||
|
height: UM.Theme.getSize("default_lining").height
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ToolboxDetailList
|
||||||
|
{
|
||||||
|
anchors
|
||||||
|
{
|
||||||
|
top: header.bottom
|
||||||
|
bottom: page.bottom
|
||||||
|
left: header.left
|
||||||
|
right: page.right
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
69
plugins/Toolbox/resources/qml/ToolboxBackColumn.qml
Normal file
69
plugins/Toolbox/resources/qml/ToolboxBackColumn.qml
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
// Copyright (c) 2018 Ultimaker B.V.
|
||||||
|
// Toolbox 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.1 as UM
|
||||||
|
|
||||||
|
Item
|
||||||
|
{
|
||||||
|
id: sidebar
|
||||||
|
height: parent.height
|
||||||
|
width: UM.Theme.getSize("toolbox_back_column").width
|
||||||
|
anchors
|
||||||
|
{
|
||||||
|
top: parent.top
|
||||||
|
left: parent.left
|
||||||
|
topMargin: UM.Theme.getSize("wide_margin").height
|
||||||
|
leftMargin: UM.Theme.getSize("default_margin").width
|
||||||
|
rightMargin: UM.Theme.getSize("default_margin").width
|
||||||
|
}
|
||||||
|
Button
|
||||||
|
{
|
||||||
|
id: button
|
||||||
|
text: catalog.i18nc("@action:button", "Back")
|
||||||
|
UM.RecolorImage
|
||||||
|
{
|
||||||
|
id: backArrow
|
||||||
|
anchors
|
||||||
|
{
|
||||||
|
verticalCenter: parent.verticalCenter
|
||||||
|
left: parent.left
|
||||||
|
rightMargin: UM.Theme.getSize("default_margin").width
|
||||||
|
}
|
||||||
|
width: UM.Theme.getSize("standard_arrow").width
|
||||||
|
height: UM.Theme.getSize("standard_arrow").height
|
||||||
|
sourceSize
|
||||||
|
{
|
||||||
|
width: width
|
||||||
|
height: height
|
||||||
|
}
|
||||||
|
color: button.hovered ? UM.Theme.getColor("primary") : UM.Theme.getColor("text")
|
||||||
|
source: UM.Theme.getIcon("arrow_left")
|
||||||
|
}
|
||||||
|
width: UM.Theme.getSize("toolbox_back_button").width
|
||||||
|
height: UM.Theme.getSize("toolbox_back_button").height
|
||||||
|
onClicked:
|
||||||
|
{
|
||||||
|
toolbox.viewPage = "overview"
|
||||||
|
toolbox.filterModelByProp("packages", "type", toolbox.viewCategory)
|
||||||
|
}
|
||||||
|
style: ButtonStyle
|
||||||
|
{
|
||||||
|
background: Rectangle
|
||||||
|
{
|
||||||
|
color: "transparent"
|
||||||
|
}
|
||||||
|
label: Label
|
||||||
|
{
|
||||||
|
id: labelStyle
|
||||||
|
text: control.text
|
||||||
|
color: control.hovered ? UM.Theme.getColor("primary") : UM.Theme.getColor("text")
|
||||||
|
font: UM.Theme.getFont("default_bold")
|
||||||
|
horizontalAlignment: Text.AlignRight
|
||||||
|
width: control.width
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
35
plugins/Toolbox/resources/qml/ToolboxDetailList.qml
Normal file
35
plugins/Toolbox/resources/qml/ToolboxDetailList.qml
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
// Copyright (c) 2018 Ultimaker B.V.
|
||||||
|
// Toolbox 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.1 as UM
|
||||||
|
|
||||||
|
Item
|
||||||
|
{
|
||||||
|
id: detailList
|
||||||
|
ScrollView
|
||||||
|
{
|
||||||
|
frameVisible: false
|
||||||
|
anchors.fill: detailList
|
||||||
|
style: UM.Theme.styles.scrollview
|
||||||
|
Column
|
||||||
|
{
|
||||||
|
anchors
|
||||||
|
{
|
||||||
|
right: parent.right
|
||||||
|
topMargin: UM.Theme.getSize("wide_margin").height
|
||||||
|
bottomMargin: UM.Theme.getSize("wide_margin").height
|
||||||
|
top: parent.top
|
||||||
|
}
|
||||||
|
height: childrenRect.height + 2 * UM.Theme.getSize("wide_margin").height
|
||||||
|
spacing: UM.Theme.getSize("default_margin").height
|
||||||
|
Repeater
|
||||||
|
{
|
||||||
|
model: toolbox.packagesModel
|
||||||
|
delegate: ToolboxDetailTile {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
157
plugins/Toolbox/resources/qml/ToolboxDetailPage.qml
Normal file
157
plugins/Toolbox/resources/qml/ToolboxDetailPage.qml
Normal file
|
@ -0,0 +1,157 @@
|
||||||
|
// Copyright (c) 2018 Ultimaker B.V.
|
||||||
|
// Toolbox 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.1 as UM
|
||||||
|
|
||||||
|
Item
|
||||||
|
{
|
||||||
|
id: page
|
||||||
|
property var details: base.selection
|
||||||
|
anchors.fill: parent
|
||||||
|
width: parent.width
|
||||||
|
ToolboxBackColumn
|
||||||
|
{
|
||||||
|
id: sidebar
|
||||||
|
}
|
||||||
|
Item
|
||||||
|
{
|
||||||
|
id: header
|
||||||
|
anchors
|
||||||
|
{
|
||||||
|
left: sidebar.right
|
||||||
|
right: parent.right
|
||||||
|
rightMargin: UM.Theme.getSize("wide_margin").width
|
||||||
|
}
|
||||||
|
height: UM.Theme.getSize("toolbox_detail_header").height
|
||||||
|
Image
|
||||||
|
{
|
||||||
|
id: thumbnail
|
||||||
|
width: UM.Theme.getSize("toolbox_thumbnail_medium").width
|
||||||
|
height: UM.Theme.getSize("toolbox_thumbnail_medium").height
|
||||||
|
fillMode: Image.PreserveAspectFit
|
||||||
|
source: details.icon_url || "../images/logobot.svg"
|
||||||
|
anchors
|
||||||
|
{
|
||||||
|
top: parent.top
|
||||||
|
left: parent.left
|
||||||
|
leftMargin: UM.Theme.getSize("wide_margin").width
|
||||||
|
topMargin: UM.Theme.getSize("wide_margin").height
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Label
|
||||||
|
{
|
||||||
|
id: title
|
||||||
|
anchors
|
||||||
|
{
|
||||||
|
top: thumbnail.top
|
||||||
|
left: thumbnail.right
|
||||||
|
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.name
|
||||||
|
font: UM.Theme.getFont("large")
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
|
width: parent.width
|
||||||
|
height: UM.Theme.getSize("toolbox_property_label").height
|
||||||
|
}
|
||||||
|
|
||||||
|
Column
|
||||||
|
{
|
||||||
|
id: properties
|
||||||
|
anchors
|
||||||
|
{
|
||||||
|
top: title.bottom
|
||||||
|
left: title.left
|
||||||
|
topMargin: UM.Theme.getSize("default_margin").height
|
||||||
|
}
|
||||||
|
spacing: Math.floor(UM.Theme.getSize("narrow_margin").height)
|
||||||
|
width: childrenRect.width
|
||||||
|
Label
|
||||||
|
{
|
||||||
|
text: catalog.i18nc("@label", "Version") + ":"
|
||||||
|
font: UM.Theme.getFont("very_small")
|
||||||
|
color: UM.Theme.getColor("text_medium")
|
||||||
|
}
|
||||||
|
Label
|
||||||
|
{
|
||||||
|
text: catalog.i18nc("@label", "Last updated") + ":"
|
||||||
|
font: UM.Theme.getFont("very_small")
|
||||||
|
color: UM.Theme.getColor("text_medium")
|
||||||
|
}
|
||||||
|
Label
|
||||||
|
{
|
||||||
|
text: catalog.i18nc("@label", "Author") + ":"
|
||||||
|
font: UM.Theme.getFont("very_small")
|
||||||
|
color: UM.Theme.getColor("text_medium")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Column
|
||||||
|
{
|
||||||
|
id: values
|
||||||
|
anchors
|
||||||
|
{
|
||||||
|
top: title.bottom
|
||||||
|
left: properties.right
|
||||||
|
leftMargin: UM.Theme.getSize("default_margin").width
|
||||||
|
topMargin: UM.Theme.getSize("default_margin").height
|
||||||
|
}
|
||||||
|
spacing: Math.floor(UM.Theme.getSize("narrow_margin").height)
|
||||||
|
Label
|
||||||
|
{
|
||||||
|
text: details.version
|
||||||
|
font: UM.Theme.getFont("very_small")
|
||||||
|
color: UM.Theme.getColor("text")
|
||||||
|
}
|
||||||
|
Label
|
||||||
|
{
|
||||||
|
text:
|
||||||
|
{
|
||||||
|
var date = new Date(details.last_updated)
|
||||||
|
return date.toLocaleString(Qt.locale())
|
||||||
|
}
|
||||||
|
font: UM.Theme.getFont("very_small")
|
||||||
|
color: UM.Theme.getColor("text")
|
||||||
|
}
|
||||||
|
Label
|
||||||
|
{
|
||||||
|
text:
|
||||||
|
{
|
||||||
|
if (details.author_email)
|
||||||
|
{
|
||||||
|
return "<a href=\"mailto:" + details.author_email+"?Subject=Cura: " + details.name + "\">" + details.author_name + "</a>"
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return "<a href=\"" + details.website + "\">" + details.author_name + "</a>"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
font: UM.Theme.getFont("very_small")
|
||||||
|
color: UM.Theme.getColor("text")
|
||||||
|
onLinkActivated: Qt.openUrlExternally(link)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Rectangle
|
||||||
|
{
|
||||||
|
color: UM.Theme.getColor("lining")
|
||||||
|
width: parent.width
|
||||||
|
height: UM.Theme.getSize("default_lining").height
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ToolboxDetailList
|
||||||
|
{
|
||||||
|
anchors
|
||||||
|
{
|
||||||
|
top: header.bottom
|
||||||
|
bottom: page.bottom
|
||||||
|
left: header.left
|
||||||
|
right: page.right
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
274
plugins/Toolbox/resources/qml/ToolboxDetailTile.qml
Normal file
274
plugins/Toolbox/resources/qml/ToolboxDetailTile.qml
Normal file
|
@ -0,0 +1,274 @@
|
||||||
|
// Copyright (c) 2018 Ultimaker B.V.
|
||||||
|
// Toolbox 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.1 as UM
|
||||||
|
|
||||||
|
Item
|
||||||
|
{
|
||||||
|
id: tile
|
||||||
|
property bool installed: toolbox.isInstalled(model.id)
|
||||||
|
width: detailList.width - UM.Theme.getSize("wide_margin").width
|
||||||
|
// TODO: Without this line, every instance of this object has 0 height. With
|
||||||
|
// it, QML spits out tons of bugs claiming a binding loop (not true). Why?
|
||||||
|
// Because QT is garbage.
|
||||||
|
height: Math.max( UM.Theme.getSize("toolbox_detail_tile").height, childrenRect.height + UM.Theme.getSize("default_margin").height)
|
||||||
|
Item
|
||||||
|
{
|
||||||
|
id: normalData
|
||||||
|
height: childrenRect.height
|
||||||
|
anchors
|
||||||
|
{
|
||||||
|
left: parent.left
|
||||||
|
right: controls.left
|
||||||
|
rightMargin: UM.Theme.getSize("default_margin").width
|
||||||
|
top: parent.top
|
||||||
|
}
|
||||||
|
Label
|
||||||
|
{
|
||||||
|
id: packageName
|
||||||
|
width: parent.width
|
||||||
|
height: UM.Theme.getSize("toolbox_property_label").height
|
||||||
|
text: model.name
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
|
color: UM.Theme.getColor("text")
|
||||||
|
font: UM.Theme.getFont("medium_bold")
|
||||||
|
}
|
||||||
|
Label
|
||||||
|
{
|
||||||
|
anchors.top: packageName.bottom
|
||||||
|
width: parent.width
|
||||||
|
text:
|
||||||
|
{
|
||||||
|
if (model.description.length > 235)
|
||||||
|
{
|
||||||
|
if (model.description.substring(234, 235) == " ")
|
||||||
|
{
|
||||||
|
return model.description.substring(0, 234) + "..."
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return model.description.substring(0, 235) + "..."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return model.description
|
||||||
|
}
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
|
color: UM.Theme.getColor("text")
|
||||||
|
font: UM.Theme.getFont("default")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Item
|
||||||
|
{
|
||||||
|
id: controls
|
||||||
|
anchors.right: tile.right
|
||||||
|
anchors.top: tile.top
|
||||||
|
width: childrenRect.width
|
||||||
|
height: childrenRect.height
|
||||||
|
Button
|
||||||
|
{
|
||||||
|
id: installButton
|
||||||
|
text:
|
||||||
|
{
|
||||||
|
if (installed)
|
||||||
|
{
|
||||||
|
return catalog.i18nc("@action:button", "Installed")
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if ( toolbox.isDownloading && toolbox.activePackage == model )
|
||||||
|
{
|
||||||
|
return catalog.i18nc("@action:button", "Cancel")
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return catalog.i18nc("@action:button", "Install")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
enabled:
|
||||||
|
{
|
||||||
|
if (installed)
|
||||||
|
{
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if ( toolbox.isDownloading )
|
||||||
|
{
|
||||||
|
return toolbox.activePackage == model ? true : false
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
opacity: enabled ? 1.0 : 0.5
|
||||||
|
style: ButtonStyle
|
||||||
|
{
|
||||||
|
background: Rectangle
|
||||||
|
{
|
||||||
|
implicitWidth: 96
|
||||||
|
implicitHeight: 30
|
||||||
|
color:
|
||||||
|
{
|
||||||
|
if (installed)
|
||||||
|
{
|
||||||
|
return UM.Theme.getColor("action_button_disabled")
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if ( control.hovered )
|
||||||
|
{
|
||||||
|
return UM.Theme.getColor("primary_hover")
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return UM.Theme.getColor("primary")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
label: Label
|
||||||
|
{
|
||||||
|
text: control.text
|
||||||
|
color:
|
||||||
|
{
|
||||||
|
if (installed)
|
||||||
|
{
|
||||||
|
return UM.Theme.getColor("action_button_disabled_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: UM.Theme.getFont("default_bold")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onClicked:
|
||||||
|
{
|
||||||
|
if (installed)
|
||||||
|
{
|
||||||
|
toolbox.viewCategory = "installed"
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// if ( toolbox.isDownloading && toolbox.activePackage == model )
|
||||||
|
if ( toolbox.isDownloading )
|
||||||
|
{
|
||||||
|
toolbox.cancelDownload();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
toolbox.activePackage = model
|
||||||
|
// toolbox.activePackage = model;
|
||||||
|
if ( model.can_upgrade )
|
||||||
|
{
|
||||||
|
// toolbox.downloadAndInstallPlugin( model.update_url );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
toolbox.startDownload( model.download_url );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item
|
||||||
|
{
|
||||||
|
id: supportedConfigsChart
|
||||||
|
anchors.top: normalData.bottom
|
||||||
|
anchors.topMargin: UM.Theme.getSize("default_margin").height
|
||||||
|
height: visible ? childrenRect.height : 0
|
||||||
|
width: normalData.width
|
||||||
|
visible: model.type == "material" && model.supported_configs.length > 0
|
||||||
|
Label
|
||||||
|
{
|
||||||
|
id: compatibilityHeading
|
||||||
|
anchors.topMargin: UM.Theme.getSize("default_margin").height
|
||||||
|
width: parent.width
|
||||||
|
text: catalog.i18nc("@label", "Compatibility")
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
|
color: UM.Theme.getColor("text_medium")
|
||||||
|
font: UM.Theme.getFont("default")
|
||||||
|
}
|
||||||
|
Column
|
||||||
|
{
|
||||||
|
id: compatibilityLabels
|
||||||
|
anchors
|
||||||
|
{
|
||||||
|
top: compatibilityHeading.bottom
|
||||||
|
topMargin: UM.Theme.getSize("default_margin").height
|
||||||
|
bottomMargin: UM.Theme.getSize("default_margin").height
|
||||||
|
}
|
||||||
|
width: childrenRect.width
|
||||||
|
Label
|
||||||
|
{
|
||||||
|
text: catalog.i18nc("@label", "Machines") + ":"
|
||||||
|
font: UM.Theme.getFont("small")
|
||||||
|
}
|
||||||
|
Label
|
||||||
|
{
|
||||||
|
text: catalog.i18nc("@label", "Print Cores") + ":"
|
||||||
|
font: UM.Theme.getFont("small")
|
||||||
|
}
|
||||||
|
Label
|
||||||
|
{
|
||||||
|
text: catalog.i18nc("@label", "Quality Profiles") + ":"
|
||||||
|
font: UM.Theme.getFont("small")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Column
|
||||||
|
{
|
||||||
|
id: compatibilityValues
|
||||||
|
anchors
|
||||||
|
{
|
||||||
|
left: compatibilityLabels.right
|
||||||
|
leftMargin: UM.Theme.getSize("default_margin").height
|
||||||
|
top: compatibilityLabels.top
|
||||||
|
bottom: compatibilityLabels.bottom
|
||||||
|
}
|
||||||
|
Label
|
||||||
|
{
|
||||||
|
text: "Thingy"
|
||||||
|
font: UM.Theme.getFont("very_small")
|
||||||
|
}
|
||||||
|
Label
|
||||||
|
{
|
||||||
|
text: "Thingy"
|
||||||
|
font: UM.Theme.getFont("very_small")
|
||||||
|
}
|
||||||
|
Label
|
||||||
|
{
|
||||||
|
text: "Thingy"
|
||||||
|
font: UM.Theme.getFont("very_small")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle
|
||||||
|
{
|
||||||
|
color: UM.Theme.getColor("lining")
|
||||||
|
width: tile.width
|
||||||
|
height: UM.Theme.getSize("default_lining").height
|
||||||
|
anchors.bottom: tile.bottom
|
||||||
|
}
|
||||||
|
Connections
|
||||||
|
{
|
||||||
|
target: toolbox
|
||||||
|
onInstallChanged: installed = toolbox.isInstalled(model.id)
|
||||||
|
}
|
||||||
|
}
|
42
plugins/Toolbox/resources/qml/ToolboxDownloadsGrid.qml
Normal file
42
plugins/Toolbox/resources/qml/ToolboxDownloadsGrid.qml
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
// Copyright (c) 2018 Ultimaker B.V.
|
||||||
|
// Toolbox 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 QtQuick.Layouts 1.3
|
||||||
|
import UM 1.1 as UM
|
||||||
|
|
||||||
|
Column
|
||||||
|
{
|
||||||
|
// HACK: GridLayouts don't render to the correct height with odd numbers of
|
||||||
|
// items, so if odd, add some extra space.
|
||||||
|
height: grid.model.items.length % 2 == 0 ? childrenRect.height : childrenRect.height + UM.Theme.getSize("toolbox_thumbnail_small").height
|
||||||
|
width: parent.width
|
||||||
|
spacing: UM.Theme.getSize("default_margin").height
|
||||||
|
Label
|
||||||
|
{
|
||||||
|
id: heading
|
||||||
|
text: toolbox.viewCategory == "material" ? catalog.i18nc("@label", "Maker Choices") : catalog.i18nc("@label", "Community Plugins")
|
||||||
|
width: parent.width
|
||||||
|
color: UM.Theme.getColor("text_medium")
|
||||||
|
font: UM.Theme.getFont("medium")
|
||||||
|
}
|
||||||
|
GridLayout
|
||||||
|
{
|
||||||
|
id: grid
|
||||||
|
property var model: toolbox.viewCategory == "material" ? toolbox.authorsModel : toolbox.packagesModel
|
||||||
|
width: parent.width
|
||||||
|
columns: 2
|
||||||
|
columnSpacing: UM.Theme.getSize("default_margin").height
|
||||||
|
rowSpacing: UM.Theme.getSize("default_margin").width
|
||||||
|
Repeater
|
||||||
|
{
|
||||||
|
model: grid.model
|
||||||
|
delegate: ToolboxDownloadsGridTile
|
||||||
|
{
|
||||||
|
Layout.preferredWidth: (grid.width - (grid.columns - 1) * grid.columnSpacing) / grid.columns
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
99
plugins/Toolbox/resources/qml/ToolboxDownloadsGridTile.qml
Normal file
99
plugins/Toolbox/resources/qml/ToolboxDownloadsGridTile.qml
Normal file
|
@ -0,0 +1,99 @@
|
||||||
|
// Copyright (c) 2018 Ultimaker B.V.
|
||||||
|
// Toolbox 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 QtQuick.Layouts 1.3
|
||||||
|
import UM 1.1 as UM
|
||||||
|
|
||||||
|
Item
|
||||||
|
{
|
||||||
|
height: childrenRect.height
|
||||||
|
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 - 26
|
||||||
|
height: UM.Theme.getSize("toolbox_thumbnail_small").height - 26
|
||||||
|
fillMode: Image.PreserveAspectFit
|
||||||
|
source: model.icon_url || "../images/logobot.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("very_small")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
MouseArea
|
||||||
|
{
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
onEntered:
|
||||||
|
{
|
||||||
|
thumbnail.border.color = UM.Theme.getColor("primary")
|
||||||
|
highlight.opacity = 0.1
|
||||||
|
}
|
||||||
|
onExited:
|
||||||
|
{
|
||||||
|
thumbnail.border.color = UM.Theme.getColor("lining")
|
||||||
|
highlight.opacity = 0.0
|
||||||
|
}
|
||||||
|
onClicked:
|
||||||
|
{
|
||||||
|
base.selection = model
|
||||||
|
switch(toolbox.viewCategory)
|
||||||
|
{
|
||||||
|
case "material":
|
||||||
|
toolbox.viewPage = "author"
|
||||||
|
toolbox.filterModelByProp("packages", "author_id", model.id)
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
toolbox.viewPage = "detail"
|
||||||
|
toolbox.filterModelByProp("packages", "id", model.id)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
38
plugins/Toolbox/resources/qml/ToolboxDownloadsPage.qml
Normal file
38
plugins/Toolbox/resources/qml/ToolboxDownloadsPage.qml
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
// Copyright (c) 2018 Ultimaker B.V.
|
||||||
|
// Toolbox is released under the terms of the LGPLv3 or higher.
|
||||||
|
|
||||||
|
import QtQuick 2.7
|
||||||
|
import QtQuick.Controls 1.4
|
||||||
|
import QtQuick.Controls.Styles 1.4
|
||||||
|
import UM 1.1 as UM
|
||||||
|
|
||||||
|
ScrollView
|
||||||
|
{
|
||||||
|
frameVisible: false
|
||||||
|
width: parent.width
|
||||||
|
height: parent.height
|
||||||
|
style: UM.Theme.styles.scrollview
|
||||||
|
Column
|
||||||
|
{
|
||||||
|
width: parent.width - 2 * padding
|
||||||
|
spacing: UM.Theme.getSize("default_margin").height
|
||||||
|
padding: UM.Theme.getSize("wide_margin").height
|
||||||
|
height: childrenRect.height + 2 * padding
|
||||||
|
ToolboxDownloadsShowcase
|
||||||
|
{
|
||||||
|
id: showcase
|
||||||
|
width: parent.width
|
||||||
|
}
|
||||||
|
Rectangle
|
||||||
|
{
|
||||||
|
color: UM.Theme.getColor("lining")
|
||||||
|
width: parent.width
|
||||||
|
height: UM.Theme.getSize("default_lining").height
|
||||||
|
}
|
||||||
|
ToolboxDownloadsGrid
|
||||||
|
{
|
||||||
|
id: allPlugins
|
||||||
|
width: parent.width
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
45
plugins/Toolbox/resources/qml/ToolboxDownloadsShowcase.qml
Normal file
45
plugins/Toolbox/resources/qml/ToolboxDownloadsShowcase.qml
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
// Copyright (c) 2018 Ultimaker B.V.
|
||||||
|
// Toolbox 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.1 as UM
|
||||||
|
|
||||||
|
Column
|
||||||
|
{
|
||||||
|
height: childrenRect.height
|
||||||
|
spacing: UM.Theme.getSize("toolbox_showcase_spacing").width
|
||||||
|
width: parent.width
|
||||||
|
Label
|
||||||
|
{
|
||||||
|
id: heading
|
||||||
|
text: catalog.i18nc("@label", "Featured")
|
||||||
|
width: parent.width
|
||||||
|
color: UM.Theme.getColor("text_medium")
|
||||||
|
font: UM.Theme.getFont("medium")
|
||||||
|
}
|
||||||
|
Row
|
||||||
|
{
|
||||||
|
height: childrenRect.height
|
||||||
|
spacing: UM.Theme.getSize("wide_margin").width
|
||||||
|
anchors
|
||||||
|
{
|
||||||
|
horizontalCenter: parent.horizontalCenter
|
||||||
|
}
|
||||||
|
Repeater
|
||||||
|
{
|
||||||
|
model: {
|
||||||
|
if ( toolbox.viewCategory == "plugin" )
|
||||||
|
{
|
||||||
|
return toolbox.pluginsShowcaseModel
|
||||||
|
}
|
||||||
|
if ( toolbox.viewCategory == "material" )
|
||||||
|
{
|
||||||
|
return toolbox.materialsShowcaseModel
|
||||||
|
}
|
||||||
|
}
|
||||||
|
delegate: ToolboxDownloadsShowcaseTile {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,90 @@
|
||||||
|
// Copyright (c) 2018 Ultimaker B.V.
|
||||||
|
// Toolbox 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.1 as UM
|
||||||
|
|
||||||
|
Item
|
||||||
|
{
|
||||||
|
width: UM.Theme.getSize("toolbox_thumbnail_large").width
|
||||||
|
height: childrenRect.height
|
||||||
|
Rectangle
|
||||||
|
{
|
||||||
|
id: highlight
|
||||||
|
anchors.fill: parent
|
||||||
|
opacity: 0.0
|
||||||
|
color: UM.Theme.getColor("primary")
|
||||||
|
}
|
||||||
|
Rectangle
|
||||||
|
{
|
||||||
|
id: thumbnail
|
||||||
|
color: "white"
|
||||||
|
width: UM.Theme.getSize("toolbox_thumbnail_large").width
|
||||||
|
height: UM.Theme.getSize("toolbox_thumbnail_large").height
|
||||||
|
border
|
||||||
|
{
|
||||||
|
width: UM.Theme.getSize("default_lining").width
|
||||||
|
color: UM.Theme.getColor("lining")
|
||||||
|
}
|
||||||
|
anchors
|
||||||
|
{
|
||||||
|
top: parent.top
|
||||||
|
horizontalCenter: parent.horizontalCenter
|
||||||
|
}
|
||||||
|
Image {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
width: UM.Theme.getSize("toolbox_thumbnail_large").width - 2 * UM.Theme.getSize("default_margin").width
|
||||||
|
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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Label
|
||||||
|
{
|
||||||
|
text: model.name
|
||||||
|
anchors
|
||||||
|
{
|
||||||
|
bottom: parent.bottom
|
||||||
|
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("text")
|
||||||
|
font: UM.Theme.getFont("medium_bold")
|
||||||
|
}
|
||||||
|
MouseArea
|
||||||
|
{
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
onEntered:
|
||||||
|
{
|
||||||
|
thumbnail.border.color = UM.Theme.getColor("primary")
|
||||||
|
highlight.opacity = 0.1
|
||||||
|
}
|
||||||
|
onExited:
|
||||||
|
{
|
||||||
|
thumbnail.border.color = UM.Theme.getColor("lining")
|
||||||
|
highlight.opacity = 0.0
|
||||||
|
}
|
||||||
|
onClicked:
|
||||||
|
{
|
||||||
|
base.selection = model
|
||||||
|
switch(toolbox.viewCategory)
|
||||||
|
{
|
||||||
|
case "material":
|
||||||
|
toolbox.viewPage = "author"
|
||||||
|
toolbox.filterModelByProp("packages", "author_name", model.name)
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
toolbox.viewPage = "detail"
|
||||||
|
toolbox.filterModelByProp("packages", "id", model.id)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
68
plugins/Toolbox/resources/qml/ToolboxFooter.qml
Normal file
68
plugins/Toolbox/resources/qml/ToolboxFooter.qml
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
// Copyright (c) 2018 Ultimaker B.V.
|
||||||
|
// Toolbox 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.1 as UM
|
||||||
|
|
||||||
|
Item
|
||||||
|
{
|
||||||
|
id: footer
|
||||||
|
width: parent.width
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
height: visible ? Math.floor(UM.Theme.getSize("toolbox_footer").height) : 0
|
||||||
|
Label
|
||||||
|
{
|
||||||
|
visible: toolbox.restartRequired
|
||||||
|
text: catalog.i18nc("@info", "You will need to restart Cura before changes in plugins have effect.")
|
||||||
|
height: Math.floor(UM.Theme.getSize("toolbox_footer_button").height)
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
anchors
|
||||||
|
{
|
||||||
|
top: restartButton.top
|
||||||
|
left: parent.left
|
||||||
|
leftMargin: UM.Theme.getSize("wide_margin").width
|
||||||
|
right: restartButton.right
|
||||||
|
rightMargin: UM.Theme.getSize("default_margin").width
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Button
|
||||||
|
{
|
||||||
|
id: restartButton
|
||||||
|
text: catalog.i18nc("@info:button", "Quit Cura")
|
||||||
|
anchors
|
||||||
|
{
|
||||||
|
top: parent.top
|
||||||
|
topMargin: UM.Theme.getSize("default_margin").height
|
||||||
|
right: parent.right
|
||||||
|
rightMargin: UM.Theme.getSize("wide_margin").width
|
||||||
|
}
|
||||||
|
visible: toolbox.restartRequired
|
||||||
|
iconName: "dialog-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: Text
|
||||||
|
{
|
||||||
|
color: UM.Theme.getColor("button_text")
|
||||||
|
font: UM.Theme.getFont("default_bold")
|
||||||
|
text: control.text
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ToolboxShadow
|
||||||
|
{
|
||||||
|
visible: toolbox.restartRequired
|
||||||
|
anchors.bottom: footer.top
|
||||||
|
reversed: true
|
||||||
|
}
|
||||||
|
}
|
62
plugins/Toolbox/resources/qml/ToolboxHeader.qml
Normal file
62
plugins/Toolbox/resources/qml/ToolboxHeader.qml
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
// Copyright (c) 2018 Ultimaker B.V.
|
||||||
|
// Toolbox is released under the terms of the LGPLv3 or higher.
|
||||||
|
|
||||||
|
import QtQuick 2.2
|
||||||
|
import QtQuick.Controls 1.4
|
||||||
|
import UM 1.1 as UM
|
||||||
|
|
||||||
|
Item
|
||||||
|
{
|
||||||
|
id: header
|
||||||
|
width: parent.width
|
||||||
|
height: UM.Theme.getSize("toolbox_header").height
|
||||||
|
Row
|
||||||
|
{
|
||||||
|
id: bar
|
||||||
|
spacing: UM.Theme.getSize("default_margin").width
|
||||||
|
height: childrenRect.height
|
||||||
|
width: childrenRect.width
|
||||||
|
anchors
|
||||||
|
{
|
||||||
|
left: parent.left
|
||||||
|
leftMargin: UM.Theme.getSize("default_margin").width
|
||||||
|
}
|
||||||
|
ToolboxTabButton
|
||||||
|
{
|
||||||
|
text: catalog.i18nc("@title:tab", "Plugins")
|
||||||
|
active: toolbox.viewCategory == "plugin"
|
||||||
|
onClicked:
|
||||||
|
{
|
||||||
|
toolbox.filterModelByProp("packages", "type", "plugin")
|
||||||
|
toolbox.viewCategory = "plugin"
|
||||||
|
toolbox.viewPage = "overview"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ToolboxTabButton
|
||||||
|
{
|
||||||
|
text: catalog.i18nc("@title:tab", "Materials")
|
||||||
|
active: toolbox.viewCategory == "material"
|
||||||
|
onClicked:
|
||||||
|
{
|
||||||
|
toolbox.filterModelByProp("authors", "package_types", "material")
|
||||||
|
toolbox.viewCategory = "material"
|
||||||
|
toolbox.viewPage = "overview"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ToolboxTabButton
|
||||||
|
{
|
||||||
|
text: catalog.i18nc("@title:tab", "Installed")
|
||||||
|
active: toolbox.viewCategory == "installed"
|
||||||
|
anchors
|
||||||
|
{
|
||||||
|
right: parent.right
|
||||||
|
rightMargin: UM.Theme.getSize("default_margin").width
|
||||||
|
}
|
||||||
|
onClicked: toolbox.viewCategory = "installed"
|
||||||
|
}
|
||||||
|
ToolboxShadow
|
||||||
|
{
|
||||||
|
anchors.top: bar.bottom
|
||||||
|
}
|
||||||
|
}
|
106
plugins/Toolbox/resources/qml/ToolboxInstalledPage.qml
Normal file
106
plugins/Toolbox/resources/qml/ToolboxInstalledPage.qml
Normal file
|
@ -0,0 +1,106 @@
|
||||||
|
// Copyright (c) 2018 Ultimaker B.V.
|
||||||
|
// Toolbox is released under the terms of the LGPLv3 or higher.
|
||||||
|
|
||||||
|
import QtQuick 2.7
|
||||||
|
import QtQuick.Dialogs 1.1
|
||||||
|
import QtQuick.Window 2.2
|
||||||
|
import QtQuick.Controls 1.4
|
||||||
|
import QtQuick.Controls.Styles 1.4
|
||||||
|
import UM 1.1 as UM
|
||||||
|
|
||||||
|
ScrollView
|
||||||
|
{
|
||||||
|
id: page
|
||||||
|
frameVisible: false
|
||||||
|
width: parent.width
|
||||||
|
height: parent.height
|
||||||
|
style: UM.Theme.styles.scrollview
|
||||||
|
Column
|
||||||
|
{
|
||||||
|
spacing: UM.Theme.getSize("default_margin").height
|
||||||
|
anchors
|
||||||
|
{
|
||||||
|
right: parent.right
|
||||||
|
left: parent.left
|
||||||
|
leftMargin: UM.Theme.getSize("wide_margin").width
|
||||||
|
topMargin: UM.Theme.getSize("wide_margin").height
|
||||||
|
bottomMargin: UM.Theme.getSize("wide_margin").height
|
||||||
|
top: parent.top
|
||||||
|
}
|
||||||
|
height: childrenRect.height + 4 * UM.Theme.getSize("default_margin").height
|
||||||
|
Label
|
||||||
|
{
|
||||||
|
visible: toolbox.pluginsInstalledModel.items.length > 0
|
||||||
|
width: parent.width
|
||||||
|
text: catalog.i18nc("@title:tab", "Plugins")
|
||||||
|
color: UM.Theme.getColor("text_medium")
|
||||||
|
font: UM.Theme.getFont("medium")
|
||||||
|
}
|
||||||
|
Rectangle
|
||||||
|
{
|
||||||
|
visible: toolbox.pluginsInstalledModel.items.length > 0
|
||||||
|
color: "transparent"
|
||||||
|
width: parent.width
|
||||||
|
height: childrenRect.height + 1 * UM.Theme.getSize("default_lining").width
|
||||||
|
border.color: UM.Theme.getColor("lining")
|
||||||
|
border.width: UM.Theme.getSize("default_lining").width
|
||||||
|
Column
|
||||||
|
{
|
||||||
|
height: childrenRect.height
|
||||||
|
anchors
|
||||||
|
{
|
||||||
|
top: parent.top
|
||||||
|
right: parent.right
|
||||||
|
left: parent.left
|
||||||
|
leftMargin: 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
|
||||||
|
{
|
||||||
|
id: materialList
|
||||||
|
model: toolbox.pluginsInstalledModel
|
||||||
|
delegate: ToolboxInstalledTile {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Label
|
||||||
|
{
|
||||||
|
visible: toolbox.materialsInstalledModel.items.length > 0
|
||||||
|
width: page.width
|
||||||
|
text: catalog.i18nc("@title:tab", "Materials")
|
||||||
|
color: UM.Theme.getColor("text_medium")
|
||||||
|
font: UM.Theme.getFont("medium")
|
||||||
|
}
|
||||||
|
Rectangle
|
||||||
|
{
|
||||||
|
visible: toolbox.materialsInstalledModel.items.length > 0
|
||||||
|
color: "transparent"
|
||||||
|
width: parent.width
|
||||||
|
height: childrenRect.height + 1 * UM.Theme.getSize("default_lining").width
|
||||||
|
border.color: UM.Theme.getColor("lining")
|
||||||
|
border.width: UM.Theme.getSize("default_lining").width
|
||||||
|
Column
|
||||||
|
{
|
||||||
|
height: Math.max( UM.Theme.getSize("wide_margin").height, childrenRect.height)
|
||||||
|
anchors
|
||||||
|
{
|
||||||
|
top: parent.top
|
||||||
|
right: parent.right
|
||||||
|
left: parent.left
|
||||||
|
leftMargin: 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
|
||||||
|
{
|
||||||
|
id: pluginList
|
||||||
|
model: toolbox.materialsInstalledModel
|
||||||
|
delegate: ToolboxInstalledTile {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
219
plugins/Toolbox/resources/qml/ToolboxInstalledTile.qml
Normal file
219
plugins/Toolbox/resources/qml/ToolboxInstalledTile.qml
Normal file
|
@ -0,0 +1,219 @@
|
||||||
|
// Copyright (c) 2018 Ultimaker B.V.
|
||||||
|
// Toolbox 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.1 as UM
|
||||||
|
|
||||||
|
Item
|
||||||
|
{
|
||||||
|
property bool canUpdate: false
|
||||||
|
property bool isEnabled: true
|
||||||
|
height: UM.Theme.getSize("toolbox_installed_tile").height
|
||||||
|
anchors
|
||||||
|
{
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
}
|
||||||
|
Rectangle
|
||||||
|
{
|
||||||
|
color: UM.Theme.getColor("lining")
|
||||||
|
width: parent.width
|
||||||
|
height: UM.Theme.getSize("default_lining").height
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
}
|
||||||
|
Column
|
||||||
|
{
|
||||||
|
id: pluginInfo
|
||||||
|
property var color: isEnabled ? UM.Theme.getColor("text") : UM.Theme.getColor("lining")
|
||||||
|
height: parent.height
|
||||||
|
anchors
|
||||||
|
{
|
||||||
|
left: parent.left
|
||||||
|
top: parent.top
|
||||||
|
right: authorInfo.left
|
||||||
|
topMargin: UM.Theme.getSize("default_margin").height
|
||||||
|
rightMargin: UM.Theme.getSize("default_margin").width
|
||||||
|
}
|
||||||
|
Label
|
||||||
|
{
|
||||||
|
text: model.name
|
||||||
|
width: parent.width
|
||||||
|
height: UM.Theme.getSize("toolbox_property_label").height
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
font: UM.Theme.getFont("default_bold")
|
||||||
|
color: pluginInfo.color
|
||||||
|
}
|
||||||
|
Text
|
||||||
|
{
|
||||||
|
text: model.description
|
||||||
|
width: parent.width
|
||||||
|
height: UM.Theme.getSize("toolbox_property_label").height
|
||||||
|
clip: true
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
|
color: pluginInfo.color
|
||||||
|
elide: Text.ElideRight
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Column
|
||||||
|
{
|
||||||
|
id: authorInfo
|
||||||
|
height: parent.height
|
||||||
|
width: Math.floor(UM.Theme.getSize("toolbox_action_button").width * 1.25)
|
||||||
|
anchors
|
||||||
|
{
|
||||||
|
top: parent.top
|
||||||
|
topMargin: UM.Theme.getSize("default_margin").height
|
||||||
|
right: pluginActions.left
|
||||||
|
rightMargin: UM.Theme.getSize("default_margin").width
|
||||||
|
}
|
||||||
|
Label
|
||||||
|
{
|
||||||
|
text:
|
||||||
|
{
|
||||||
|
if (model.author_email)
|
||||||
|
{
|
||||||
|
return "<a href=\"mailto:" + model.author_email + "?Subject=Cura: " + model.name + "\">" + model.author_name + "</a>"
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return model.author_name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
width: parent.width
|
||||||
|
height: UM.Theme.getSize("toolbox_property_label").height
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
horizontalAlignment: Text.AlignLeft
|
||||||
|
onLinkActivated: Qt.openUrlExternally("mailto:" + model.author_email + "?Subject=Cura: " + model.name + " Plugin")
|
||||||
|
color: model.enabled ? UM.Theme.getColor("text") : UM.Theme.getColor("lining")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Column
|
||||||
|
{
|
||||||
|
id: pluginActions
|
||||||
|
width: childrenRect.width
|
||||||
|
height: childrenRect.height
|
||||||
|
spacing: UM.Theme.getSize("default_margin").height
|
||||||
|
anchors
|
||||||
|
{
|
||||||
|
top: parent.top
|
||||||
|
right: parent.right
|
||||||
|
topMargin: UM.Theme.getSize("default_margin").height
|
||||||
|
}
|
||||||
|
Button {
|
||||||
|
id: removeButton
|
||||||
|
text:
|
||||||
|
{
|
||||||
|
if (model.is_bundled)
|
||||||
|
{
|
||||||
|
return isEnabled ? catalog.i18nc("@action:button", "Disable") : catalog.i18nc("@action:button", "Enable")
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return catalog.i18nc("@action:button", "Uninstall")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
enabled: !toolbox.isDownloading
|
||||||
|
style: ButtonStyle
|
||||||
|
{
|
||||||
|
background: Rectangle
|
||||||
|
{
|
||||||
|
implicitWidth: UM.Theme.getSize("toolbox_action_button").width
|
||||||
|
implicitHeight: UM.Theme.getSize("toolbox_action_button").height
|
||||||
|
color: "transparent"
|
||||||
|
border
|
||||||
|
{
|
||||||
|
width: UM.Theme.getSize("default_lining").width
|
||||||
|
color: UM.Theme.getColor("lining")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
label: Text
|
||||||
|
{
|
||||||
|
text: control.text
|
||||||
|
color: UM.Theme.getColor("text")
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onClicked:
|
||||||
|
{
|
||||||
|
if (model.is_bundled)
|
||||||
|
{
|
||||||
|
if (toolbox.isEnabled(model.id))
|
||||||
|
{
|
||||||
|
toolbox.disable(model.id)
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
toolbox.enable(model.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
toolbox.uninstall( model.id )
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Button
|
||||||
|
{
|
||||||
|
id: updateButton
|
||||||
|
text: catalog.i18nc("@action:button", "Update")
|
||||||
|
visible: canUpdate
|
||||||
|
style: ButtonStyle
|
||||||
|
{
|
||||||
|
background: Rectangle
|
||||||
|
{
|
||||||
|
implicitWidth: UM.Theme.getSize("toolbox_action_button").width
|
||||||
|
implicitHeight: UM.Theme.getSize("toolbox_action_button").height
|
||||||
|
color: control.hovered ? UM.Theme.getColor("primary_hover") : UM.Theme.getColor("primary")
|
||||||
|
}
|
||||||
|
label: Label
|
||||||
|
{
|
||||||
|
text: control.text
|
||||||
|
color: control.hovered ? UM.Theme.getColor("button_text") : UM.Theme.getColor("button_text_hover")
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
font: UM.Theme.getFont("default_bold")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onClicked:
|
||||||
|
{
|
||||||
|
toolbox.update(model.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ProgressBar
|
||||||
|
{
|
||||||
|
id: progressbar
|
||||||
|
anchors
|
||||||
|
{
|
||||||
|
left: updateButton.left
|
||||||
|
right: updateButton.right
|
||||||
|
top: updateButton.bottom
|
||||||
|
topMargin: Math.floor(UM.Theme.getSize("default_margin") / 4)
|
||||||
|
}
|
||||||
|
value: toolbox.isDownloading ? toolbox.downloadProgress : 0
|
||||||
|
visible: toolbox.isDownloading
|
||||||
|
style: ProgressBarStyle
|
||||||
|
{
|
||||||
|
background: Rectangle
|
||||||
|
{
|
||||||
|
color: UM.Theme.getColor("lining")
|
||||||
|
implicitHeight: Math.floor(UM.Theme.getSize("toolbox_progress_bar").height)
|
||||||
|
}
|
||||||
|
progress: Rectangle
|
||||||
|
{
|
||||||
|
color: UM.Theme.getColor("primary")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Connections
|
||||||
|
{
|
||||||
|
target: toolbox
|
||||||
|
onEnabledChanged: isEnabled = toolbox.isEnabled(model.id)
|
||||||
|
onMetadataChanged: canUpdate = toolbox.canUpdate(model.id)
|
||||||
|
}
|
||||||
|
}
|
72
plugins/Toolbox/resources/qml/ToolboxLicenseDialog.qml
Normal file
72
plugins/Toolbox/resources/qml/ToolboxLicenseDialog.qml
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
// Copyright (c) 2018 Ultimaker B.V.
|
||||||
|
// Toolbox is released under the terms of the LGPLv3 or higher.
|
||||||
|
|
||||||
|
import QtQuick 2.2
|
||||||
|
import QtQuick.Dialogs 1.1
|
||||||
|
import QtQuick.Window 2.2
|
||||||
|
import QtQuick.Controls 1.4
|
||||||
|
import QtQuick.Controls.Styles 1.4
|
||||||
|
|
||||||
|
// TODO: Switch to QtQuick.Controls 2.x and remove QtQuick.Controls.Styles
|
||||||
|
|
||||||
|
import UM 1.1 as UM
|
||||||
|
|
||||||
|
UM.Dialog
|
||||||
|
{
|
||||||
|
title: catalog.i18nc("@title:window", "Plugin License Agreement")
|
||||||
|
minimumWidth: UM.Theme.getSize("license_window_minimum").width
|
||||||
|
minimumHeight: UM.Theme.getSize("license_window_minimum").height
|
||||||
|
width: minimumWidth
|
||||||
|
height: minimumHeight
|
||||||
|
property var pluginName;
|
||||||
|
property var licenseContent;
|
||||||
|
property var pluginFileLocation;
|
||||||
|
Item
|
||||||
|
{
|
||||||
|
anchors.fill: parent
|
||||||
|
Label
|
||||||
|
{
|
||||||
|
id: licenseTitle
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.left: parent.left
|
||||||
|
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?")
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
}
|
||||||
|
TextArea
|
||||||
|
{
|
||||||
|
id: licenseText
|
||||||
|
anchors.top: licenseTitle.bottom
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.topMargin: UM.Theme.getSize("default_margin").height
|
||||||
|
readOnly: true
|
||||||
|
text: licenseDialog.licenseContent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rightButtons:
|
||||||
|
[
|
||||||
|
Button
|
||||||
|
{
|
||||||
|
id: acceptButton
|
||||||
|
anchors.margins: UM.Theme.getSize("default_margin").width
|
||||||
|
text: catalog.i18nc("@action:button", "Accept")
|
||||||
|
onClicked:
|
||||||
|
{
|
||||||
|
licenseDialog.close();
|
||||||
|
toolbox.install(licenseDialog.pluginFileLocation);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Button
|
||||||
|
{
|
||||||
|
id: declineButton
|
||||||
|
anchors.margins: UM.Theme.getSize("default_margin").width
|
||||||
|
text: catalog.i18nc("@action:button", "Decline")
|
||||||
|
onClicked:
|
||||||
|
{
|
||||||
|
licenseDialog.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
22
plugins/Toolbox/resources/qml/ToolboxLoadingPage.qml
Normal file
22
plugins/Toolbox/resources/qml/ToolboxLoadingPage.qml
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
// Copyright (c) 2018 Ultimaker B.V.
|
||||||
|
// Toolbox is released under the terms of the LGPLv3 or higher.
|
||||||
|
|
||||||
|
import QtQuick 2.7
|
||||||
|
import QtQuick.Controls 1.4
|
||||||
|
import QtQuick.Controls.Styles 1.4
|
||||||
|
|
||||||
|
Rectangle
|
||||||
|
{
|
||||||
|
id: page
|
||||||
|
width: parent.width
|
||||||
|
height: parent.height
|
||||||
|
color: "transparent"
|
||||||
|
Label
|
||||||
|
{
|
||||||
|
text: catalog.i18nc("@info", "Fetching packages...")
|
||||||
|
anchors
|
||||||
|
{
|
||||||
|
centerIn: parent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
24
plugins/Toolbox/resources/qml/ToolboxShadow.qml
Normal file
24
plugins/Toolbox/resources/qml/ToolboxShadow.qml
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
// Copyright (c) 2018 Ultimaker B.V.
|
||||||
|
// Toolbox is released under the terms of the LGPLv3 or higher.
|
||||||
|
|
||||||
|
import QtQuick 2.2
|
||||||
|
|
||||||
|
Rectangle
|
||||||
|
{
|
||||||
|
property bool reversed: false
|
||||||
|
width: parent.width
|
||||||
|
height: 8
|
||||||
|
gradient: Gradient
|
||||||
|
{
|
||||||
|
GradientStop
|
||||||
|
{
|
||||||
|
position: reversed ? 1.0 : 0.0
|
||||||
|
color: reversed ? Qt.rgba(0,0,0,0.05) : Qt.rgba(0,0,0,0.2)
|
||||||
|
}
|
||||||
|
GradientStop
|
||||||
|
{
|
||||||
|
position: reversed ? 0.0 : 1.0
|
||||||
|
color: Qt.rgba(0,0,0,0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
51
plugins/Toolbox/resources/qml/ToolboxTabButton.qml
Normal file
51
plugins/Toolbox/resources/qml/ToolboxTabButton.qml
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
// Copyright (c) 2018 Ultimaker B.V.
|
||||||
|
// Toolbox 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.1 as UM
|
||||||
|
|
||||||
|
Button
|
||||||
|
{
|
||||||
|
property bool active: false
|
||||||
|
style: ButtonStyle
|
||||||
|
{
|
||||||
|
background: Rectangle
|
||||||
|
{
|
||||||
|
color: "transparent"
|
||||||
|
implicitWidth: UM.Theme.getSize("toolbox_header_tab").width
|
||||||
|
implicitHeight: UM.Theme.getSize("toolbox_header_tab").height
|
||||||
|
Rectangle
|
||||||
|
{
|
||||||
|
visible: control.active
|
||||||
|
color: UM.Theme.getColor("sidebar_header_highlight_hover")
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
width: parent.width
|
||||||
|
height: UM.Theme.getSize("sidebar_header_highlight").height
|
||||||
|
}
|
||||||
|
}
|
||||||
|
label: Text
|
||||||
|
{
|
||||||
|
text: control.text
|
||||||
|
color:
|
||||||
|
{
|
||||||
|
if(control.hovered)
|
||||||
|
{
|
||||||
|
return UM.Theme.getColor("topbar_button_text_hovered");
|
||||||
|
}
|
||||||
|
if(control.active)
|
||||||
|
{
|
||||||
|
return UM.Theme.getColor("topbar_button_text_active");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return UM.Theme.getColor("topbar_button_text_inactive");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
font: control.active ? UM.Theme.getFont("medium_bold") : UM.Theme.getFont("medium")
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
93
plugins/Toolbox/src/AuthorsModel.py
Normal file
93
plugins/Toolbox/src/AuthorsModel.py
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
# Copyright (c) 2018 Ultimaker B.V.
|
||||||
|
# Cura is released under the terms of the LGPLv3 or higher.
|
||||||
|
|
||||||
|
import re
|
||||||
|
from typing import Dict
|
||||||
|
|
||||||
|
from PyQt5.QtCore import Qt, pyqtProperty, pyqtSignal
|
||||||
|
|
||||||
|
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.
|
||||||
|
class AuthorsModel(ListModel):
|
||||||
|
def __init__(self, parent = None):
|
||||||
|
super().__init__(parent)
|
||||||
|
|
||||||
|
self._metadata = None
|
||||||
|
|
||||||
|
self.addRoleName(Qt.UserRole + 1, "id")
|
||||||
|
self.addRoleName(Qt.UserRole + 2, "name")
|
||||||
|
self.addRoleName(Qt.UserRole + 3, "email")
|
||||||
|
self.addRoleName(Qt.UserRole + 4, "website")
|
||||||
|
self.addRoleName(Qt.UserRole + 5, "package_count")
|
||||||
|
self.addRoleName(Qt.UserRole + 6, "package_types")
|
||||||
|
self.addRoleName(Qt.UserRole + 7, "icon_url")
|
||||||
|
self.addRoleName(Qt.UserRole + 8, "description")
|
||||||
|
|
||||||
|
# List of filters for queries. The result is the union of the each list of results.
|
||||||
|
self._filter = {} # type: Dict[str,str]
|
||||||
|
|
||||||
|
def setMetadata(self, data):
|
||||||
|
self._metadata = data
|
||||||
|
self._update()
|
||||||
|
|
||||||
|
def _update(self):
|
||||||
|
items = []
|
||||||
|
|
||||||
|
for author in self._metadata:
|
||||||
|
items.append({
|
||||||
|
"id": author["author_id"],
|
||||||
|
"name": author["display_name"],
|
||||||
|
"email": author["email"] if "email" in author else None,
|
||||||
|
"website": author["website"],
|
||||||
|
"package_count": author["package_count"] if "package_count" in author else 0,
|
||||||
|
"package_types": author["package_types"] if "package_types" in author else [],
|
||||||
|
"icon_url": author["icon_url"] if "icon_url" in author else None,
|
||||||
|
"description": "Material and quality profiles from {author_name}".format( author_name = author["display_name"])
|
||||||
|
})
|
||||||
|
|
||||||
|
# Filter on all the key-word arguments.
|
||||||
|
for key, value in self._filter.items():
|
||||||
|
if key is "package_types":
|
||||||
|
key_filter = lambda item, value = value: value in item["package_types"]
|
||||||
|
elif "*" in value:
|
||||||
|
key_filter = lambda item, key = key, value = value: self._matchRegExp(item, key, value)
|
||||||
|
else:
|
||||||
|
key_filter = lambda item, key = key, value = value: self._matchString(item, key, value)
|
||||||
|
items = filter(key_filter, items)
|
||||||
|
|
||||||
|
# Execute all filters.
|
||||||
|
filtered_items = list(items)
|
||||||
|
|
||||||
|
filtered_items.sort(key = lambda k: k["name"])
|
||||||
|
self.setItems(filtered_items)
|
||||||
|
|
||||||
|
## Set the filter of this model based on a string.
|
||||||
|
# \param filter_dict \type{Dict} Dictionary to do the filtering by.
|
||||||
|
def setFilter(self, filter_dict: Dict[str, str]) -> None:
|
||||||
|
if filter_dict != self._filter:
|
||||||
|
self._filter = filter_dict
|
||||||
|
self._update()
|
||||||
|
|
||||||
|
@pyqtProperty("QVariantMap", fset = setFilter, constant = True)
|
||||||
|
def filter(self) -> Dict[str, str]:
|
||||||
|
return self._filter
|
||||||
|
|
||||||
|
# Check to see if a container matches with a regular expression
|
||||||
|
def _matchRegExp(self, metadata, property_name, value):
|
||||||
|
if property_name not in metadata:
|
||||||
|
return False
|
||||||
|
value = re.escape(value) #Escape for regex patterns.
|
||||||
|
value = "^" + value.replace("\\*", ".*") + "$" #Instead of (now escaped) asterisks, match on any string. Also add anchors for a complete match.
|
||||||
|
if self._ignore_case:
|
||||||
|
value_pattern = re.compile(value, re.IGNORECASE)
|
||||||
|
else:
|
||||||
|
value_pattern = re.compile(value)
|
||||||
|
|
||||||
|
return value_pattern.match(str(metadata[property_name]))
|
||||||
|
|
||||||
|
# Check to see if a container matches with a string
|
||||||
|
def _matchString(self, metadata, property_name, value):
|
||||||
|
if property_name not in metadata:
|
||||||
|
return False
|
||||||
|
return value.lower() == str(metadata[property_name]).lower()
|
106
plugins/Toolbox/src/PackagesModel.py
Normal file
106
plugins/Toolbox/src/PackagesModel.py
Normal file
|
@ -0,0 +1,106 @@
|
||||||
|
# Copyright (c) 2018 Ultimaker B.V.
|
||||||
|
# Cura is released under the terms of the LGPLv3 or higher.
|
||||||
|
|
||||||
|
import re
|
||||||
|
from typing import Dict
|
||||||
|
|
||||||
|
from PyQt5.QtCore import Qt, pyqtProperty
|
||||||
|
|
||||||
|
from UM.Application import Application
|
||||||
|
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.
|
||||||
|
class PackagesModel(ListModel):
|
||||||
|
def __init__(self, parent = None):
|
||||||
|
super().__init__(parent)
|
||||||
|
|
||||||
|
self._metadata = None
|
||||||
|
|
||||||
|
self.addRoleName(Qt.UserRole + 1, "id")
|
||||||
|
self.addRoleName(Qt.UserRole + 2, "type")
|
||||||
|
self.addRoleName(Qt.UserRole + 3, "name")
|
||||||
|
self.addRoleName(Qt.UserRole + 4, "version")
|
||||||
|
self.addRoleName(Qt.UserRole + 5, "author_id")
|
||||||
|
self.addRoleName(Qt.UserRole + 6, "author_name")
|
||||||
|
self.addRoleName(Qt.UserRole + 7, "author_email")
|
||||||
|
self.addRoleName(Qt.UserRole + 8, "description")
|
||||||
|
self.addRoleName(Qt.UserRole + 9, "icon_url")
|
||||||
|
self.addRoleName(Qt.UserRole + 10, "image_urls")
|
||||||
|
self.addRoleName(Qt.UserRole + 11, "download_url")
|
||||||
|
self.addRoleName(Qt.UserRole + 12, "last_updated")
|
||||||
|
self.addRoleName(Qt.UserRole + 13, "is_bundled")
|
||||||
|
self.addRoleName(Qt.UserRole + 14, "supported_configs")
|
||||||
|
|
||||||
|
# List of filters for queries. The result is the union of the each list of results.
|
||||||
|
self._filter = {} # type: Dict[str, str]
|
||||||
|
|
||||||
|
def setMetadata(self, data):
|
||||||
|
self._metadata = data
|
||||||
|
self._update()
|
||||||
|
|
||||||
|
def _update(self):
|
||||||
|
items = []
|
||||||
|
|
||||||
|
for package in self._metadata:
|
||||||
|
print(package["author"])
|
||||||
|
items.append({
|
||||||
|
"id": package["package_id"],
|
||||||
|
"type": package["package_type"],
|
||||||
|
"name": package["display_name"],
|
||||||
|
"version": package["package_version"],
|
||||||
|
"author_id": package["author"]["author_id"] if "author_id" in package["author"] else package["author"]["name"],
|
||||||
|
"author_name": package["author"]["display_name"] if "display_name" in package["author"] else package["author"]["name"],
|
||||||
|
"author_email": package["author"]["email"] if "email" in package["author"] else "None",
|
||||||
|
"description": package["description"],
|
||||||
|
"icon_url": package["icon_url"] if "icon_url" in package else None,
|
||||||
|
"image_urls": package["image_urls"] if "image_urls" in package else None,
|
||||||
|
"download_url": package["download_url"] if "download_url" in package else None,
|
||||||
|
"last_updated": package["last_updated"] if "last_updated" in package else None,
|
||||||
|
"is_bundled": package["is_bundled"] if "is_bundled" in package else False,
|
||||||
|
"supported_configs": package["supported_configs"] if "supported_configs" in package else []
|
||||||
|
})
|
||||||
|
|
||||||
|
# Filter on all the key-word arguments.
|
||||||
|
for key, value in self._filter.items():
|
||||||
|
if "*" in value:
|
||||||
|
key_filter = lambda candidate, key = key, value = value: self._matchRegExp(candidate, key, value)
|
||||||
|
else:
|
||||||
|
key_filter = lambda candidate, key = key, value = value: self._matchString(candidate, key, value)
|
||||||
|
items = filter(key_filter, items)
|
||||||
|
|
||||||
|
# Execute all filters.
|
||||||
|
filtered_items = list(items)
|
||||||
|
|
||||||
|
filtered_items.sort(key = lambda k: k["name"])
|
||||||
|
self.setItems(filtered_items)
|
||||||
|
|
||||||
|
## Set the filter of this model based on a string.
|
||||||
|
# \param filter_dict \type{Dict} Dictionary to do the filtering by.
|
||||||
|
def setFilter(self, filter_dict: Dict[str, str]) -> None:
|
||||||
|
if filter_dict != self._filter:
|
||||||
|
self._filter = filter_dict
|
||||||
|
self._update()
|
||||||
|
|
||||||
|
@pyqtProperty("QVariantMap", fset = setFilter, constant = True)
|
||||||
|
def filter(self) -> Dict[str, str]:
|
||||||
|
return self._filter
|
||||||
|
|
||||||
|
# Check to see if a container matches with a regular expression
|
||||||
|
def _matchRegExp(self, metadata, property_name, value):
|
||||||
|
if property_name not in metadata:
|
||||||
|
return False
|
||||||
|
value = re.escape(value) #Escape for regex patterns.
|
||||||
|
value = "^" + value.replace("\\*", ".*") + "$" #Instead of (now escaped) asterisks, match on any string. Also add anchors for a complete match.
|
||||||
|
if self._ignore_case:
|
||||||
|
value_pattern = re.compile(value, re.IGNORECASE)
|
||||||
|
else:
|
||||||
|
value_pattern = re.compile(value)
|
||||||
|
|
||||||
|
return value_pattern.match(str(metadata[property_name]))
|
||||||
|
|
||||||
|
# Check to see if a container matches with a string
|
||||||
|
def _matchString(self, metadata, property_name, value):
|
||||||
|
if property_name not in metadata:
|
||||||
|
return False
|
||||||
|
return value.lower() == str(metadata[property_name]).lower()
|
515
plugins/Toolbox/src/Toolbox.py
Normal file
515
plugins/Toolbox/src/Toolbox.py
Normal file
|
@ -0,0 +1,515 @@
|
||||||
|
# Copyright (c) 2018 Ultimaker B.V.
|
||||||
|
# Toolbox is released under the terms of the LGPLv3 or higher.
|
||||||
|
|
||||||
|
from typing import Dict
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import tempfile
|
||||||
|
import platform
|
||||||
|
|
||||||
|
from PyQt5.QtCore import QUrl, QObject, pyqtProperty, pyqtSignal, pyqtSlot
|
||||||
|
from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkRequest, QNetworkReply
|
||||||
|
|
||||||
|
from UM.Application import Application
|
||||||
|
from UM.Logger import Logger
|
||||||
|
from UM.PluginRegistry import PluginRegistry
|
||||||
|
from UM.Qt.Bindings.PluginsModel import PluginsModel
|
||||||
|
from UM.Extension import Extension
|
||||||
|
from UM.i18n import i18nCatalog
|
||||||
|
from UM.Version import Version
|
||||||
|
|
||||||
|
from cura.CuraApplication import CuraApplication
|
||||||
|
from .AuthorsModel import AuthorsModel
|
||||||
|
from .PackagesModel import PackagesModel
|
||||||
|
|
||||||
|
i18n_catalog = i18nCatalog("cura")
|
||||||
|
|
||||||
|
## The Toolbox class is responsible of communicating with the server through the API
|
||||||
|
class Toolbox(QObject, Extension):
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
super().__init__(parent)
|
||||||
|
|
||||||
|
self._application = Application.getInstance()
|
||||||
|
self._package_manager = None
|
||||||
|
self._plugin_registry = Application.getInstance().getPluginRegistry()
|
||||||
|
self._packages_version = self._plugin_registry.APIVersion
|
||||||
|
self._api_version = 1
|
||||||
|
self._api_url = "https://api-staging.ultimaker.com/cura-packages/v{api_version}/cura/v{package_version}".format( api_version = self._api_version, package_version = self._packages_version)
|
||||||
|
|
||||||
|
# Network:
|
||||||
|
self._get_packages_request = None
|
||||||
|
self._get_showcase_request = None
|
||||||
|
self._download_request = None
|
||||||
|
self._download_reply = None
|
||||||
|
self._download_progress = 0
|
||||||
|
self._is_downloading = False
|
||||||
|
self._network_manager = None
|
||||||
|
self._request_header = [
|
||||||
|
b"User-Agent",
|
||||||
|
str.encode(
|
||||||
|
"%s/%s (%s %s)" % (
|
||||||
|
Application.getInstance().getApplicationName(),
|
||||||
|
Application.getInstance().getVersion(),
|
||||||
|
platform.system(),
|
||||||
|
platform.machine(),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
]
|
||||||
|
self._request_urls = {
|
||||||
|
"authors": QUrl("{base_url}/authors".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)),
|
||||||
|
"materials_showcase": QUrl("{base_url}/showcase".format(base_url = self._api_url))
|
||||||
|
}
|
||||||
|
|
||||||
|
# Data:
|
||||||
|
self._metadata = {
|
||||||
|
"authors": [],
|
||||||
|
"packages": [],
|
||||||
|
"plugins_showcase": [],
|
||||||
|
"plugins_installed": [],
|
||||||
|
"materials_showcase": [],
|
||||||
|
"materials_installed": []
|
||||||
|
}
|
||||||
|
|
||||||
|
# Models:
|
||||||
|
self._models = {
|
||||||
|
"authors": AuthorsModel(self),
|
||||||
|
"packages": PackagesModel(self),
|
||||||
|
"plugins_showcase": PackagesModel(self),
|
||||||
|
"plugins_available": PackagesModel(self),
|
||||||
|
"plugins_installed": PackagesModel(self),
|
||||||
|
"materials_showcase": AuthorsModel(self),
|
||||||
|
"materials_available": PackagesModel(self),
|
||||||
|
"materials_installed": PackagesModel(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
# These properties are for keeping track of the UI state:
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
# View category defines which filter to use, and therefore effectively
|
||||||
|
# which category is currently being displayed. For example, possible
|
||||||
|
# values include "plugin" or "material", but also "installed".
|
||||||
|
self._view_category = "plugin"
|
||||||
|
|
||||||
|
# View page defines which type of page layout to use. For example,
|
||||||
|
# possible values include "overview", "detail" or "author".
|
||||||
|
self._view_page = "loading"
|
||||||
|
|
||||||
|
# Active package refers to which package is currently being downloaded,
|
||||||
|
# installed, or otherwise modified.
|
||||||
|
self._active_package = None
|
||||||
|
|
||||||
|
self._dialog = None
|
||||||
|
self._restart_required = False
|
||||||
|
|
||||||
|
# variables for the license agreement dialog
|
||||||
|
self._license_dialog_plugin_name = ""
|
||||||
|
self._license_dialog_license_content = ""
|
||||||
|
self._license_dialog_plugin_file_location = ""
|
||||||
|
self._restart_dialog_message = ""
|
||||||
|
|
||||||
|
Application.getInstance().initializationFinished.connect(self._onAppInitialized)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Signals:
|
||||||
|
# --------------------------------------------------------------------------
|
||||||
|
# Downloading changes
|
||||||
|
activePackageChanged = pyqtSignal()
|
||||||
|
onDownloadProgressChanged = pyqtSignal()
|
||||||
|
onIsDownloadingChanged = pyqtSignal()
|
||||||
|
restartRequiredChanged = pyqtSignal()
|
||||||
|
installChanged = pyqtSignal()
|
||||||
|
enabledChanged = pyqtSignal()
|
||||||
|
|
||||||
|
# UI changes
|
||||||
|
viewChanged = pyqtSignal()
|
||||||
|
detailViewChanged = pyqtSignal()
|
||||||
|
filterChanged = pyqtSignal()
|
||||||
|
metadataChanged = pyqtSignal()
|
||||||
|
showLicenseDialog = pyqtSignal()
|
||||||
|
|
||||||
|
@pyqtSlot(result = str)
|
||||||
|
def getLicenseDialogPluginName(self) -> str:
|
||||||
|
return self._license_dialog_plugin_name
|
||||||
|
|
||||||
|
@pyqtSlot(result = str)
|
||||||
|
def getLicenseDialogPluginFileLocation(self) -> str:
|
||||||
|
return self._license_dialog_plugin_file_location
|
||||||
|
|
||||||
|
@pyqtSlot(result = str)
|
||||||
|
def getLicenseDialogLicenseContent(self) -> str:
|
||||||
|
return self._license_dialog_license_content
|
||||||
|
|
||||||
|
def openLicenseDialog(self, plugin_name: str, license_content: str, plugin_file_location: str):
|
||||||
|
self._license_dialog_plugin_name = plugin_name
|
||||||
|
self._license_dialog_license_content = license_content
|
||||||
|
self._license_dialog_plugin_file_location = plugin_file_location
|
||||||
|
self.showLicenseDialog.emit()
|
||||||
|
|
||||||
|
# This is a plugin, so most of the components required are not ready when
|
||||||
|
# this is initialized. Therefore, we wait until the application is ready.
|
||||||
|
def _onAppInitialized(self):
|
||||||
|
self._package_manager = Application.getInstance().getCuraPackageManager()
|
||||||
|
|
||||||
|
@pyqtSlot()
|
||||||
|
def browsePackages(self):
|
||||||
|
# Create the network manager:
|
||||||
|
# This was formerly its own function but really had no reason to be as
|
||||||
|
# it was never called more than once ever.
|
||||||
|
if self._network_manager:
|
||||||
|
self._network_manager.finished.disconnect(self._onRequestFinished)
|
||||||
|
self._network_manager.networkAccessibleChanged.disconnect(self._onNetworkAccessibleChanged)
|
||||||
|
self._network_manager = QNetworkAccessManager()
|
||||||
|
self._network_manager.finished.connect(self._onRequestFinished)
|
||||||
|
self._network_manager.networkAccessibleChanged.connect(self._onNetworkAccessibleChanged)
|
||||||
|
|
||||||
|
# Make remote requests:
|
||||||
|
self._makeRequestByType("packages")
|
||||||
|
self._makeRequestByType("authors")
|
||||||
|
self._makeRequestByType("plugins_showcase")
|
||||||
|
self._makeRequestByType("materials_showcase")
|
||||||
|
|
||||||
|
# Gather installed packages:
|
||||||
|
self._updateInstalledModels()
|
||||||
|
|
||||||
|
if not self._dialog:
|
||||||
|
self._dialog = self._createDialog("Toolbox.qml")
|
||||||
|
self._dialog.show()
|
||||||
|
|
||||||
|
# Apply enabled/disabled state to installed plugins
|
||||||
|
self.enabledChanged.emit()
|
||||||
|
|
||||||
|
def _createDialog(self, qml_name: str):
|
||||||
|
Logger.log("d", "Toolbox: Creating dialog [%s].", qml_name)
|
||||||
|
path = os.path.join(PluginRegistry.getInstance().getPluginPath(self.getPluginId()), "resources", "qml", qml_name)
|
||||||
|
dialog = Application.getInstance().createQmlComponent(path, {"toolbox": self})
|
||||||
|
return dialog
|
||||||
|
|
||||||
|
@pyqtSlot()
|
||||||
|
def _updateInstalledModels(self):
|
||||||
|
all_packages = self._package_manager.getAllInstalledPackagesInfo()
|
||||||
|
if "plugin" in all_packages:
|
||||||
|
self._metadata["plugins_installed"] = all_packages["plugin"]
|
||||||
|
self._models["plugins_installed"].setMetadata(self._metadata["plugins_installed"])
|
||||||
|
self.metadataChanged.emit()
|
||||||
|
if "material" in all_packages:
|
||||||
|
self._metadata["materials_installed"] = all_packages["material"]
|
||||||
|
self._models["materials_installed"].setMetadata(self._metadata["materials_installed"])
|
||||||
|
self.metadataChanged.emit()
|
||||||
|
|
||||||
|
@pyqtSlot(str)
|
||||||
|
def install(self, file_path: str):
|
||||||
|
self._package_manager.installPackage(file_path)
|
||||||
|
self.installChanged.emit()
|
||||||
|
self._updateInstalledModels()
|
||||||
|
self.metadataChanged.emit()
|
||||||
|
self._restart_required = True
|
||||||
|
self.restartRequiredChanged.emit()
|
||||||
|
|
||||||
|
@pyqtSlot(str)
|
||||||
|
def uninstall(self, plugin_id: str):
|
||||||
|
self._package_manager.removePackage(plugin_id)
|
||||||
|
self.installChanged.emit()
|
||||||
|
self._updateInstalledModels()
|
||||||
|
self.metadataChanged.emit()
|
||||||
|
self._restart_required = True
|
||||||
|
self.restartRequiredChanged.emit()
|
||||||
|
|
||||||
|
@pyqtSlot(str)
|
||||||
|
def enable(self, plugin_id: str):
|
||||||
|
self._plugin_registry.enablePlugin(plugin_id)
|
||||||
|
self.enabledChanged.emit()
|
||||||
|
Logger.log("i", "%s was set as 'active'.", plugin_id)
|
||||||
|
self._restart_required = True
|
||||||
|
self.restartRequiredChanged.emit()
|
||||||
|
|
||||||
|
@pyqtSlot(str)
|
||||||
|
def disable(self, plugin_id: str):
|
||||||
|
self._plugin_registry.disablePlugin(plugin_id)
|
||||||
|
self.enabledChanged.emit()
|
||||||
|
Logger.log("i", "%s was set as 'deactive'.", plugin_id)
|
||||||
|
self._restart_required = True
|
||||||
|
self.restartRequiredChanged.emit()
|
||||||
|
|
||||||
|
@pyqtProperty(bool, notify = metadataChanged)
|
||||||
|
def dataReady(self):
|
||||||
|
return self._packages_model is not None
|
||||||
|
|
||||||
|
@pyqtProperty(bool, notify = restartRequiredChanged)
|
||||||
|
def restartRequired(self):
|
||||||
|
return self._restart_required
|
||||||
|
|
||||||
|
@pyqtSlot()
|
||||||
|
def restart(self):
|
||||||
|
self._package_manager._removeAllScheduledPackages()
|
||||||
|
CuraApplication.getInstance().windowClosed()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Checks
|
||||||
|
# --------------------------------------------------------------------------
|
||||||
|
@pyqtSlot(str, result = bool)
|
||||||
|
def canUpdate(self, package_id: str) -> bool:
|
||||||
|
local_package = self._package_manager.getInstalledPackageInfo(package_id)
|
||||||
|
if local_package is None:
|
||||||
|
return False
|
||||||
|
|
||||||
|
remote_package = None
|
||||||
|
for package in self._metadata["packages"]:
|
||||||
|
if package["package_id"] == package_id:
|
||||||
|
remote_package = package
|
||||||
|
if remote_package is None:
|
||||||
|
return False
|
||||||
|
|
||||||
|
local_version = local_package["package_version"]
|
||||||
|
remote_version = remote_package["package_version"]
|
||||||
|
return Version(remote_version) > Version(local_version)
|
||||||
|
|
||||||
|
@pyqtSlot(str, result = bool)
|
||||||
|
def isInstalled(self, package_id: str) -> bool:
|
||||||
|
return self._package_manager.isPackageInstalled(package_id)
|
||||||
|
|
||||||
|
@pyqtSlot(str, result = bool)
|
||||||
|
def isEnabled(self, package_id: str) -> bool:
|
||||||
|
if package_id in self._plugin_registry.getActivePlugins():
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def loadingComplete(self) -> bool:
|
||||||
|
populated = 0
|
||||||
|
for list in self._metadata.items():
|
||||||
|
if len(list) > 0:
|
||||||
|
populated += 1
|
||||||
|
if populated == len(self._metadata.items()):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Make API Calls
|
||||||
|
# --------------------------------------------------------------------------
|
||||||
|
def _makeRequestByType(self, type: str):
|
||||||
|
Logger.log("i", "Toolbox: Requesting %s metadata from server.", type)
|
||||||
|
request = QNetworkRequest(self._request_urls[type])
|
||||||
|
request.setRawHeader(*self._request_header)
|
||||||
|
self._network_manager.get(request)
|
||||||
|
|
||||||
|
@pyqtSlot(str)
|
||||||
|
def startDownload(self, url: str):
|
||||||
|
Logger.log("i", "Toolbox: Attempting to download & install package from %s.", url)
|
||||||
|
url = QUrl(url)
|
||||||
|
self._download_request = QNetworkRequest(url)
|
||||||
|
self._download_request.setAttribute(QNetworkRequest.RedirectPolicyAttribute, QNetworkRequest.NoLessSafeRedirectPolicy)
|
||||||
|
self._download_request.setRawHeader(*self._request_header)
|
||||||
|
self._download_reply = self._network_manager.get(self._download_request)
|
||||||
|
self.setDownloadProgress(0)
|
||||||
|
self.setIsDownloading(True)
|
||||||
|
self._download_reply.downloadProgress.connect(self._onDownloadProgress)
|
||||||
|
|
||||||
|
@pyqtSlot()
|
||||||
|
def cancelDownload(self):
|
||||||
|
Logger.log("i", "Toolbox: User cancelled the download of a plugin.")
|
||||||
|
self.resetDownload()
|
||||||
|
return
|
||||||
|
|
||||||
|
def resetDownload(self):
|
||||||
|
self._download_reply.abort()
|
||||||
|
self._download_reply.downloadProgress.disconnect(self._onDownloadProgress)
|
||||||
|
self._download_reply = None
|
||||||
|
self._download_request = None
|
||||||
|
self.setDownloadProgress(0)
|
||||||
|
self.setIsDownloading(False)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Handlers for Network Events
|
||||||
|
# --------------------------------------------------------------------------
|
||||||
|
def _onNetworkAccessibleChanged(self, accessible: int):
|
||||||
|
if accessible == 0:
|
||||||
|
self.resetDownload()
|
||||||
|
|
||||||
|
def _onRequestFinished(self, reply: QNetworkReply):
|
||||||
|
|
||||||
|
if reply.error() == QNetworkReply.TimeoutError:
|
||||||
|
Logger.log("w", "Got a timeout.")
|
||||||
|
self.resetDownload()
|
||||||
|
return
|
||||||
|
|
||||||
|
if reply.error() == QNetworkReply.HostNotFoundError:
|
||||||
|
Logger.log("w", "Unable to reach server.")
|
||||||
|
self.resetDownload()
|
||||||
|
return
|
||||||
|
|
||||||
|
if reply.operation() == QNetworkAccessManager.GetOperation:
|
||||||
|
for type, url in self._request_urls.items():
|
||||||
|
if reply.url() == url:
|
||||||
|
try:
|
||||||
|
json_data = json.loads(bytes(reply.readAll()).decode("utf-8"))
|
||||||
|
|
||||||
|
# Check for errors:
|
||||||
|
if "errors" in json_data:
|
||||||
|
for error in json_data["errors"]:
|
||||||
|
Logger.log("e", "%s", error["title"])
|
||||||
|
return
|
||||||
|
|
||||||
|
# Create model and apply metadata:
|
||||||
|
if not self._models[type]:
|
||||||
|
Logger.log("e", "Could not find the %s model.", type)
|
||||||
|
break
|
||||||
|
|
||||||
|
# HACK: Eventually get rid of the code from here...
|
||||||
|
if type is "plugins_showcase" or type is "materials_showcase":
|
||||||
|
self._metadata["plugins_showcase"] = json_data["data"]["plugin"]["packages"]
|
||||||
|
self._models["plugins_showcase"].setMetadata(self._metadata["plugins_showcase"])
|
||||||
|
self._metadata["materials_showcase"] = json_data["data"]["material"]["authors"]
|
||||||
|
self._models["materials_showcase"].setMetadata(self._metadata["materials_showcase"])
|
||||||
|
else:
|
||||||
|
# ...until here.
|
||||||
|
# This hack arises for multiple reasons but the main
|
||||||
|
# one is because there are not separate API calls
|
||||||
|
# for different kinds of showcases.
|
||||||
|
self._metadata[type] = json_data["data"]
|
||||||
|
self._models[type].setMetadata(self._metadata[type])
|
||||||
|
|
||||||
|
# Do some auto filtering
|
||||||
|
# TODO: Make multiple API calls in the future to handle this
|
||||||
|
if type is "packages":
|
||||||
|
self._models[type].setFilter({"type": "plugin"})
|
||||||
|
if type is "authors":
|
||||||
|
self._models[type].setFilter({"package_types": "material"})
|
||||||
|
|
||||||
|
self.metadataChanged.emit()
|
||||||
|
|
||||||
|
if self.loadingComplete() is True:
|
||||||
|
self.setViewPage("overview")
|
||||||
|
|
||||||
|
return
|
||||||
|
except json.decoder.JSONDecodeError:
|
||||||
|
Logger.log("w", "Toolbox: Received invalid JSON for %s.", type)
|
||||||
|
break
|
||||||
|
|
||||||
|
else:
|
||||||
|
# Ignore any operation that is not a get operation
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _onDownloadProgress(self, bytes_sent: int, bytes_total: int):
|
||||||
|
if bytes_total > 0:
|
||||||
|
new_progress = bytes_sent / bytes_total * 100
|
||||||
|
self.setDownloadProgress(new_progress)
|
||||||
|
if bytes_sent == bytes_total:
|
||||||
|
self.setIsDownloading(False)
|
||||||
|
self._download_reply.downloadProgress.disconnect(self._onDownloadProgress)
|
||||||
|
# must not delete the temporary file on Windows
|
||||||
|
self._temp_plugin_file = tempfile.NamedTemporaryFile(mode = "w+b", suffix = ".curapackage", delete = False)
|
||||||
|
file_path = self._temp_plugin_file.name
|
||||||
|
# write first and close, otherwise on Windows, it cannot read the file
|
||||||
|
self._temp_plugin_file.write(self._download_reply.readAll())
|
||||||
|
self._temp_plugin_file.close()
|
||||||
|
self._onDownloadComplete(file_path)
|
||||||
|
return
|
||||||
|
|
||||||
|
def _onDownloadComplete(self, file_path: str):
|
||||||
|
Logger.log("i", "Toolbox: Download complete.")
|
||||||
|
try:
|
||||||
|
package_info = self._package_manager.getPackageInfo(file_path)
|
||||||
|
except:
|
||||||
|
Logger.logException("w", "Toolbox: Package file [%s] was not a valid CuraPackage.", file_path)
|
||||||
|
return
|
||||||
|
|
||||||
|
license_content = self._package_manager.getPackageLicense(file_path)
|
||||||
|
if license_content is not None:
|
||||||
|
self.openLicenseDialog(package_info["package_id"], license_content, file_path)
|
||||||
|
return
|
||||||
|
|
||||||
|
self.install(file_path)
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Getter & Setters for Properties:
|
||||||
|
# --------------------------------------------------------------------------
|
||||||
|
def setDownloadProgress(self, progress: int):
|
||||||
|
if progress != self._download_progress:
|
||||||
|
self._download_progress = progress
|
||||||
|
self.onDownloadProgressChanged.emit()
|
||||||
|
@pyqtProperty(int, fset = setDownloadProgress, notify = onDownloadProgressChanged)
|
||||||
|
def downloadProgress(self) -> int:
|
||||||
|
return self._download_progress
|
||||||
|
|
||||||
|
def setIsDownloading(self, is_downloading: bool):
|
||||||
|
if self._is_downloading != is_downloading:
|
||||||
|
self._is_downloading = is_downloading
|
||||||
|
self.onIsDownloadingChanged.emit()
|
||||||
|
@pyqtProperty(bool, fset = setIsDownloading, notify = onIsDownloadingChanged)
|
||||||
|
def isDownloading(self) -> bool:
|
||||||
|
return self._is_downloading
|
||||||
|
|
||||||
|
def setActivePackage(self, package: dict):
|
||||||
|
self._active_package = package
|
||||||
|
self.activePackageChanged.emit()
|
||||||
|
@pyqtProperty(QObject, fset = setActivePackage, notify = activePackageChanged)
|
||||||
|
def activePackage(self) -> dict:
|
||||||
|
return self._active_package
|
||||||
|
|
||||||
|
def setViewCategory(self, category: str = "plugin"):
|
||||||
|
self._view_category = category
|
||||||
|
self.viewChanged.emit()
|
||||||
|
@pyqtProperty(str, fset = setViewCategory, notify = viewChanged)
|
||||||
|
def viewCategory(self) -> str:
|
||||||
|
return self._view_category
|
||||||
|
|
||||||
|
def setViewPage(self, page: str = "overview"):
|
||||||
|
self._view_page = page
|
||||||
|
self.viewChanged.emit()
|
||||||
|
@pyqtProperty(str, fset = setViewPage, notify = viewChanged)
|
||||||
|
def viewPage(self) -> str:
|
||||||
|
return self._view_page
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Expose Models:
|
||||||
|
# --------------------------------------------------------------------------
|
||||||
|
@pyqtProperty(QObject, notify = metadataChanged)
|
||||||
|
def authorsModel(self) -> AuthorsModel:
|
||||||
|
return self._models["authors"]
|
||||||
|
|
||||||
|
@pyqtProperty(QObject, notify = metadataChanged)
|
||||||
|
def packagesModel(self) -> PackagesModel:
|
||||||
|
return self._models["packages"]
|
||||||
|
|
||||||
|
@pyqtProperty(QObject, notify = metadataChanged)
|
||||||
|
def pluginsShowcaseModel(self) -> PackagesModel:
|
||||||
|
return self._models["plugins_showcase"]
|
||||||
|
|
||||||
|
@pyqtProperty(QObject, notify = metadataChanged)
|
||||||
|
def pluginsInstalledModel(self) -> PackagesModel:
|
||||||
|
return self._models["plugins_installed"]
|
||||||
|
|
||||||
|
@pyqtProperty(QObject, notify = metadataChanged)
|
||||||
|
def materialsShowcaseModel(self) -> PackagesModel:
|
||||||
|
return self._models["materials_showcase"]
|
||||||
|
|
||||||
|
@pyqtProperty(QObject, notify = metadataChanged)
|
||||||
|
def materialsInstalledModel(self) -> PackagesModel:
|
||||||
|
return self._models["materials_installed"]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Filter Models:
|
||||||
|
# --------------------------------------------------------------------------
|
||||||
|
@pyqtSlot(str, str, str)
|
||||||
|
def filterModelByProp(self, modelType: str, filterType: str, parameter: str):
|
||||||
|
if not self._models[modelType]:
|
||||||
|
Logger.log("w", "Toolbox: Couldn't filter %s model because it doesn't exist.", modelType)
|
||||||
|
return
|
||||||
|
self._models[modelType].setFilter({ filterType: parameter })
|
||||||
|
self.filterChanged.emit()
|
||||||
|
|
||||||
|
@pyqtSlot()
|
||||||
|
def removeFilters(self, modelType: str):
|
||||||
|
if not self._models[modelType]:
|
||||||
|
Logger.log("w", "Toolbox: Couldn't remove filters on %s model because it doesn't exist.", modelType)
|
||||||
|
return
|
||||||
|
self._models[modelType].setFilter({})
|
||||||
|
self.filterChanged.emit()
|
0
plugins/Toolbox/src/__init__.py
Normal file
0
plugins/Toolbox/src/__init__.py
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
# Copyright (c) 2018 Ultimaker B.V.
|
||||||
|
# Cura is released under the terms of the LGPLv3 or higher.
|
||||||
|
|
||||||
|
import configparser #To parse preference files.
|
||||||
|
import io #To serialise the preference files afterwards.
|
||||||
|
|
||||||
|
from UM.VersionUpgrade import VersionUpgrade #We're inheriting from this.
|
||||||
|
|
||||||
|
|
||||||
|
## Upgrades configurations from the state they were in at version 3.2 to the
|
||||||
|
# state they should be in at version 3.3.
|
||||||
|
class VersionUpgrade33to34(VersionUpgrade):
|
||||||
|
|
||||||
|
## Gets the version number from a CFG file in Uranium's 3.2 format.
|
||||||
|
#
|
||||||
|
# Since the format may change, this is implemented for the 3.2 format only
|
||||||
|
# and needs to be included in the version upgrade system rather than
|
||||||
|
# globally in Uranium.
|
||||||
|
#
|
||||||
|
# \param serialised The serialised form of a CFG file.
|
||||||
|
# \return The version number stored in the CFG file.
|
||||||
|
# \raises ValueError The format of the version number in the file is
|
||||||
|
# incorrect.
|
||||||
|
# \raises KeyError The format of the file is incorrect.
|
||||||
|
def getCfgVersion(self, serialised):
|
||||||
|
parser = configparser.ConfigParser(interpolation = None)
|
||||||
|
parser.read_string(serialised)
|
||||||
|
format_version = int(parser.get("general", "version")) #Explicitly give an exception when this fails. That means that the file format is not recognised.
|
||||||
|
setting_version = int(parser.get("metadata", "setting_version", fallback = 0))
|
||||||
|
return format_version * 1000000 + setting_version
|
||||||
|
|
||||||
|
## Upgrades instance containers to have the new version
|
||||||
|
# number.
|
||||||
|
def upgradeInstanceContainer(self, serialized, filename):
|
||||||
|
parser = configparser.ConfigParser(interpolation = None)
|
||||||
|
parser.read_string(serialized)
|
||||||
|
|
||||||
|
# Update version number.
|
||||||
|
parser["general"]["version"] = "4"
|
||||||
|
|
||||||
|
result = io.StringIO()
|
||||||
|
parser.write(result)
|
||||||
|
return [filename], [result.getvalue()]
|
35
plugins/VersionUpgrade/VersionUpgrade33to34/__init__.py
Normal file
35
plugins/VersionUpgrade/VersionUpgrade33to34/__init__.py
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
# Copyright (c) 2018 Ultimaker B.V.
|
||||||
|
# Cura is released under the terms of the LGPLv3 or higher.
|
||||||
|
|
||||||
|
from . import VersionUpgrade33to34
|
||||||
|
|
||||||
|
upgrade = VersionUpgrade33to34.VersionUpgrade33to34()
|
||||||
|
|
||||||
|
|
||||||
|
def getMetaData():
|
||||||
|
return {
|
||||||
|
"version_upgrade": {
|
||||||
|
# From To Upgrade function
|
||||||
|
("definition_changes", 3000004): ("definition_changes", 4000004, upgrade.upgradeInstanceContainer),
|
||||||
|
("quality_changes", 3000004): ("quality_changes", 4000004, upgrade.upgradeInstanceContainer),
|
||||||
|
("user", 3000004): ("user", 4000004, upgrade.upgradeInstanceContainer),
|
||||||
|
},
|
||||||
|
"sources": {
|
||||||
|
"definition_changes": {
|
||||||
|
"get_version": upgrade.getCfgVersion,
|
||||||
|
"location": {"./definition_changes"}
|
||||||
|
},
|
||||||
|
"quality_changes": {
|
||||||
|
"get_version": upgrade.getCfgVersion,
|
||||||
|
"location": {"./quality"}
|
||||||
|
},
|
||||||
|
"user": {
|
||||||
|
"get_version": upgrade.getCfgVersion,
|
||||||
|
"location": {"./user"}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def register(app):
|
||||||
|
return { "version_upgrade": upgrade }
|
8
plugins/VersionUpgrade/VersionUpgrade33to34/plugin.json
Normal file
8
plugins/VersionUpgrade/VersionUpgrade33to34/plugin.json
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"name": "Version Upgrade 3.3 to 3.4",
|
||||||
|
"author": "Ultimaker B.V.",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "Upgrades configurations from Cura 3.3 to Cura 3.4.",
|
||||||
|
"api": 4,
|
||||||
|
"i18n-catalog": "cura"
|
||||||
|
}
|
|
@ -13,6 +13,13 @@ vertex =
|
||||||
}
|
}
|
||||||
|
|
||||||
fragment =
|
fragment =
|
||||||
|
#ifdef GL_ES
|
||||||
|
#ifdef GL_FRAGMENT_PRECISION_HIGH
|
||||||
|
precision highp float;
|
||||||
|
#else
|
||||||
|
precision mediump float;
|
||||||
|
#endif // GL_FRAGMENT_PRECISION_HIGH
|
||||||
|
#endif // GL_ES
|
||||||
uniform sampler2D u_layer0; //Default pass.
|
uniform sampler2D u_layer0; //Default pass.
|
||||||
uniform sampler2D u_layer1; //Selection pass.
|
uniform sampler2D u_layer1; //Selection pass.
|
||||||
uniform sampler2D u_layer2; //X-ray pass.
|
uniform sampler2D u_layer2; //X-ray pass.
|
||||||
|
|
|
@ -3288,6 +3288,18 @@
|
||||||
"settable_per_mesh": false,
|
"settable_per_mesh": false,
|
||||||
"settable_per_extruder": false
|
"settable_per_extruder": false
|
||||||
},
|
},
|
||||||
|
"retraction_combing_max_distance":
|
||||||
|
{
|
||||||
|
"label": "Max Comb Distance With No Retract",
|
||||||
|
"description": "When non-zero, combing travel moves that are longer than this distance will use retraction.",
|
||||||
|
"unit": "mm",
|
||||||
|
"type": "float",
|
||||||
|
"default_value": 0,
|
||||||
|
"minimum_value": "0",
|
||||||
|
"enabled": "resolveOrValue('retraction_combing') != 'off'",
|
||||||
|
"settable_per_mesh": false,
|
||||||
|
"settable_per_extruder": true
|
||||||
|
},
|
||||||
"travel_retract_before_outer_wall":
|
"travel_retract_before_outer_wall":
|
||||||
{
|
{
|
||||||
"label": "Retract Before Outer Wall",
|
"label": "Retract Before Outer Wall",
|
||||||
|
|
|
@ -68,7 +68,7 @@ Item
|
||||||
|
|
||||||
property alias configureSettingVisibility: configureSettingVisibilityAction
|
property alias configureSettingVisibility: configureSettingVisibilityAction
|
||||||
|
|
||||||
property alias browsePlugins: browsePluginsAction
|
property alias browsePackages: browsePackagesAction
|
||||||
|
|
||||||
UM.I18nCatalog{id: catalog; name:"cura"}
|
UM.I18nCatalog{id: catalog; name:"cura"}
|
||||||
|
|
||||||
|
@ -429,8 +429,8 @@ Item
|
||||||
|
|
||||||
Action
|
Action
|
||||||
{
|
{
|
||||||
id: browsePluginsAction
|
id: browsePackagesAction
|
||||||
text: catalog.i18nc("@action:menu", "Browse plugins...")
|
text: catalog.i18nc("@action:menu", "Browse packages...")
|
||||||
iconName: "plugins_browse"
|
iconName: "plugins_browse"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -271,9 +271,9 @@ UM.MainWindow
|
||||||
Menu
|
Menu
|
||||||
{
|
{
|
||||||
id: plugin_menu
|
id: plugin_menu
|
||||||
title: catalog.i18nc("@title:menu menubar:toplevel", "P&lugins")
|
title: catalog.i18nc("@title:menu menubar:toplevel", "&Toolbox")
|
||||||
|
|
||||||
MenuItem { action: Cura.Actions.browsePlugins }
|
MenuItem { action: Cura.Actions.browsePackages }
|
||||||
}
|
}
|
||||||
|
|
||||||
Menu
|
Menu
|
||||||
|
@ -323,31 +323,6 @@ UM.MainWindow
|
||||||
{
|
{
|
||||||
if (drop.urls.length > 0)
|
if (drop.urls.length > 0)
|
||||||
{
|
{
|
||||||
// As the drop area also supports plugins, first check if it's a plugin that was dropped.
|
|
||||||
if (drop.urls.length == 1)
|
|
||||||
{
|
|
||||||
if (PluginRegistry.isPluginFile(drop.urls[0]))
|
|
||||||
{
|
|
||||||
// Try to install plugin & close.
|
|
||||||
var result = PluginRegistry.installPlugin(drop.urls[0]);
|
|
||||||
pluginInstallDialog.text = result.message;
|
|
||||||
if (result.status == "ok")
|
|
||||||
{
|
|
||||||
pluginInstallDialog.icon = StandardIcon.Information;
|
|
||||||
}
|
|
||||||
else if (result.status == "duplicate")
|
|
||||||
{
|
|
||||||
pluginInstallDialog.icon = StandardIcon.Warning;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
pluginInstallDialog.icon = StandardIcon.Critical;
|
|
||||||
}
|
|
||||||
pluginInstallDialog.open();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
openDialog.handleOpenFileUrls(drop.urls);
|
openDialog.handleOpenFileUrls(drop.urls);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -669,9 +644,9 @@ UM.MainWindow
|
||||||
// show the plugin browser dialog
|
// show the plugin browser dialog
|
||||||
Connections
|
Connections
|
||||||
{
|
{
|
||||||
target: Cura.Actions.browsePlugins
|
target: Cura.Actions.browsePackages
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
curaExtensions.callExtensionMethod("Plugin Browser", "browsePlugins")
|
curaExtensions.callExtensionMethod("Toolbox", "browsePackages")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -814,14 +789,6 @@ UM.MainWindow
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MessageDialog
|
|
||||||
{
|
|
||||||
id: pluginInstallDialog
|
|
||||||
title: catalog.i18nc("@window:title", "Install Plugin");
|
|
||||||
standardButtons: StandardButton.Ok
|
|
||||||
modality: Qt.ApplicationModal
|
|
||||||
}
|
|
||||||
|
|
||||||
MessageDialog {
|
MessageDialog {
|
||||||
id: infoMultipleFilesWithGcodeDialog
|
id: infoMultipleFilesWithGcodeDialog
|
||||||
title: catalog.i18nc("@title:window", "Open File(s)")
|
title: catalog.i18nc("@title:window", "Open File(s)")
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
[general]
|
[general]
|
||||||
version = 3
|
version = 4
|
||||||
name = Fine
|
name = Fine
|
||||||
definition = abax_pri3
|
definition = abax_pri3
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
[general]
|
[general]
|
||||||
version = 3
|
version = 4
|
||||||
name = Extra Fine
|
name = Extra Fine
|
||||||
definition = abax_pri3
|
definition = abax_pri3
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
[general]
|
[general]
|
||||||
version = 3
|
version = 4
|
||||||
name = Fine
|
name = Fine
|
||||||
definition = abax_pri3
|
definition = abax_pri3
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
[general]
|
[general]
|
||||||
version = 3
|
version = 4
|
||||||
name = Fine
|
name = Fine
|
||||||
definition = abax_pri5
|
definition = abax_pri5
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
[general]
|
[general]
|
||||||
version = 3
|
version = 4
|
||||||
name = Extra Fine
|
name = Extra Fine
|
||||||
definition = abax_pri5
|
definition = abax_pri5
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
[general]
|
[general]
|
||||||
version = 3
|
version = 4
|
||||||
name = Fine
|
name = Fine
|
||||||
definition = abax_pri5
|
definition = abax_pri5
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
[general]
|
[general]
|
||||||
version = 3
|
version = 4
|
||||||
name = Fine
|
name = Fine
|
||||||
definition = abax_titan
|
definition = abax_titan
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
[general]
|
[general]
|
||||||
version = 3
|
version = 4
|
||||||
name = Extra Fine
|
name = Extra Fine
|
||||||
definition = abax_titan
|
definition = abax_titan
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
[general]
|
[general]
|
||||||
version = 3
|
version = 4
|
||||||
name = Fine
|
name = Fine
|
||||||
definition = abax_titan
|
definition = abax_titan
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
[general]
|
[general]
|
||||||
version = 3
|
version = 4
|
||||||
name = Draft
|
name = Draft
|
||||||
definition = anycubic_i3_mega
|
definition = anycubic_i3_mega
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
[general]
|
[general]
|
||||||
version = 3
|
version = 4
|
||||||
name = High
|
name = High
|
||||||
definition = anycubic_i3_mega
|
definition = anycubic_i3_mega
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
[general]
|
[general]
|
||||||
version = 3
|
version = 4
|
||||||
name = Normal
|
name = Normal
|
||||||
definition = anycubic_i3_mega
|
definition = anycubic_i3_mega
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
[general]
|
[general]
|
||||||
version = 3
|
version = 4
|
||||||
name = Coarse
|
name = Coarse
|
||||||
definition = builder_premium_small
|
definition = builder_premium_small
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
[general]
|
[general]
|
||||||
version = 3
|
version = 4
|
||||||
name = High Quality
|
name = High Quality
|
||||||
definition = builder_premium_small
|
definition = builder_premium_small
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
[general]
|
[general]
|
||||||
version = 3
|
version = 4
|
||||||
name = Normal
|
name = Normal
|
||||||
definition = builder_premium_small
|
definition = builder_premium_small
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
[general]
|
[general]
|
||||||
version = 3
|
version = 4
|
||||||
name = Coarse
|
name = Coarse
|
||||||
definition = builder_premium_small
|
definition = builder_premium_small
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
[general]
|
[general]
|
||||||
version = 3
|
version = 4
|
||||||
name = High Quality
|
name = High Quality
|
||||||
definition = builder_premium_small
|
definition = builder_premium_small
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
[general]
|
[general]
|
||||||
version = 3
|
version = 4
|
||||||
name = Normal
|
name = Normal
|
||||||
definition = builder_premium_small
|
definition = builder_premium_small
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
[general]
|
[general]
|
||||||
version = 3
|
version = 4
|
||||||
name = Coarse
|
name = Coarse
|
||||||
definition = builder_premium_small
|
definition = builder_premium_small
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
[general]
|
[general]
|
||||||
version = 3
|
version = 4
|
||||||
name = High Quality
|
name = High Quality
|
||||||
definition = builder_premium_small
|
definition = builder_premium_small
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
[general]
|
[general]
|
||||||
version = 3
|
version = 4
|
||||||
name = Normal
|
name = Normal
|
||||||
definition = builder_premium_small
|
definition = builder_premium_small
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
[general]
|
[general]
|
||||||
version = 3
|
version = 4
|
||||||
name = Coarse
|
name = Coarse
|
||||||
definition = builder_premium_small
|
definition = builder_premium_small
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
[general]
|
[general]
|
||||||
version = 3
|
version = 4
|
||||||
name = High Quality
|
name = High Quality
|
||||||
definition = builder_premium_small
|
definition = builder_premium_small
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
[general]
|
[general]
|
||||||
version = 3
|
version = 4
|
||||||
name = Normal
|
name = Normal
|
||||||
definition = builder_premium_small
|
definition = builder_premium_small
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
[general]
|
[general]
|
||||||
version = 3
|
version = 4
|
||||||
name = Coarse
|
name = Coarse
|
||||||
definition = builder_premium_small
|
definition = builder_premium_small
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
[general]
|
[general]
|
||||||
version = 3
|
version = 4
|
||||||
name = High Quality
|
name = High Quality
|
||||||
definition = builder_premium_small
|
definition = builder_premium_small
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
[general]
|
[general]
|
||||||
version = 3
|
version = 4
|
||||||
name = Normal
|
name = Normal
|
||||||
definition = builder_premium_small
|
definition = builder_premium_small
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
[general]
|
[general]
|
||||||
version = 3
|
version = 4
|
||||||
name = Coarse
|
name = Coarse
|
||||||
definition = builder_premium_small
|
definition = builder_premium_small
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
[general]
|
[general]
|
||||||
version = 3
|
version = 4
|
||||||
name = High Quality
|
name = High Quality
|
||||||
definition = builder_premium_small
|
definition = builder_premium_small
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
[general]
|
[general]
|
||||||
version = 3
|
version = 4
|
||||||
name = Normal
|
name = Normal
|
||||||
definition = builder_premium_small
|
definition = builder_premium_small
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
[general]
|
[general]
|
||||||
version = 3
|
version = 4
|
||||||
name = High
|
name = High
|
||||||
definition = cartesio
|
definition = cartesio
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
[general]
|
[general]
|
||||||
version = 3
|
version = 4
|
||||||
name = Normal
|
name = Normal
|
||||||
definition = cartesio
|
definition = cartesio
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
[general]
|
[general]
|
||||||
version = 3
|
version = 4
|
||||||
name = High
|
name = High
|
||||||
definition = cartesio
|
definition = cartesio
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
[general]
|
[general]
|
||||||
version = 3
|
version = 4
|
||||||
name = Normal
|
name = Normal
|
||||||
definition = cartesio
|
definition = cartesio
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
[general]
|
[general]
|
||||||
version = 3
|
version = 4
|
||||||
name = Coarse
|
name = Coarse
|
||||||
definition = cartesio
|
definition = cartesio
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
[general]
|
[general]
|
||||||
version = 3
|
version = 4
|
||||||
name = Extra Coarse
|
name = Extra Coarse
|
||||||
definition = cartesio
|
definition = cartesio
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
[general]
|
[general]
|
||||||
version = 3
|
version = 4
|
||||||
name = High
|
name = High
|
||||||
definition = cartesio
|
definition = cartesio
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
[general]
|
[general]
|
||||||
version = 3
|
version = 4
|
||||||
name = Normal
|
name = Normal
|
||||||
definition = cartesio
|
definition = cartesio
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
[general]
|
[general]
|
||||||
version = 3
|
version = 4
|
||||||
name = High
|
name = High
|
||||||
definition = cartesio
|
definition = cartesio
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
[general]
|
[general]
|
||||||
version = 3
|
version = 4
|
||||||
name = Normal
|
name = Normal
|
||||||
definition = cartesio
|
definition = cartesio
|
||||||
|
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue