mirror of
https://git.mirrors.martin98.com/https://github.com/Ultimaker/Cura
synced 2025-08-14 04:36:01 +08:00
Merge branch 'master' of https://github.com/x40-Community/Cura
This commit is contained in:
commit
22156a5421
@ -10,9 +10,9 @@ For crashes and similar issues, please attach the following information:
|
|||||||
|
|
||||||
* (On Windows) The log as produced by dxdiag (start -> run -> dxdiag -> save output)
|
* (On Windows) The log as produced by dxdiag (start -> run -> dxdiag -> save output)
|
||||||
* The Cura GUI log file, located at
|
* The Cura GUI log file, located at
|
||||||
* `%APPDATA%\cura\<Cura version>\cura.log` (Windows), or usually `C:\Users\\<your username>\AppData\Roaming\cura\<Cura version>\cura.log`
|
* `%APPDATA%\cura\<Cura version>\cura.log` (Windows), or usually `C:\Users\<your username>\AppData\Roaming\cura\<Cura version>\cura.log`
|
||||||
* `$USER/Library/Application Support/cura/<Cura version>/cura.log` (OSX)
|
* `$HOME/Library/Application Support/cura/<Cura version>/cura.log` (OSX)
|
||||||
* `$USER/.local/share/cura/<Cura version>/cura.log` (Ubuntu/Linux)
|
* `$HOME/.local/share/cura/<Cura version>/cura.log` (Ubuntu/Linux)
|
||||||
|
|
||||||
If the Cura user interface still starts, you can also reach this directory from the application menu in Help -> Show settings folder
|
If the Cura user interface still starts, you can also reach this directory from the application menu in Help -> Show settings folder
|
||||||
|
|
||||||
|
@ -109,7 +109,6 @@ class Account(QObject):
|
|||||||
self._authorization_service.accessTokenChanged.connect(self._onAccessTokenChanged)
|
self._authorization_service.accessTokenChanged.connect(self._onAccessTokenChanged)
|
||||||
self._authorization_service.loadAuthDataFromPreferences()
|
self._authorization_service.loadAuthDataFromPreferences()
|
||||||
|
|
||||||
|
|
||||||
@pyqtProperty(int, notify=syncStateChanged)
|
@pyqtProperty(int, notify=syncStateChanged)
|
||||||
def syncState(self):
|
def syncState(self):
|
||||||
return self._sync_state
|
return self._sync_state
|
||||||
@ -178,6 +177,7 @@ class Account(QObject):
|
|||||||
if error_message:
|
if error_message:
|
||||||
if self._error_message:
|
if self._error_message:
|
||||||
self._error_message.hide()
|
self._error_message.hide()
|
||||||
|
Logger.log("w", "Failed to login: %s", error_message)
|
||||||
self._error_message = Message(error_message, title = i18n_catalog.i18nc("@info:title", "Login failed"))
|
self._error_message = Message(error_message, title = i18n_catalog.i18nc("@info:title", "Login failed"))
|
||||||
self._error_message.show()
|
self._error_message.show()
|
||||||
self._logged_in = False
|
self._logged_in = False
|
||||||
|
@ -2,9 +2,10 @@
|
|||||||
# Cura is released under the terms of the LGPLv3 or higher.
|
# Cura is released under the terms of the LGPLv3 or higher.
|
||||||
|
|
||||||
import copy # To duplicate materials.
|
import copy # To duplicate materials.
|
||||||
from PyQt5.QtCore import QObject, pyqtSignal, pyqtSlot # To allow the preference page proxy to be used from the actual preferences page.
|
from PyQt5.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject, QUrl
|
||||||
from typing import Any, Dict, Optional, TYPE_CHECKING
|
from typing import Any, Dict, Optional, TYPE_CHECKING
|
||||||
import uuid # To generate new GUIDs for new materials.
|
import uuid # To generate new GUIDs for new materials.
|
||||||
|
import zipfile # To export all materials in a .zip archive.
|
||||||
|
|
||||||
from UM.i18n import i18nCatalog
|
from UM.i18n import i18nCatalog
|
||||||
from UM.Logger import Logger
|
from UM.Logger import Logger
|
||||||
@ -20,11 +21,6 @@ if TYPE_CHECKING:
|
|||||||
catalog = i18nCatalog("cura")
|
catalog = i18nCatalog("cura")
|
||||||
|
|
||||||
class MaterialManagementModel(QObject):
|
class MaterialManagementModel(QObject):
|
||||||
"""Proxy class to the materials page in the preferences.
|
|
||||||
|
|
||||||
This class handles the actions in that page, such as creating new materials, renaming them, etc.
|
|
||||||
"""
|
|
||||||
|
|
||||||
favoritesChanged = pyqtSignal(str)
|
favoritesChanged = pyqtSignal(str)
|
||||||
"""Triggered when a favorite is added or removed.
|
"""Triggered when a favorite is added or removed.
|
||||||
|
|
||||||
@ -264,3 +260,40 @@ class MaterialManagementModel(QObject):
|
|||||||
self.favoritesChanged.emit(material_base_file)
|
self.favoritesChanged.emit(material_base_file)
|
||||||
except ValueError: # Material was not in the favorites list.
|
except ValueError: # Material was not in the favorites list.
|
||||||
Logger.log("w", "Material {material_base_file} was already not a favorite material.".format(material_base_file = material_base_file))
|
Logger.log("w", "Material {material_base_file} was already not a favorite material.".format(material_base_file = material_base_file))
|
||||||
|
|
||||||
|
@pyqtSlot(result = QUrl)
|
||||||
|
def getPreferredExportAllPath(self) -> QUrl:
|
||||||
|
"""
|
||||||
|
Get the preferred path to export materials to.
|
||||||
|
|
||||||
|
If there is a removable drive, that should be the preferred path. Otherwise it should be the most recent local
|
||||||
|
file path.
|
||||||
|
:return: The preferred path to export all materials to.
|
||||||
|
"""
|
||||||
|
cura_application = cura.CuraApplication.CuraApplication.getInstance()
|
||||||
|
device_manager = cura_application.getOutputDeviceManager()
|
||||||
|
devices = device_manager.getOutputDevices()
|
||||||
|
for device in devices:
|
||||||
|
if device.__class__.__name__ == "RemovableDriveOutputDevice":
|
||||||
|
return QUrl.fromLocalFile(device.getId())
|
||||||
|
else: # No removable drives? Use local path.
|
||||||
|
return cura_application.getDefaultPath("dialog_material_path")
|
||||||
|
|
||||||
|
@pyqtSlot(QUrl)
|
||||||
|
def exportAll(self, file_path: QUrl) -> None:
|
||||||
|
"""
|
||||||
|
Export all materials to a certain file path.
|
||||||
|
:param file_path: The path to export the materials to.
|
||||||
|
"""
|
||||||
|
registry = CuraContainerRegistry.getInstance()
|
||||||
|
|
||||||
|
archive = zipfile.ZipFile(file_path.toLocalFile(), "w", compression = zipfile.ZIP_DEFLATED)
|
||||||
|
for metadata in registry.findInstanceContainersMetadata(type = "material"):
|
||||||
|
if metadata["base_file"] != metadata["id"]: # Only process base files.
|
||||||
|
continue
|
||||||
|
if metadata["id"] == "empty_material": # Don't export the empty material.
|
||||||
|
continue
|
||||||
|
material = registry.findContainers(id = metadata["id"])[0]
|
||||||
|
suffix = registry.getMimeTypeForContainer(type(material)).preferredSuffix
|
||||||
|
filename = metadata["id"] + "." + suffix
|
||||||
|
archive.writestr(filename, material.serialize())
|
||||||
|
@ -54,6 +54,7 @@ class LocalAuthorizationServer:
|
|||||||
if self._web_server:
|
if self._web_server:
|
||||||
# If the server is already running (because of a previously aborted auth flow), we don't have to start it.
|
# If the server is already running (because of a previously aborted auth flow), we don't have to start it.
|
||||||
# We still inject the new verification code though.
|
# We still inject the new verification code though.
|
||||||
|
Logger.log("d", "Auth web server was already running. Updating the verification code")
|
||||||
self._web_server.setVerificationCode(verification_code)
|
self._web_server.setVerificationCode(verification_code)
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -85,6 +86,7 @@ class LocalAuthorizationServer:
|
|||||||
except OSError:
|
except OSError:
|
||||||
# OS error can happen if the socket was already closed. We really don't care about that case.
|
# OS error can happen if the socket was already closed. We really don't care about that case.
|
||||||
pass
|
pass
|
||||||
|
Logger.log("d", "Local oauth2 web server was shut down")
|
||||||
self._web_server = None
|
self._web_server = None
|
||||||
self._web_server_thread = None
|
self._web_server_thread = None
|
||||||
|
|
||||||
@ -96,12 +98,13 @@ class LocalAuthorizationServer:
|
|||||||
|
|
||||||
:return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
|
Logger.log("d", "Local web server for authorization has started")
|
||||||
if self._web_server:
|
if self._web_server:
|
||||||
if sys.platform == "win32":
|
if sys.platform == "win32":
|
||||||
try:
|
try:
|
||||||
self._web_server.serve_forever()
|
self._web_server.serve_forever()
|
||||||
except OSError as e:
|
except OSError:
|
||||||
Logger.warning(str(e))
|
Logger.logException("w", "An exception happened while serving the auth server")
|
||||||
else:
|
else:
|
||||||
# Leave the default behavior in non-windows platforms
|
# Leave the default behavior in non-windows platforms
|
||||||
self._web_server.serve_forever()
|
self._web_server.serve_forever()
|
||||||
|
@ -86,6 +86,14 @@ class GlobalStack(CuraContainerStack):
|
|||||||
def supportsNetworkConnection(self):
|
def supportsNetworkConnection(self):
|
||||||
return self.getMetaDataEntry("supports_network_connection", False)
|
return self.getMetaDataEntry("supports_network_connection", False)
|
||||||
|
|
||||||
|
@pyqtProperty(bool, constant = True)
|
||||||
|
def supportsMaterialExport(self):
|
||||||
|
"""
|
||||||
|
Whether the printer supports Cura's export format of material profiles.
|
||||||
|
:return: ``True`` if it supports it, or ``False`` if not.
|
||||||
|
"""
|
||||||
|
return self.getMetaDataEntry("supports_material_export", False)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def getLoadingPriority(cls) -> int:
|
def getLoadingPriority(cls) -> int:
|
||||||
return 2
|
return 2
|
||||||
|
@ -4,12 +4,12 @@
|
|||||||
import argparse #To run the engine in debug mode if the front-end is in debug mode.
|
import argparse #To run the engine in debug mode if the front-end is in debug mode.
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
import os
|
import os
|
||||||
from PyQt5.QtCore import QObject, QTimer, pyqtSlot
|
from PyQt5.QtCore import QObject, QTimer, QUrl, pyqtSlot
|
||||||
import sys
|
import sys
|
||||||
from time import time
|
from time import time
|
||||||
from typing import Any, cast, Dict, List, Optional, Set, TYPE_CHECKING
|
from typing import Any, cast, Dict, List, Optional, Set, TYPE_CHECKING
|
||||||
|
|
||||||
from PyQt5.QtGui import QImage
|
from PyQt5.QtGui import QDesktopServices, QImage
|
||||||
|
|
||||||
from UM.Backend.Backend import Backend, BackendState
|
from UM.Backend.Backend import Backend, BackendState
|
||||||
from UM.Scene.SceneNode import SceneNode
|
from UM.Scene.SceneNode import SceneNode
|
||||||
@ -157,6 +157,18 @@ class CuraEngineBackend(QObject, Backend):
|
|||||||
self.determineAutoSlicing()
|
self.determineAutoSlicing()
|
||||||
application.getPreferences().preferenceChanged.connect(self._onPreferencesChanged)
|
application.getPreferences().preferenceChanged.connect(self._onPreferencesChanged)
|
||||||
|
|
||||||
|
self._slicing_error_message = Message(
|
||||||
|
text = catalog.i18nc("@message", "Slicing failed with an unexpected error. Please consider reporting a bug on our issue tracker."),
|
||||||
|
title = catalog.i18nc("@message:title", "Slicing failed")
|
||||||
|
)
|
||||||
|
self._slicing_error_message.addAction(
|
||||||
|
action_id = "report_bug",
|
||||||
|
name = catalog.i18nc("@message:button", "Report a bug"),
|
||||||
|
description = catalog.i18nc("@message:description", "Report a bug on Ultimaker Cura's issue tracker."),
|
||||||
|
icon = "[no_icon]"
|
||||||
|
)
|
||||||
|
self._slicing_error_message.actionTriggered.connect(self._reportBackendError)
|
||||||
|
|
||||||
self._snapshot = None #type: Optional[QImage]
|
self._snapshot = None #type: Optional[QImage]
|
||||||
|
|
||||||
application.initializationFinished.connect(self.initialize)
|
application.initializationFinished.connect(self.initialize)
|
||||||
@ -922,9 +934,22 @@ class CuraEngineBackend(QObject, Backend):
|
|||||||
|
|
||||||
if not self._restart:
|
if not self._restart:
|
||||||
if self._process: # type: ignore
|
if self._process: # type: ignore
|
||||||
Logger.log("d", "Backend quit with return code %s. Resetting process and socket.", self._process.wait()) # type: ignore
|
return_code = self._process.wait()
|
||||||
|
if return_code != 0:
|
||||||
|
Logger.log("e", f"Backend exited abnormally with return code {return_code}!")
|
||||||
|
self._slicing_error_message.show()
|
||||||
|
self.setState(BackendState.Error)
|
||||||
|
self.stopSlicing()
|
||||||
|
else:
|
||||||
|
Logger.log("d", "Backend finished slicing. Resetting process and socket.")
|
||||||
self._process = None # type: ignore
|
self._process = None # type: ignore
|
||||||
|
|
||||||
|
def _reportBackendError(self, _message_id: str, _action_id: str) -> None:
|
||||||
|
"""
|
||||||
|
Triggered when the user wants to report an error in the back-end.
|
||||||
|
"""
|
||||||
|
QDesktopServices.openUrl(QUrl("https://github.com/Ultimaker/Cura/issues/new/choose"))
|
||||||
|
|
||||||
def _onGlobalStackChanged(self) -> None:
|
def _onGlobalStackChanged(self) -> None:
|
||||||
"""Called when the global container stack changes"""
|
"""Called when the global container stack changes"""
|
||||||
|
|
||||||
|
@ -0,0 +1,52 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 378.13 348.13" version="1.1">
|
||||||
|
<defs
|
||||||
|
id="defs7">
|
||||||
|
<style
|
||||||
|
id="style2">
|
||||||
|
.cls-2,.cls-6{fill:#c5dbfb;}
|
||||||
|
.cls-6,.cls-7{stroke-width:2px;}
|
||||||
|
.cls-7{fill:#f3f8fe;}
|
||||||
|
.cls-6,.cls-7{stroke:#061884;}
|
||||||
|
</style>
|
||||||
|
</defs>
|
||||||
|
<path class="cls-2" d="M43,17V3H83a2,2,0,0,1,2,2V17Z" />
|
||||||
|
<path fill="white" d="M 3 1 C 1.8954305 1 1 1.8954305 1 3 L 1 67 C 1 68.104569 1.8954305 69 3 69 L 72.152344 69 A 100 100 0 0 1 89 49.873047 L 89 19 C 89 17.895431 88.104569 17 87 17 L 56 17 L 40 1 L 3 1 z " />
|
||||||
|
<path fill="#c5dbfb" d="M 3 0 C 1.3549904 0 0 1.3549904 0 3 L 0 67 C 0 68.64501 1.3549904 70 3 70 L 71.484375 70 A 100 100 0 0 1 72.835938 68 L 3 68 C 2.4358706 68 2 67.564129 2 67 L 2 3 C 2 2.4358706 2.4358706 2 3 2 L 39.585938 2 L 55.585938 18 L 87 18 C 87.564129 18 88 18.435871 88 19 L 88 50.765625 A 100 100 0 0 1 90 49.007812 L 90 19 C 90 17.35499 88.64501 16 87 16 L 56.414062 16 L 40.414062 0 L 3 0 z " />
|
||||||
|
<path class="cls-2" d="M153,17V3h40a2,2,0,0,1,2,2V17Z" />
|
||||||
|
<path fill="white" d="M 113 1 C 111.89543 1 111 1.8954305 111 3 L 111 35.201172 A 100 100 0 0 1 155 25 A 100 100 0 0 1 199 35.201172 L 199 19 C 199 17.895431 198.10457 17 197 17 L 166 17 L 150 1 L 113 1 z " />
|
||||||
|
<path fill="#c5dbfb" d="M 113 0 C 111.35499 0 110 1.3549904 110 3 L 110 35.699219 A 100 100 0 0 1 112 34.716797 L 112 3 C 112 2.4358706 112.43587 2 113 2 L 149.58594 2 L 165.58594 18 L 197 18 C 197.56413 18 198 18.435871 198 19 L 198 34.716797 A 100 100 0 0 1 200 35.699219 L 200 19 C 200 17.35499 198.64501 16 197 16 L 166.41406 16 L 150.41406 0 L 113 0 z " />
|
||||||
|
<path class="cls-2" d="M263,17V3h40a2,2,0,0,1,2,2V17Z" />
|
||||||
|
<path fill="white" d="M 223 1 C 221.89543 1 221 1.8954305 221 3 L 221 49.875 A 100 100 0 0 1 237.84961 69 L 307 69 C 308.10457 69 309 68.104569 309 67 L 309 19 C 309 17.895431 308.10457 17 307 17 L 276 17 L 260 1 L 223 1 z " />
|
||||||
|
<path fill="#c5dbfb" d="M 223 0 C 221.35499 0 220 1.3549904 220 3 L 220 49.005859 A 100 100 0 0 1 222 50.765625 L 222 3 C 222 2.4358706 222.43587 2 223 2 L 259.58594 2 L 275.58594 18 L 307 18 C 307.56413 18 308 18.435871 308 19 L 308 67 C 308 67.564129 307.56413 68 307 68 L 237.16406 68 A 100 100 0 0 1 238.51562 70 L 307 70 C 308.64501 70 310 68.64501 310 67 L 310 19 C 310 17.35499 308.64501 16 307 16 L 276.41406 16 L 260.41406 0 L 223 0 z " />
|
||||||
|
<path fill="#c5dbfb" d="M 43 93 L 43 107 L 56.634766 107 A 100 100 0 0 1 60.259766 93 L 43 93 z " />
|
||||||
|
<path fill="white" d="M 3 91 C 1.8954305 91 1 91.895431 1 93 L 1 157 C 1 158.10457 1.8954305 159 3 159 L 60.958984 159 A 100 100 0 0 1 55 125 A 100 100 0 0 1 56.634766 107 L 56 107 L 40 91 L 3 91 z " />
|
||||||
|
<path fill="#c5dbfb" d="M 3 90 C 1.3549904 90 0 91.35499 0 93 L 0 157 C 0 158.64501 1.3549904 160 3 160 L 61.324219 160 A 100 100 0 0 1 60.603516 158 L 3 158 C 2.4358706 158 2 157.56413 2 157 L 2 93 C 2 92.435871 2.4358706 92 3 92 L 39.585938 92 L 55.585938 108 L 56.455078 108 A 100 100 0 0 1 56.822266 106 L 56.414062 106 L 40.414062 90 L 3 90 z " />
|
||||||
|
<path class="cls-2" d="M263,107V93h40a2,2,0,0,1,2,2v12Z" />
|
||||||
|
<path fill="white" d="M 249.04102 91 A 100 100 0 0 1 255 125 A 100 100 0 0 1 249.04102 159 L 307 159 C 308.10457 159 309 158.10457 309 157 L 309 109 C 309 107.89543 308.10457 107 307 107 L 276 107 L 260 91 L 249.04102 91 z " />
|
||||||
|
<path fill="#c5dbfb" d="M 248.67578 90 A 100 100 0 0 1 249.39648 92 L 259.58594 92 L 275.58594 108 L 307 108 C 307.56413 108 308 108.43587 308 109 L 308 157 C 308 157.56413 307.56413 158 307 158 L 249.39844 158 A 100 100 0 0 1 248.67383 160 L 307 160 C 308.64501 160 310 158.64501 310 157 L 310 109 C 310 107.35499 308.64501 106 307 106 L 276.41406 106 L 260.41406 90 L 248.67578 90 z " />
|
||||||
|
<path fill="#c5dbfb" d="M 43 183 L 43 197 L 85 197 L 85 196.41406 A 100 100 0 0 1 73.539062 183 L 43 183 z " />
|
||||||
|
<path fill="white" d="M 3 181 C 1.8954305 181 1 181.89543 1 183 L 1 247 C 1 248.10457 1.8954305 249 3 249 L 87 249 C 88.104569 249 89 248.10457 89 247 L 89 200.125 A 100 100 0 0 1 85.603516 197 L 56 197 L 40 181 L 3 181 z " />
|
||||||
|
<path fill="#c5dbfb" d="M 3 180 C 1.3549904 180 0 181.35499 0 183 L 0 247 C 0 248.64501 1.3549904 250 3 250 L 87 250 C 88.64501 250 90 248.64501 90 247 L 90 200.99414 A 100 100 0 0 1 88 199.23438 L 88 247 C 88 247.56413 87.564129 248 87 248 L 3 248 C 2.4358706 248 2 247.56413 2 247 L 2 183 C 2 182.43587 2.4358706 182 3 182 L 39.585938 182 L 55.585938 198 L 86.65625 198 A 100 100 0 0 1 84.580078 196 L 56.414062 196 L 40.414062 180 L 3 180 z " />
|
||||||
|
<path fill="white" d="M 111 214.79883 L 111 247 C 111 248.10457 111.89543 249 113 249 L 197 249 C 198.10457 249 199 248.10457 199 247 L 199 214.79883 A 100 100 0 0 1 155 225 A 100 100 0 0 1 111 214.79883 z " />
|
||||||
|
<path fill="#c5dbfb" d="M 110 214.30078 L 110 247 C 110 248.64501 111.35499 250 113 250 L 197 250 C 198.64501 250 200 248.64501 200 247 L 200 214.30078 A 100 100 0 0 1 198 215.2832 L 198 247 C 198 247.56413 197.56413 248 197 248 L 113 248 C 112.43587 248 112 247.56413 112 247 L 112 215.2832 A 100 100 0 0 1 110 214.30078 z " />
|
||||||
|
<path class="cls-2" d="M263,197V183h40a2,2,0,0,1,2,2v12Z" />
|
||||||
|
<path fill="white" d="M 237.84766 181 A 100 100 0 0 1 221 200.12695 L 221 247 C 221 248.10457 221.89543 249 223 249 L 307 249 C 308.10457 249 309 248.10457 309 247 L 309 199 C 309 197.89543 308.10457 197 307 197 L 276 197 L 260 181 L 237.84766 181 z " />
|
||||||
|
<path fill="#c5dbfb" d="M 238.51562 180 A 100 100 0 0 1 237.16406 182 L 259.58594 182 L 275.58594 198 L 307 198 C 307.56413 198 308 198.43587 308 199 L 308 247 C 308 247.56413 307.56413 248 307 248 L 223 248 C 222.43587 248 222 247.56413 222 247 L 222 199.23438 A 100 100 0 0 1 220 200.99219 L 220 247 C 220 248.64501 221.35499 250 223 250 L 307 250 C 308.64501 250 310 248.64501 310 247 L 310 199 C 310 197.35499 308.64501 196 307 196 L 276.41406 196 L 260.41406 180 L 238.51562 180 z " />
|
||||||
|
<path class="cls-6" d="M351.12,322.62h20a10,10,0,0,1,10,10v7a0,0,0,0,1,0,0h-40a0,0,0,0,1,0,0v-7A10,10,0,0,1,351.12,322.62Z" transform="translate(850.61 309.91) rotate(135)" />
|
||||||
|
<rect class="cls-7" x="293.75" y="225.25" width="40" height="117" transform="translate(-108.74 304.96) rotate(-45)" />
|
||||||
|
<polyline class="cls-7" points="213.69 199.25 252.58 238.14 267.43 223.29 228.54 184.4" />
|
||||||
|
<path fill="white" stroke="#061884" stroke-width="2px" d="M 154.94141 30 A 95 95 0 0 0 60 125 A 95 95 0 0 0 155 220 A 95 95 0 0 0 250 125 A 95 95 0 0 0 155 30 A 95 95 0 0 0 154.94141 30 z M 154.82812 40 A 85 85 0 0 1 155 40 A 85 85 0 0 1 240 125 A 85 85 0 0 1 155 210 A 85 85 0 0 1 70 125 A 85 85 0 0 1 154.82812 40 z " />
|
||||||
|
<path class="cls-6" d="M256.37,227.87h20a10,10,0,0,1,10,10v7a0,0,0,0,1,0,0h-40a0,0,0,0,1,0,0v-7a10,10,0,0,1,10-10Z" transform="translate(-89.12 257.58) rotate(-45)" />
|
||||||
|
<path fill="white" d="M 154.94141 45 A 80 80 0 0 0 111 58.185547 L 111 67 C 111 68.104569 111.89543 69 113 69 L 197 69 C 198.10457 69 199 68.104569 199 67 L 199 58.1875 A 80 80 0 0 0 155 45 A 80 80 0 0 0 154.94141 45 z " />
|
||||||
|
<path fill="#061884" d="M 112 57.539062 A 80 80 0 0 0 110 58.857422 L 110 67 C 110 68.64501 111.35499 70 113 70 L 197 70 C 198.64501 70 200 68.64501 200 67 L 200 58.857422 A 80 80 0 0 0 198 57.541016 L 198 67 C 198 67.564129 197.56413 68 197 68 L 113 68 C 112.43587 68 112 67.564129 112 67 L 112 57.539062 z " />
|
||||||
|
<path fill="#196ef0" d="M 81.679688 93 A 80 80 0 0 0 77.050781 107 L 85 107 L 85 95 A 2 2 0 0 0 83 93 L 81.679688 93 z " />
|
||||||
|
<path fill="white" d="M 77.050781 107 A 80 80 0 0 0 75 125 A 80 80 0 0 0 82.585938 159 L 87 159 C 88.104569 159 89 158.10457 89 157 L 89 109 C 89 107.89543 88.104569 107 87 107 L 77.050781 107 z " />
|
||||||
|
<path fill="#061884" d="M 77.289062 106 A 80 80 0 0 0 76.828125 108 L 87 108 C 87.564129 108 88 108.43587 88 109 L 88 157 C 88 157.56413 87.564129 158 87 158 L 82.125 158 A 80 80 0 0 0 83.0625 160 L 87 160 C 88.64501 160 90 158.64501 90 157 L 90 109 C 90 107.35499 88.64501 106 87 106 L 77.289062 106 z " />
|
||||||
|
<path fill="white" d="M 223 91 C 221.89543 91 221 91.895431 221 93 L 221 157 C 221 158.10457 221.89543 159 223 159 L 227.41406 159 A 80 80 0 0 0 235 125 A 80 80 0 0 0 227.41406 91 L 223 91 z " />
|
||||||
|
<path fill="#061884" d="M 223 90 C 221.35499 90 220 91.35499 220 93 L 220 157 C 220 158.64501 221.35499 160 223 160 L 226.9375 160 A 80 80 0 0 0 227.87695 158 L 223 158 C 222.43587 158 222 157.56413 222 157 L 222 93 C 222 92.435871 222.43587 92 223 92 L 227.875 92 A 80 80 0 0 0 226.9375 90 L 223 90 z " />
|
||||||
|
<path fill="#196ef0" d="M 153 183 L 153 197 L 189.86914 197 A 80 80 0 0 0 195 194.28125 L 195 185 A 2 2 0 0 0 193 183 L 153 183 z "/>
|
||||||
|
<path fill="white" d="M 113 181 C 111.89543 181 111 181.89543 111 183 L 111 191.8125 A 80 80 0 0 0 155 205 A 80 80 0 0 0 189.86914 197 L 166 197 L 150 181 L 113 181 z " />
|
||||||
|
<path fill="#061884" d="M 113 180 C 111.35499 180 110 181.35499 110 183 L 110 191.14258 A 80 80 0 0 0 112 192.45898 L 112 183 C 112 182.43587 112.43587 182 113 182 L 149.58594 182 L 165.58594 198 L 187.72461 198 A 80 80 0 0 0 191.86328 196 L 166.41406 196 L 150.41406 180 L 113 180 z " />
|
||||||
|
<path fill="#061884" d="m 149.18,133.69 v -3.48 a 14.36,14.36 0 0 1 1.74,-7.25 20.17,20.17 0 0 1 6.4,-6.17 25.87,25.87 0 0 0 5.68,-4.79 7,7 0 0 0 1.48,-4.34 4.13,4.13 0 0 0 -1.93,-3.62 9,9 0 0 0 -5.14,-1.3 24.94,24.94 0 0 0 -7.34,1.16 45.2,45.2 0 0 0 -7.78,3.31 l -5.37,-10.64 a 48.41,48.41 0 0 1 9.89,-4.21 40.25,40.25 0 0 1 11.67,-1.61 q 9.57,0 14.9,4.43 a 14.16,14.16 0 0 1 5.32,11.41 15.41,15.41 0 0 1 -2.55,9 30.38,30.38 0 0 1 -7.92,7.34 32.11,32.11 0 0 0 -5.23,4.37 5.91,5.91 0 0 0 -1.34,4 v 2.41 z m -1.61,15.12 q 0,-4.38 2.46,-6.12 a 10,10 0 0 1 5.95,-1.75 9.69,9.69 0 0 1 5.77,1.75 q 2.46,1.74 2.46,6.12 0,4.22 -2.46,6 a 9.42,9.42 0 0 1 -5.77,1.84 9.69,9.69 0 0 1 -5.95,-1.84 q -2.46,-1.81 -2.46,-6 z" />
|
||||||
|
</svg>
|
After Width: | Height: | Size: 9.6 KiB |
@ -93,7 +93,7 @@ Popup
|
|||||||
}
|
}
|
||||||
validator: RegExpValidator
|
validator: RegExpValidator
|
||||||
{
|
{
|
||||||
regExp: /^[^\\\/\*\?\|\[\]]{0,96}$/
|
regExp: /^[^\\\/\*\?\|\[\]]{0,99}$/
|
||||||
}
|
}
|
||||||
|
|
||||||
text: PrintInformation.jobName
|
text: PrintInformation.jobName
|
||||||
@ -148,7 +148,7 @@ Popup
|
|||||||
anchors.bottom: parent.bottom
|
anchors.bottom: parent.bottom
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
text: "Create"
|
text: "Create"
|
||||||
enabled: newProjectNameTextField.text != "" && !busy
|
enabled: newProjectNameTextField.text.length >= 2 && !busy
|
||||||
|
|
||||||
onClicked:
|
onClicked:
|
||||||
{
|
{
|
||||||
|
@ -63,7 +63,7 @@ Item
|
|||||||
anchors.topMargin: UM.Theme.getSize("thin_margin").height
|
anchors.topMargin: UM.Theme.getSize("thin_margin").height
|
||||||
validator: RegExpValidator
|
validator: RegExpValidator
|
||||||
{
|
{
|
||||||
regExp: /^[^\\\/\*\?\|\[\]]{0,96}$/
|
regExp: /^[\w\-\. ()]{0,255}$/
|
||||||
}
|
}
|
||||||
|
|
||||||
text: PrintInformation.jobName
|
text: PrintInformation.jobName
|
||||||
@ -200,7 +200,7 @@ Item
|
|||||||
anchors.bottom: parent.bottom
|
anchors.bottom: parent.bottom
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
text: "Save"
|
text: "Save"
|
||||||
enabled: (asProjectCheckbox.checked || asSlicedCheckbox.checked) && dfFilenameTextfield.text != ""
|
enabled: (asProjectCheckbox.checked || asSlicedCheckbox.checked) && dfFilenameTextfield.text.length >= 1
|
||||||
|
|
||||||
onClicked:
|
onClicked:
|
||||||
{
|
{
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
// Copyright (C) 2021 Ultimaker B.V.
|
// Copyright (C) 2021 Ultimaker B.V.
|
||||||
|
// Cura is released under the terms of the LGPLv3 or higher.
|
||||||
|
|
||||||
import QtQuick 2.10
|
import QtQuick 2.10
|
||||||
import QtQuick.Window 2.2
|
import QtQuick.Window 2.2
|
||||||
import QtQuick.Controls 1.4 as OldControls // TableView doesn't exist in the QtQuick Controls 2.x in 5.10, so use the old one
|
import QtQuick.Controls 1.4 as OldControls // TableView doesn't exist in the QtQuick Controls 2.x in 5.10, so use the old one
|
||||||
import QtQuick.Controls 2.3
|
import QtQuick.Controls 2.3
|
||||||
import QtQuick.Controls.Styles 1.4
|
import QtQuick.Controls.Styles 1.4
|
||||||
|
import QtQuick.Layouts 1.1
|
||||||
|
|
||||||
import UM 1.2 as UM
|
import UM 1.2 as UM
|
||||||
import Cura 1.6 as Cura
|
import Cura 1.6 as Cura
|
||||||
@ -18,7 +20,7 @@ Item
|
|||||||
|
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: parent.height
|
height: parent.height
|
||||||
property alias createNewProjectButtonVisible: createNewProjectButton.visible
|
property bool createNewProjectButtonVisible: true
|
||||||
|
|
||||||
anchors
|
anchors
|
||||||
{
|
{
|
||||||
@ -29,25 +31,37 @@ Item
|
|||||||
margins: UM.Theme.getSize("default_margin").width
|
margins: UM.Theme.getSize("default_margin").width
|
||||||
}
|
}
|
||||||
|
|
||||||
Label
|
RowLayout
|
||||||
{
|
{
|
||||||
id: selectProjectLabel
|
id: headerRow
|
||||||
|
|
||||||
text: "Select Project"
|
anchors
|
||||||
font: UM.Theme.getFont("medium")
|
{
|
||||||
color: UM.Theme.getColor("small_button_text")
|
top: parent.top
|
||||||
anchors.top: parent.top
|
left: parent.left
|
||||||
anchors.left: parent.left
|
right: parent.right
|
||||||
visible: projectListContainer.visible
|
}
|
||||||
|
height: childrenRect.height
|
||||||
|
spacing: UM.Theme.getSize("default_margin").width
|
||||||
|
|
||||||
|
Cura.TextField
|
||||||
|
{
|
||||||
|
id: searchBar
|
||||||
|
Layout.fillWidth: true
|
||||||
|
implicitHeight: createNewProjectButton.height
|
||||||
|
|
||||||
|
onTextEdited: manager.projectFilter = text //Update the search filter when editing this text field.
|
||||||
|
|
||||||
|
leftIcon: UM.Theme.getIcon("Magnifier")
|
||||||
|
placeholderText: "Search"
|
||||||
}
|
}
|
||||||
|
|
||||||
Cura.SecondaryButton
|
Cura.SecondaryButton
|
||||||
{
|
{
|
||||||
id: createNewProjectButton
|
id: createNewProjectButton
|
||||||
|
|
||||||
anchors.verticalCenter: selectProjectLabel.verticalCenter
|
|
||||||
anchors.right: parent.right
|
|
||||||
text: "New Library project"
|
text: "New Library project"
|
||||||
|
visible: createNewProjectButtonVisible && manager.userAccountCanCreateNewLibraryProject && (manager.retrievingProjectsStatus == DF.RetrievalStatus.Success || manager.retrievingProjectsStatus == DF.RetrievalStatus.Failed)
|
||||||
|
|
||||||
onClicked:
|
onClicked:
|
||||||
{
|
{
|
||||||
@ -56,6 +70,21 @@ Item
|
|||||||
busy: manager.creatingNewProjectStatus == DF.RetrievalStatus.InProgress
|
busy: manager.creatingNewProjectStatus == DF.RetrievalStatus.InProgress
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Cura.SecondaryButton
|
||||||
|
{
|
||||||
|
id: upgradePlanButton
|
||||||
|
|
||||||
|
text: "Upgrade plan"
|
||||||
|
iconSource: UM.Theme.getIcon("LinkExternal")
|
||||||
|
visible: createNewProjectButtonVisible && !manager.userAccountCanCreateNewLibraryProject && (manager.retrievingProjectsStatus == DF.RetrievalStatus.Success || manager.retrievingProjectsStatus == DF.RetrievalStatus.Failed)
|
||||||
|
tooltip: "You have reached the maximum number of projects allowed by your subscription. Please upgrade to the Professional subscription to create more projects."
|
||||||
|
tooltipWidth: parent.width * 0.5
|
||||||
|
|
||||||
|
onClicked: Qt.openUrlExternally("https://ultimaker.com/software/ultimaker-essentials/sign-up-cura?utm_source=cura&utm_medium=software&utm_campaign=lib-max")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Item
|
Item
|
||||||
{
|
{
|
||||||
id: noLibraryProjectsContainer
|
id: noLibraryProjectsContainer
|
||||||
@ -76,19 +105,18 @@ Item
|
|||||||
{
|
{
|
||||||
id: digitalFactoryImage
|
id: digitalFactoryImage
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
source: "../images/digital_factory.svg"
|
source: searchBar.text === "" ? "../images/digital_factory.svg" : "../images/projects_not_found.svg"
|
||||||
fillMode: Image.PreserveAspectFit
|
fillMode: Image.PreserveAspectFit
|
||||||
width: parent.width - 2 * UM.Theme.getSize("thick_margin").width
|
width: parent.width - 2 * UM.Theme.getSize("thick_margin").width
|
||||||
sourceSize.width: width
|
|
||||||
sourceSize.height: height
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Label
|
Label
|
||||||
{
|
{
|
||||||
id: noLibraryProjectsLabel
|
id: noLibraryProjectsLabel
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
text: "It appears that you don't have any projects in the Library yet."
|
text: searchBar.text === "" ? "It appears that you don't have any projects in the Library yet." : "No projects found that match the search query."
|
||||||
font: UM.Theme.getFont("medium")
|
font: UM.Theme.getFont("medium")
|
||||||
|
color: UM.Theme.getColor("text")
|
||||||
}
|
}
|
||||||
|
|
||||||
Cura.TertiaryButton
|
Cura.TertiaryButton
|
||||||
@ -97,6 +125,7 @@ Item
|
|||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
text: "Visit Digital Library"
|
text: "Visit Digital Library"
|
||||||
onClicked: Qt.openUrlExternally(CuraApplication.ultimakerDigitalFactoryUrl + "/app/library")
|
onClicked: Qt.openUrlExternally(CuraApplication.ultimakerDigitalFactoryUrl + "/app/library")
|
||||||
|
visible: searchBar.text === "" //Show the link to Digital Library when there are no projects in the user's Library.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -106,7 +135,7 @@ Item
|
|||||||
id: projectListContainer
|
id: projectListContainer
|
||||||
anchors
|
anchors
|
||||||
{
|
{
|
||||||
top: selectProjectLabel.bottom
|
top: headerRow.bottom
|
||||||
topMargin: UM.Theme.getSize("default_margin").height
|
topMargin: UM.Theme.getSize("default_margin").height
|
||||||
bottom: parent.bottom
|
bottom: parent.bottom
|
||||||
left: parent.left
|
left: parent.left
|
||||||
|
@ -22,6 +22,7 @@ from .DFFileUploader import DFFileUploader
|
|||||||
from .DFLibraryFileUploadRequest import DFLibraryFileUploadRequest
|
from .DFLibraryFileUploadRequest import DFLibraryFileUploadRequest
|
||||||
from .DFLibraryFileUploadResponse import DFLibraryFileUploadResponse
|
from .DFLibraryFileUploadResponse import DFLibraryFileUploadResponse
|
||||||
from .DFPrintJobUploadRequest import DFPrintJobUploadRequest
|
from .DFPrintJobUploadRequest import DFPrintJobUploadRequest
|
||||||
|
from .DigitalFactoryFeatureBudgetResponse import DigitalFactoryFeatureBudgetResponse
|
||||||
from .DigitalFactoryFileResponse import DigitalFactoryFileResponse
|
from .DigitalFactoryFileResponse import DigitalFactoryFileResponse
|
||||||
from .DigitalFactoryProjectResponse import DigitalFactoryProjectResponse
|
from .DigitalFactoryProjectResponse import DigitalFactoryProjectResponse
|
||||||
from .PaginationLinks import PaginationLinks
|
from .PaginationLinks import PaginationLinks
|
||||||
@ -54,9 +55,67 @@ class DigitalFactoryApiClient:
|
|||||||
self._http = HttpRequestManager.getInstance()
|
self._http = HttpRequestManager.getInstance()
|
||||||
self._on_error = on_error
|
self._on_error = on_error
|
||||||
self._file_uploader = None # type: Optional[DFFileUploader]
|
self._file_uploader = None # type: Optional[DFFileUploader]
|
||||||
|
self._library_max_private_projects: Optional[int] = None
|
||||||
|
|
||||||
self._projects_pagination_mgr = PaginationManager(limit = projects_limit_per_page) if projects_limit_per_page else None # type: Optional[PaginationManager]
|
self._projects_pagination_mgr = PaginationManager(limit = projects_limit_per_page) if projects_limit_per_page else None # type: Optional[PaginationManager]
|
||||||
|
|
||||||
|
def checkUserHasAccess(self, callback: Callable) -> None:
|
||||||
|
"""Checks if the user has any sort of access to the digital library.
|
||||||
|
A user is considered to have access if the max-# of private projects is greater then 0 (or -1 for unlimited).
|
||||||
|
"""
|
||||||
|
|
||||||
|
def callbackWrap(response: Optional[Any] = None, *args, **kwargs) -> None:
|
||||||
|
if (response is not None and isinstance(response, DigitalFactoryFeatureBudgetResponse) and
|
||||||
|
response.library_max_private_projects is not None):
|
||||||
|
callback(
|
||||||
|
response.library_max_private_projects == -1 or # Note: -1 is unlimited
|
||||||
|
response.library_max_private_projects > 0)
|
||||||
|
self._library_max_private_projects = response.library_max_private_projects
|
||||||
|
else:
|
||||||
|
Logger.warning(f"Digital Factory: Response is not a feature budget, likely an error: {str(response)}")
|
||||||
|
callback(False)
|
||||||
|
|
||||||
|
self._http.get(f"{self.CURA_API_ROOT}/feature_budgets",
|
||||||
|
scope = self._scope,
|
||||||
|
callback = self._parseCallback(callbackWrap, DigitalFactoryFeatureBudgetResponse, callbackWrap),
|
||||||
|
error_callback = callbackWrap,
|
||||||
|
timeout = self.DEFAULT_REQUEST_TIMEOUT)
|
||||||
|
|
||||||
|
def checkUserCanCreateNewLibraryProject(self, callback: Callable) -> None:
|
||||||
|
"""
|
||||||
|
Checks if the user is allowed to create new library projects.
|
||||||
|
A user is allowed to create new library projects if the haven't reached their maximum allowed private projects.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def callbackWrap(response: Optional[Any] = None, *args, **kwargs) -> None:
|
||||||
|
if response is not None:
|
||||||
|
if isinstance(response, DigitalFactoryProjectResponse): # The user has only one private project
|
||||||
|
callback(True)
|
||||||
|
elif isinstance(response, list) and all(isinstance(r, DigitalFactoryProjectResponse) for r in response):
|
||||||
|
callback(len(response) < cast(int, self._library_max_private_projects))
|
||||||
|
else:
|
||||||
|
Logger.warning(f"Digital Factory: Incorrect response type received when requesting private projects: {str(response)}")
|
||||||
|
callback(False)
|
||||||
|
else:
|
||||||
|
Logger.warning(f"Digital Factory: Response is empty, likely an error: {str(response)}")
|
||||||
|
callback(False)
|
||||||
|
|
||||||
|
if self._library_max_private_projects is not None and self._library_max_private_projects > 0:
|
||||||
|
# The user has a limit in the number of private projects they can create. Check whether they have already
|
||||||
|
# reached that limit.
|
||||||
|
# Note: Set the pagination manager to None when doing this get request, or else the next/previous links
|
||||||
|
# of the pagination will become corrupted
|
||||||
|
url = f"{self.CURA_API_ROOT}/projects?shared=false&limit={self._library_max_private_projects}"
|
||||||
|
self._http.get(url,
|
||||||
|
scope = self._scope,
|
||||||
|
callback = self._parseCallback(callbackWrap, DigitalFactoryProjectResponse, callbackWrap, pagination_manager = None),
|
||||||
|
error_callback = callbackWrap,
|
||||||
|
timeout = self.DEFAULT_REQUEST_TIMEOUT)
|
||||||
|
else:
|
||||||
|
# If the limit is -1, then the user is allowed unlimited projects. If its 0 then they are not allowed to
|
||||||
|
# create any projects
|
||||||
|
callback(self._library_max_private_projects == -1)
|
||||||
|
|
||||||
def getProject(self, library_project_id: str, on_finished: Callable[[DigitalFactoryProjectResponse], Any], failed: Callable) -> None:
|
def getProject(self, library_project_id: str, on_finished: Callable[[DigitalFactoryProjectResponse], Any], failed: Callable) -> None:
|
||||||
"""
|
"""
|
||||||
Retrieves a digital factory project by its library project id.
|
Retrieves a digital factory project by its library project id.
|
||||||
@ -73,7 +132,7 @@ class DigitalFactoryApiClient:
|
|||||||
error_callback = failed,
|
error_callback = failed,
|
||||||
timeout = self.DEFAULT_REQUEST_TIMEOUT)
|
timeout = self.DEFAULT_REQUEST_TIMEOUT)
|
||||||
|
|
||||||
def getProjectsFirstPage(self, on_finished: Callable[[List[DigitalFactoryProjectResponse]], Any], failed: Callable) -> None:
|
def getProjectsFirstPage(self, search_filter: str, on_finished: Callable[[List[DigitalFactoryProjectResponse]], Any], failed: Callable) -> None:
|
||||||
"""
|
"""
|
||||||
Retrieves digital factory projects for the user that is currently logged in.
|
Retrieves digital factory projects for the user that is currently logged in.
|
||||||
|
|
||||||
@ -81,13 +140,18 @@ class DigitalFactoryApiClient:
|
|||||||
according to the limit set in the pagination manager. If there is no projects pagination manager, this function
|
according to the limit set in the pagination manager. If there is no projects pagination manager, this function
|
||||||
leaves the project limit to the default set on the server side (999999).
|
leaves the project limit to the default set on the server side (999999).
|
||||||
|
|
||||||
|
:param search_filter: Text to filter the search results. If given an empty string, results are not filtered.
|
||||||
:param on_finished: The function to be called after the result is parsed.
|
:param on_finished: The function to be called after the result is parsed.
|
||||||
:param failed: The function to be called if the request fails.
|
:param failed: The function to be called if the request fails.
|
||||||
"""
|
"""
|
||||||
url = "{}/projects".format(self.CURA_API_ROOT)
|
url = f"{self.CURA_API_ROOT}/projects"
|
||||||
|
query_character = "?"
|
||||||
if self._projects_pagination_mgr:
|
if self._projects_pagination_mgr:
|
||||||
self._projects_pagination_mgr.reset() # reset to clear all the links and response metadata
|
self._projects_pagination_mgr.reset() # reset to clear all the links and response metadata
|
||||||
url += "?limit={}".format(self._projects_pagination_mgr.limit)
|
url += f"{query_character}limit={self._projects_pagination_mgr.limit}"
|
||||||
|
query_character = "&"
|
||||||
|
if search_filter != "":
|
||||||
|
url += f"{query_character}search={search_filter}"
|
||||||
|
|
||||||
self._http.get(url,
|
self._http.get(url,
|
||||||
scope = self._scope,
|
scope = self._scope,
|
||||||
@ -301,12 +365,10 @@ class DigitalFactoryApiClient:
|
|||||||
:param on_finished: The function to be called after the result is parsed.
|
:param on_finished: The function to be called after the result is parsed.
|
||||||
:param on_error: The function to be called if anything goes wrong.
|
:param on_error: The function to be called if anything goes wrong.
|
||||||
"""
|
"""
|
||||||
|
Logger.log("i", "Attempt to create new DF project '{}'.".format(project_name))
|
||||||
display_name = re.sub(r"[^a-zA-Z0-9- ./™®ö+']", " ", project_name)
|
|
||||||
Logger.log("i", "Attempt to create new DF project '{}'.".format(display_name))
|
|
||||||
|
|
||||||
url = "{}/projects".format(self.CURA_API_ROOT)
|
url = "{}/projects".format(self.CURA_API_ROOT)
|
||||||
data = json.dumps({"data": {"display_name": display_name}}).encode()
|
data = json.dumps({"data": {"display_name": project_name}}).encode()
|
||||||
self._http.put(url,
|
self._http.put(url,
|
||||||
scope = self._scope,
|
scope = self._scope,
|
||||||
data = data,
|
data = data,
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
# Copyright (c) 2021 Ultimaker B.V.
|
# Copyright (c) 2021 Ultimaker B.V.
|
||||||
|
# Cura is released under the terms of the LGPLv3 or higher.
|
||||||
|
|
||||||
import json
|
import json
|
||||||
import math
|
import math
|
||||||
import os
|
import os
|
||||||
@ -8,7 +10,7 @@ from enum import IntEnum
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Optional, List, Dict, Any, cast
|
from typing import Optional, List, Dict, Any, cast
|
||||||
|
|
||||||
from PyQt5.QtCore import pyqtSignal, QObject, pyqtSlot, pyqtProperty, Q_ENUMS, QUrl
|
from PyQt5.QtCore import pyqtSignal, QObject, pyqtSlot, pyqtProperty, Q_ENUMS, QTimer, QUrl
|
||||||
from PyQt5.QtNetwork import QNetworkReply
|
from PyQt5.QtNetwork import QNetworkReply
|
||||||
from PyQt5.QtQml import qmlRegisterType, qmlRegisterUncreatableType
|
from PyQt5.QtQml import qmlRegisterType, qmlRegisterUncreatableType
|
||||||
|
|
||||||
@ -89,6 +91,12 @@ class DigitalFactoryController(QObject):
|
|||||||
uploadFileError = Signal()
|
uploadFileError = Signal()
|
||||||
uploadFileFinished = Signal()
|
uploadFileFinished = Signal()
|
||||||
|
|
||||||
|
"""Signal to inform about the state of user access."""
|
||||||
|
userAccessStateChanged = pyqtSignal(bool)
|
||||||
|
|
||||||
|
"""Signal to inform whether the user is allowed to create more Library projects."""
|
||||||
|
userCanCreateNewLibraryProjectChanged = pyqtSignal(bool)
|
||||||
|
|
||||||
def __init__(self, application: CuraApplication) -> None:
|
def __init__(self, application: CuraApplication) -> None:
|
||||||
super().__init__(parent = None)
|
super().__init__(parent = None)
|
||||||
|
|
||||||
@ -106,12 +114,18 @@ class DigitalFactoryController(QObject):
|
|||||||
self._has_more_projects_to_load = False
|
self._has_more_projects_to_load = False
|
||||||
|
|
||||||
self._account = self._application.getInstance().getCuraAPI().account # type: Account
|
self._account = self._application.getInstance().getCuraAPI().account # type: Account
|
||||||
|
self._account.loginStateChanged.connect(self._onLoginStateChanged)
|
||||||
self._current_workspace_information = CuraApplication.getInstance().getCurrentWorkspaceInformation()
|
self._current_workspace_information = CuraApplication.getInstance().getCurrentWorkspaceInformation()
|
||||||
|
|
||||||
# Initialize the project model
|
# Initialize the project model
|
||||||
self._project_model = DigitalFactoryProjectModel()
|
self._project_model = DigitalFactoryProjectModel()
|
||||||
self._selected_project_idx = -1
|
self._selected_project_idx = -1
|
||||||
self._project_creation_error_text = "Something went wrong while creating a new project. Please try again."
|
self._project_creation_error_text = "Something went wrong while creating a new project. Please try again."
|
||||||
|
self._project_filter = ""
|
||||||
|
self._project_filter_change_timer = QTimer()
|
||||||
|
self._project_filter_change_timer.setInterval(200)
|
||||||
|
self._project_filter_change_timer.setSingleShot(True)
|
||||||
|
self._project_filter_change_timer.timeout.connect(self._applyProjectFilter)
|
||||||
|
|
||||||
# Initialize the file model
|
# Initialize the file model
|
||||||
self._file_model = DigitalFactoryFileModel()
|
self._file_model = DigitalFactoryFileModel()
|
||||||
@ -131,6 +145,9 @@ class DigitalFactoryController(QObject):
|
|||||||
self._application.engineCreatedSignal.connect(self._onEngineCreated)
|
self._application.engineCreatedSignal.connect(self._onEngineCreated)
|
||||||
self._application.initializationFinished.connect(self._applicationInitializationFinished)
|
self._application.initializationFinished.connect(self._applicationInitializationFinished)
|
||||||
|
|
||||||
|
self._user_has_access = False
|
||||||
|
self._user_account_can_create_new_project = False
|
||||||
|
|
||||||
def clear(self) -> None:
|
def clear(self) -> None:
|
||||||
self._project_model.clearProjects()
|
self._project_model.clearProjects()
|
||||||
self._api.clear()
|
self._api.clear()
|
||||||
@ -143,16 +160,22 @@ class DigitalFactoryController(QObject):
|
|||||||
|
|
||||||
self.setSelectedProjectIndex(-1)
|
self.setSelectedProjectIndex(-1)
|
||||||
|
|
||||||
|
def _onLoginStateChanged(self, logged_in: bool) -> None:
|
||||||
|
def callback(has_access, **kwargs):
|
||||||
|
self._user_has_access = has_access
|
||||||
|
self.userAccessStateChanged.emit(logged_in)
|
||||||
|
|
||||||
|
self._api.checkUserHasAccess(callback)
|
||||||
|
|
||||||
def userAccountHasLibraryAccess(self) -> bool:
|
def userAccountHasLibraryAccess(self) -> bool:
|
||||||
"""
|
"""
|
||||||
Checks whether the currently logged in user account has access to the Digital Library
|
Checks whether the currently logged in user account has access to the Digital Library
|
||||||
|
|
||||||
:return: True if the user account has Digital Library access, else False
|
:return: True if the user account has Digital Library access, else False
|
||||||
"""
|
"""
|
||||||
subscriptions = [] # type: List[Dict[str, Any]]
|
if self._user_has_access:
|
||||||
if self._account.userProfile:
|
self._api.checkUserCanCreateNewLibraryProject(callback = self.setCanCreateNewLibraryProject)
|
||||||
subscriptions = self._account.userProfile.get("subscriptions", [])
|
return self._user_has_access
|
||||||
return len(subscriptions) > 0
|
|
||||||
|
|
||||||
def initialize(self, preselected_project_id: Optional[str] = None) -> None:
|
def initialize(self, preselected_project_id: Optional[str] = None) -> None:
|
||||||
self.clear()
|
self.clear()
|
||||||
@ -162,7 +185,7 @@ class DigitalFactoryController(QObject):
|
|||||||
if preselected_project_id:
|
if preselected_project_id:
|
||||||
self._api.getProject(preselected_project_id, on_finished = self.setProjectAsPreselected, failed = self._onGetProjectFailed)
|
self._api.getProject(preselected_project_id, on_finished = self.setProjectAsPreselected, failed = self._onGetProjectFailed)
|
||||||
else:
|
else:
|
||||||
self._api.getProjectsFirstPage(on_finished = self._onGetProjectsFirstPageFinished, failed = self._onGetProjectsFailed)
|
self._api.getProjectsFirstPage(search_filter = self._project_filter, on_finished = self._onGetProjectsFirstPageFinished, failed = self._onGetProjectsFailed)
|
||||||
|
|
||||||
def setProjectAsPreselected(self, df_project: DigitalFactoryProjectResponse) -> None:
|
def setProjectAsPreselected(self, df_project: DigitalFactoryProjectResponse) -> None:
|
||||||
"""
|
"""
|
||||||
@ -288,6 +311,38 @@ class DigitalFactoryController(QObject):
|
|||||||
self._selected_file_indices = file_indices
|
self._selected_file_indices = file_indices
|
||||||
self.selectedFileIndicesChanged.emit(file_indices)
|
self.selectedFileIndicesChanged.emit(file_indices)
|
||||||
|
|
||||||
|
def setProjectFilter(self, new_filter: str) -> None:
|
||||||
|
"""
|
||||||
|
Called when the user wants to change the search filter for projects.
|
||||||
|
|
||||||
|
The filter is not immediately applied. There is some delay to allow the user to finish typing.
|
||||||
|
:param new_filter: The new filter that the user wants to apply.
|
||||||
|
"""
|
||||||
|
self._project_filter = new_filter
|
||||||
|
self._project_filter_change_timer.start()
|
||||||
|
|
||||||
|
"""
|
||||||
|
Signal to notify Qt that the applied filter has changed.
|
||||||
|
"""
|
||||||
|
projectFilterChanged = pyqtSignal()
|
||||||
|
|
||||||
|
@pyqtProperty(str, notify = projectFilterChanged, fset = setProjectFilter)
|
||||||
|
def projectFilter(self) -> str:
|
||||||
|
"""
|
||||||
|
The current search filter being applied to the project list.
|
||||||
|
:return: The current search filter being applied to the project list.
|
||||||
|
"""
|
||||||
|
return self._project_filter
|
||||||
|
|
||||||
|
def _applyProjectFilter(self) -> None:
|
||||||
|
"""
|
||||||
|
Actually apply the current filter to search for projects with the user-defined search string.
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
self.clear()
|
||||||
|
self.projectFilterChanged.emit()
|
||||||
|
self._api.getProjectsFirstPage(search_filter = self._project_filter, on_finished = self._onGetProjectsFirstPageFinished, failed = self._onGetProjectsFailed)
|
||||||
|
|
||||||
@pyqtProperty(QObject, constant = True)
|
@pyqtProperty(QObject, constant = True)
|
||||||
def digitalFactoryProjectModel(self) -> "DigitalFactoryProjectModel":
|
def digitalFactoryProjectModel(self) -> "DigitalFactoryProjectModel":
|
||||||
return self._project_model
|
return self._project_model
|
||||||
@ -502,7 +557,8 @@ class DigitalFactoryController(QObject):
|
|||||||
# false, we also need to clean it from the projects model
|
# false, we also need to clean it from the projects model
|
||||||
self._project_model.clearProjects()
|
self._project_model.clearProjects()
|
||||||
self.setSelectedProjectIndex(-1)
|
self.setSelectedProjectIndex(-1)
|
||||||
self._api.getProjectsFirstPage(on_finished = self._onGetProjectsFirstPageFinished, failed = self._onGetProjectsFailed)
|
self._api.getProjectsFirstPage(search_filter = self._project_filter, on_finished = self._onGetProjectsFirstPageFinished, failed = self._onGetProjectsFailed)
|
||||||
|
self._api.checkUserCanCreateNewLibraryProject(callback = self.setCanCreateNewLibraryProject)
|
||||||
self.setRetrievingProjectsStatus(RetrievalStatus.InProgress)
|
self.setRetrievingProjectsStatus(RetrievalStatus.InProgress)
|
||||||
self._has_preselected_project = new_has_preselected_project
|
self._has_preselected_project = new_has_preselected_project
|
||||||
self.preselectedProjectChanged.emit()
|
self.preselectedProjectChanged.emit()
|
||||||
@ -511,6 +567,14 @@ class DigitalFactoryController(QObject):
|
|||||||
def hasPreselectedProject(self) -> bool:
|
def hasPreselectedProject(self) -> bool:
|
||||||
return self._has_preselected_project
|
return self._has_preselected_project
|
||||||
|
|
||||||
|
def setCanCreateNewLibraryProject(self, can_create_new_library_project: bool) -> None:
|
||||||
|
self._user_account_can_create_new_project = can_create_new_library_project
|
||||||
|
self.userCanCreateNewLibraryProjectChanged.emit(self._user_account_can_create_new_project)
|
||||||
|
|
||||||
|
@pyqtProperty(bool, fset = setCanCreateNewLibraryProject, notify = userCanCreateNewLibraryProjectChanged)
|
||||||
|
def userAccountCanCreateNewLibraryProject(self) -> bool:
|
||||||
|
return self._user_account_can_create_new_project
|
||||||
|
|
||||||
@pyqtSlot(str, "QStringList")
|
@pyqtSlot(str, "QStringList")
|
||||||
def saveFileToSelectedProject(self, filename: str, formats: List[str]) -> None:
|
def saveFileToSelectedProject(self, filename: str, formats: List[str]) -> None:
|
||||||
"""
|
"""
|
||||||
|
@ -0,0 +1,43 @@
|
|||||||
|
# Copyright (c) 2021 Ultimaker B.V.
|
||||||
|
# Cura is released under the terms of the LGPLv3 or higher.
|
||||||
|
|
||||||
|
from .BaseModel import BaseModel
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
|
||||||
|
class DigitalFactoryFeatureBudgetResponse(BaseModel):
|
||||||
|
"""Class representing the capabilities of a user account for Digital Library.
|
||||||
|
NOTE: For each max_..._projects fields, '-1' means unlimited!
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self,
|
||||||
|
library_can_use_business_value: Optional[bool] = False,
|
||||||
|
library_can_use_comments: Optional[bool] = False,
|
||||||
|
library_can_use_status: Optional[bool] = False,
|
||||||
|
library_can_use_tags: Optional[bool] = False,
|
||||||
|
library_can_use_technical_requirements: Optional[bool] = False,
|
||||||
|
library_max_organization_shared_projects: Optional[int] = None, # -1 means unlimited
|
||||||
|
library_max_private_projects: Optional[int] = None, # -1 means unlimited
|
||||||
|
library_max_team_shared_projects: Optional[int] = None, # -1 means unlimited
|
||||||
|
**kwargs) -> None:
|
||||||
|
|
||||||
|
self.library_can_use_business_value = library_can_use_business_value
|
||||||
|
self.library_can_use_comments = library_can_use_comments
|
||||||
|
self.library_can_use_status = library_can_use_status
|
||||||
|
self.library_can_use_tags = library_can_use_tags
|
||||||
|
self.library_can_use_technical_requirements = library_can_use_technical_requirements
|
||||||
|
self.library_max_organization_shared_projects = library_max_organization_shared_projects # -1 means unlimited
|
||||||
|
self.library_max_private_projects = library_max_private_projects # -1 means unlimited
|
||||||
|
self.library_max_team_shared_projects = library_max_team_shared_projects # -1 means unlimited
|
||||||
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return "max private: {}, max org: {}, max team: {}".format(
|
||||||
|
self.library_max_private_projects,
|
||||||
|
self.library_max_organization_shared_projects,
|
||||||
|
self.library_max_team_shared_projects)
|
||||||
|
|
||||||
|
# Validates the model, raising an exception if the model is invalid.
|
||||||
|
def validate(self) -> None:
|
||||||
|
super().validate()
|
||||||
|
# No validation for now, as the response can be "data: []", which should be interpreted as all False and 0's
|
@ -22,7 +22,7 @@ class DigitalFactoryFileProvider(FileProvider):
|
|||||||
self._dialog = None
|
self._dialog = None
|
||||||
|
|
||||||
self._account = CuraApplication.getInstance().getCuraAPI().account # type: Account
|
self._account = CuraApplication.getInstance().getCuraAPI().account # type: Account
|
||||||
self._account.loginStateChanged.connect(self._onLoginStateChanged)
|
self._controller.userAccessStateChanged.connect(self._onUserAccessStateChanged)
|
||||||
self.enabled = self._account.isLoggedIn and self._controller.userAccountHasLibraryAccess()
|
self.enabled = self._account.isLoggedIn and self._controller.userAccountHasLibraryAccess()
|
||||||
self.priority = 10
|
self.priority = 10
|
||||||
|
|
||||||
@ -53,7 +53,7 @@ class DigitalFactoryFileProvider(FileProvider):
|
|||||||
if not self._dialog:
|
if not self._dialog:
|
||||||
Logger.log("e", "Unable to create the Digital Library Open dialog.")
|
Logger.log("e", "Unable to create the Digital Library Open dialog.")
|
||||||
|
|
||||||
def _onLoginStateChanged(self, logged_in: bool) -> None:
|
def _onUserAccessStateChanged(self, logged_in: bool) -> None:
|
||||||
"""
|
"""
|
||||||
Sets the enabled status of the DigitalFactoryFileProvider according to the account's login status
|
Sets the enabled status of the DigitalFactoryFileProvider according to the account's login status
|
||||||
:param logged_in: The new login status
|
:param logged_in: The new login status
|
||||||
|
@ -45,7 +45,7 @@ class DigitalFactoryOutputDevice(ProjectOutputDevice):
|
|||||||
self._writing = False
|
self._writing = False
|
||||||
|
|
||||||
self._account = CuraApplication.getInstance().getCuraAPI().account # type: Account
|
self._account = CuraApplication.getInstance().getCuraAPI().account # type: Account
|
||||||
self._account.loginStateChanged.connect(self._onLoginStateChanged)
|
self._controller.userAccessStateChanged.connect(self._onUserAccessStateChanged)
|
||||||
self.enabled = self._account.isLoggedIn and self._controller.userAccountHasLibraryAccess()
|
self.enabled = self._account.isLoggedIn and self._controller.userAccountHasLibraryAccess()
|
||||||
|
|
||||||
self._current_workspace_information = CuraApplication.getInstance().getCurrentWorkspaceInformation()
|
self._current_workspace_information = CuraApplication.getInstance().getCurrentWorkspaceInformation()
|
||||||
@ -97,7 +97,7 @@ class DigitalFactoryOutputDevice(ProjectOutputDevice):
|
|||||||
if not self._dialog:
|
if not self._dialog:
|
||||||
Logger.log("e", "Unable to create the Digital Library Save dialog.")
|
Logger.log("e", "Unable to create the Digital Library Save dialog.")
|
||||||
|
|
||||||
def _onLoginStateChanged(self, logged_in: bool) -> None:
|
def _onUserAccessStateChanged(self, logged_in: bool) -> None:
|
||||||
"""
|
"""
|
||||||
Sets the enabled status of the DigitalFactoryOutputDevice according to the account's login status
|
Sets the enabled status of the DigitalFactoryOutputDevice according to the account's login status
|
||||||
:param logged_in: The new login status
|
:param logged_in: The new login status
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
# Copyright (c) 2021 Ultimaker B.V.
|
||||||
|
# Cura is released under the terms of the LGPLv3 or higher.
|
||||||
|
|
||||||
from unittest.mock import MagicMock
|
from unittest.mock import MagicMock
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
@ -37,7 +40,7 @@ def test_getProjectsFirstPage(api_client):
|
|||||||
failed_callback = MagicMock()
|
failed_callback = MagicMock()
|
||||||
|
|
||||||
# Call
|
# Call
|
||||||
api_client.getProjectsFirstPage(on_finished = finished_callback, failed = failed_callback)
|
api_client.getProjectsFirstPage(search_filter = "filter", on_finished = finished_callback, failed = failed_callback)
|
||||||
|
|
||||||
# Asserts
|
# Asserts
|
||||||
pagination_manager.reset.assert_called_once() # Should be called since we asked for new set of projects
|
pagination_manager.reset.assert_called_once() # Should be called since we asked for new set of projects
|
||||||
@ -45,16 +48,16 @@ def test_getProjectsFirstPage(api_client):
|
|||||||
args = http_manager.get.call_args_list[0]
|
args = http_manager.get.call_args_list[0]
|
||||||
|
|
||||||
# Ensure that it's called with the right limit
|
# Ensure that it's called with the right limit
|
||||||
assert args[0][0] == "https://api.ultimaker.com/cura/v1/projects?limit=20"
|
assert args[0][0] == "https://api.ultimaker.com/cura/v1/projects?limit=20&search=filter"
|
||||||
|
|
||||||
# Change the limit & try again
|
# Change the limit & try again
|
||||||
http_manager.get.reset_mock()
|
http_manager.get.reset_mock()
|
||||||
pagination_manager.limit = 80
|
pagination_manager.limit = 80
|
||||||
api_client.getProjectsFirstPage(on_finished = finished_callback, failed = failed_callback)
|
api_client.getProjectsFirstPage(search_filter = "filter", on_finished = finished_callback, failed = failed_callback)
|
||||||
args = http_manager.get.call_args_list[0]
|
args = http_manager.get.call_args_list[0]
|
||||||
|
|
||||||
# Ensure that it's called with the right limit
|
# Ensure that it's called with the right limit
|
||||||
assert args[0][0] == "https://api.ultimaker.com/cura/v1/projects?limit=80"
|
assert args[0][0] == "https://api.ultimaker.com/cura/v1/projects?limit=80&search=filter"
|
||||||
|
|
||||||
|
|
||||||
def test_getMoreProjects_noNewProjects(api_client):
|
def test_getMoreProjects_noNewProjects(api_client):
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# Copyright (c) 2020 Ultimaker B.V.
|
# Copyright (c) 2021 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.Logger import Logger
|
from UM.Logger import Logger
|
||||||
@ -103,20 +103,27 @@ class PerObjectSettingsTool(Tool):
|
|||||||
new_instance.resetState() # Ensure that the state is not seen as a user state.
|
new_instance.resetState() # Ensure that the state is not seen as a user state.
|
||||||
settings.addInstance(new_instance)
|
settings.addInstance(new_instance)
|
||||||
|
|
||||||
for property_key in ["top_bottom_thickness", "wall_thickness", "wall_line_count"]:
|
# Override some settings to ensure that the infill mesh by default adds no skin or walls. Or remove them if not an infill mesh.
|
||||||
|
specialized_settings = {
|
||||||
|
"top_bottom_thickness": 0,
|
||||||
|
"top_thickness": "=top_bottom_thickness",
|
||||||
|
"bottom_thickness": "=top_bottom_thickness",
|
||||||
|
"top_layers": "=0 if infill_sparse_density == 100 else math.ceil(round(top_thickness / resolveOrValue('layer_height'), 4))",
|
||||||
|
"bottom_layers": "=0 if infill_sparse_density == 100 else math.ceil(round(bottom_thickness / resolveOrValue('layer_height'), 4))",
|
||||||
|
"wall_thickness": 0,
|
||||||
|
"wall_line_count": "=max(1, round((wall_thickness - wall_line_width_0) / wall_line_width_x) + 1) if wall_thickness != 0 else 0"
|
||||||
|
}
|
||||||
|
for property_key in specialized_settings:
|
||||||
if mesh_type == "infill_mesh":
|
if mesh_type == "infill_mesh":
|
||||||
if settings.getInstance(property_key) is None:
|
if settings.getInstance(property_key) is None:
|
||||||
definition = stack.getSettingDefinition(property_key)
|
definition = stack.getSettingDefinition(property_key)
|
||||||
new_instance = SettingInstance(definition, settings)
|
new_instance = SettingInstance(definition, settings)
|
||||||
# We just want the wall_line count to be there in case it was overriden in the global stack.
|
new_instance.setProperty("value", specialized_settings[property_key])
|
||||||
# as such, we don't need to set a value.
|
|
||||||
if property_key != "wall_line_count":
|
|
||||||
new_instance.setProperty("value", 0)
|
|
||||||
new_instance.resetState() # Ensure that the state is not seen as a user state.
|
new_instance.resetState() # Ensure that the state is not seen as a user state.
|
||||||
settings.addInstance(new_instance)
|
settings.addInstance(new_instance)
|
||||||
settings_visibility_changed = True
|
settings_visibility_changed = True
|
||||||
|
|
||||||
elif old_mesh_type == "infill_mesh" and settings.getInstance(property_key) and (settings.getProperty(property_key, "value") == 0 or property_key == "wall_line_count"):
|
elif old_mesh_type == "infill_mesh" and settings.getInstance(property_key) and property_key in specialized_settings:
|
||||||
settings.removeInstance(property_key)
|
settings.removeInstance(property_key)
|
||||||
settings_visibility_changed = True
|
settings_visibility_changed = True
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
// Copyright (c) 2018 Ultimaker B.V.
|
// Copyright (c) 2021 Ultimaker B.V.
|
||||||
// Cura is released under the terms of the LGPLv3 or higher.
|
// Cura is released under the terms of the LGPLv3 or higher.
|
||||||
|
|
||||||
import QtQuick 2.7
|
import QtQuick 2.9
|
||||||
import QtQuick.Layouts 1.1
|
import QtQuick.Layouts 1.1
|
||||||
import QtQuick.Controls 2.3
|
import QtQuick.Controls 2.3
|
||||||
|
|
||||||
@ -13,6 +13,8 @@ Item
|
|||||||
{
|
{
|
||||||
id: prepareMenu
|
id: prepareMenu
|
||||||
|
|
||||||
|
property var fileProviderModel: CuraApplication.getFileProviderModel()
|
||||||
|
|
||||||
UM.I18nCatalog
|
UM.I18nCatalog
|
||||||
{
|
{
|
||||||
id: catalog
|
id: catalog
|
||||||
@ -36,9 +38,9 @@ Item
|
|||||||
{
|
{
|
||||||
id: itemRow
|
id: itemRow
|
||||||
|
|
||||||
anchors.left: openFileButton.right
|
anchors.left: parent.left
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
anchors.leftMargin: UM.Theme.getSize("default_margin").width
|
anchors.leftMargin: UM.Theme.getSize("default_margin").width + openFileButton.width + openFileMenu.width
|
||||||
property int machineSelectorWidth: Math.round((width - printSetupSelectorItem.width) / 3)
|
property int machineSelectorWidth: Math.round((width - printSetupSelectorItem.width) / 3)
|
||||||
|
|
||||||
height: parent.height
|
height: parent.height
|
||||||
@ -74,22 +76,116 @@ Item
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Pop-up shown when there are multiple items to select from.
|
||||||
|
Cura.ExpandablePopup
|
||||||
|
{
|
||||||
|
id: openFileMenu
|
||||||
|
visible: prepareMenu.fileProviderModel.count > 1
|
||||||
|
|
||||||
|
contentAlignment: Cura.ExpandablePopup.ContentAlignment.AlignLeft
|
||||||
|
headerCornerSide: Cura.RoundedRectangle.Direction.All
|
||||||
|
headerPadding: Math.round((parent.height - UM.Theme.getSize("button_icon").height) / 2)
|
||||||
|
contentPadding: UM.Theme.getSize("default_lining").width
|
||||||
|
enabled: visible
|
||||||
|
|
||||||
|
height: parent.height
|
||||||
|
width: visible ? (headerPadding * 3 + UM.Theme.getSize("button_icon").height + iconSize) : 0
|
||||||
|
|
||||||
|
headerItem: UM.RecolorImage
|
||||||
|
{
|
||||||
|
id: menuIcon
|
||||||
|
source: UM.Theme.getIcon("Folder", "medium")
|
||||||
|
color: UM.Theme.getColor("icon")
|
||||||
|
|
||||||
|
sourceSize.height: height
|
||||||
|
}
|
||||||
|
|
||||||
|
contentItem: Item
|
||||||
|
{
|
||||||
|
id: popup
|
||||||
|
|
||||||
|
Column
|
||||||
|
{
|
||||||
|
id: openProviderColumn
|
||||||
|
|
||||||
|
//The column doesn't automatically listen to its children rect if the children change internally, so we need to explicitly update the size.
|
||||||
|
onChildrenRectChanged:
|
||||||
|
{
|
||||||
|
popup.height = childrenRect.height
|
||||||
|
popup.width = childrenRect.width
|
||||||
|
}
|
||||||
|
onPositioningComplete:
|
||||||
|
{
|
||||||
|
popup.height = childrenRect.height
|
||||||
|
popup.width = childrenRect.width
|
||||||
|
}
|
||||||
|
|
||||||
|
Repeater
|
||||||
|
{
|
||||||
|
model: prepareMenu.fileProviderModel
|
||||||
|
delegate: Button
|
||||||
|
{
|
||||||
|
leftPadding: UM.Theme.getSize("default_margin").width
|
||||||
|
rightPadding: UM.Theme.getSize("default_margin").width
|
||||||
|
width: contentItem.width + leftPadding + rightPadding
|
||||||
|
height: UM.Theme.getSize("action_button").height
|
||||||
|
hoverEnabled: true
|
||||||
|
|
||||||
|
contentItem: Label
|
||||||
|
{
|
||||||
|
text: model.displayText
|
||||||
|
color: UM.Theme.getColor("text")
|
||||||
|
font: UM.Theme.getFont("medium")
|
||||||
|
renderType: Text.NativeRendering
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
|
||||||
|
width: contentWidth
|
||||||
|
height: parent.height
|
||||||
|
}
|
||||||
|
|
||||||
|
onClicked:
|
||||||
|
{
|
||||||
|
if(model.index == 0) //The 0th element is the "From Disk" option, which should activate the open local file dialog.
|
||||||
|
{
|
||||||
|
Cura.Actions.open.trigger();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
prepareMenu.fileProviderModel.trigger(model.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
background: Rectangle
|
||||||
|
{
|
||||||
|
color: parent.hovered ? UM.Theme.getColor("action_button_hovered") : "transparent"
|
||||||
|
radius: UM.Theme.getSize("action_button_radius").width
|
||||||
|
width: popup.width
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//If there is just a single item, show a button instead that directly chooses the one option.
|
||||||
Button
|
Button
|
||||||
{
|
{
|
||||||
id: openFileButton
|
id: openFileButton
|
||||||
height: UM.Theme.getSize("stage_menu").height
|
visible: prepareMenu.fileProviderModel.count <= 1
|
||||||
width: UM.Theme.getSize("stage_menu").height
|
|
||||||
|
height: parent.height
|
||||||
|
width: visible ? height : 0 //Square button (and don't take up space if invisible).
|
||||||
onClicked: Cura.Actions.open.trigger()
|
onClicked: Cura.Actions.open.trigger()
|
||||||
|
enabled: visible && prepareMenu.fileProviderModel.count > 0
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
|
|
||||||
contentItem: Item
|
contentItem: Item
|
||||||
{
|
{
|
||||||
anchors.fill: parent
|
|
||||||
UM.RecolorImage
|
UM.RecolorImage
|
||||||
{
|
{
|
||||||
id: buttonIcon
|
id: buttonIcon
|
||||||
anchors.centerIn: parent
|
|
||||||
source: UM.Theme.getIcon("Folder", "medium")
|
source: UM.Theme.getIcon("Folder", "medium")
|
||||||
|
anchors.centerIn: parent
|
||||||
width: UM.Theme.getSize("button_icon").width
|
width: UM.Theme.getSize("button_icon").width
|
||||||
height: UM.Theme.getSize("button_icon").height
|
height: UM.Theme.getSize("button_icon").height
|
||||||
color: UM.Theme.getColor("icon")
|
color: UM.Theme.getColor("icon")
|
||||||
@ -101,8 +197,8 @@ Item
|
|||||||
background: Rectangle
|
background: Rectangle
|
||||||
{
|
{
|
||||||
id: background
|
id: background
|
||||||
height: UM.Theme.getSize("stage_menu").height
|
height: parent.height
|
||||||
width: UM.Theme.getSize("stage_menu").height
|
width: parent.width
|
||||||
border.color: UM.Theme.getColor("lining")
|
border.color: UM.Theme.getColor("lining")
|
||||||
border.width: UM.Theme.getSize("default_lining").width
|
border.width: UM.Theme.getSize("default_lining").width
|
||||||
|
|
||||||
|
@ -203,16 +203,16 @@ Cura.ExpandableComponent
|
|||||||
|
|
||||||
style: UM.Theme.styles.checkbox
|
style: UM.Theme.styles.checkbox
|
||||||
|
|
||||||
|
Rectangle
|
||||||
UM.RecolorImage
|
|
||||||
{
|
{
|
||||||
id: swatch
|
id: swatch
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
anchors.right: extrudersModelCheckBox.right
|
anchors.right: extrudersModelCheckBox.right
|
||||||
width: UM.Theme.getSize("layerview_legend_size").width
|
width: UM.Theme.getSize("layerview_legend_size").width
|
||||||
height: UM.Theme.getSize("layerview_legend_size").height
|
height: UM.Theme.getSize("layerview_legend_size").height
|
||||||
source: UM.Theme.getIcon("Extruder", "medium")
|
|
||||||
color: model.color
|
color: model.color
|
||||||
|
border.width: UM.Theme.getSize("default_lining").width
|
||||||
|
border.color: UM.Theme.getColor("lining")
|
||||||
}
|
}
|
||||||
|
|
||||||
Label
|
Label
|
||||||
|
@ -256,7 +256,16 @@ class CloudOutputDevice(UltimakerNetworkedPrinterOutputDevice):
|
|||||||
"""
|
"""
|
||||||
self._uploaded_print_job = self._pre_upload_print_job
|
self._uploaded_print_job = self._pre_upload_print_job
|
||||||
self._progress.hide()
|
self._progress.hide()
|
||||||
PrintJobUploadSuccessMessage().show()
|
message = PrintJobUploadSuccessMessage()
|
||||||
|
message.addAction("monitor print",
|
||||||
|
name=I18N_CATALOG.i18nc("@action:button", "Monitor print"),
|
||||||
|
icon="",
|
||||||
|
description=I18N_CATALOG.i18nc("@action:tooltip", "Track the print in Ultimaker Digital Factory"),
|
||||||
|
button_align=message.ActionButtonAlignment.ALIGN_RIGHT)
|
||||||
|
df_url = f"https://digitalfactory.ultimaker.com/app/jobs/{self._cluster.cluster_id}?utm_source=cura&utm_medium=software&utm_campaign=monitor-button"
|
||||||
|
message.pyQtActionTriggered.connect(lambda message, action: (QDesktopServices.openUrl(QUrl(df_url)), message.hide()))
|
||||||
|
|
||||||
|
message.show()
|
||||||
self.writeFinished.emit()
|
self.writeFinished.emit()
|
||||||
|
|
||||||
def _onPrintUploadSpecificError(self, reply: "QNetworkReply", _: "QNetworkReply.NetworkError"):
|
def _onPrintUploadSpecificError(self, reply: "QNetworkReply", _: "QNetworkReply.NetworkError"):
|
||||||
|
@ -13,6 +13,5 @@ class PrintJobUploadSuccessMessage(Message):
|
|||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
super().__init__(
|
super().__init__(
|
||||||
text = I18N_CATALOG.i18nc("@info:status", "Print job was successfully sent to the printer."),
|
text = I18N_CATALOG.i18nc("@info:status", "Print job was successfully sent to the printer."),
|
||||||
title = I18N_CATALOG.i18nc("@info:title", "Data Sent"),
|
title = I18N_CATALOG.i18nc("@info:title", "Data Sent")
|
||||||
lifetime = 5
|
|
||||||
)
|
)
|
||||||
|
@ -115,6 +115,7 @@
|
|||||||
"skirt_gap": { "value": 5.0 },
|
"skirt_gap": { "value": 5.0 },
|
||||||
"skirt_line_count": { "value": 4 },
|
"skirt_line_count": { "value": 4 },
|
||||||
|
|
||||||
|
"meshfix_maximum_deviation": { "value": 0.05 },
|
||||||
|
|
||||||
"support_angle": { "value": "math.floor(math.degrees(math.atan(line_width / 2.0 / layer_height)))" },
|
"support_angle": { "value": "math.floor(math.degrees(math.atan(line_width / 2.0 / layer_height)))" },
|
||||||
"support_pattern": { "value": "'zigzag'" },
|
"support_pattern": { "value": "'zigzag'" },
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
"metadata": {
|
"metadata": {
|
||||||
"visible": true,
|
"visible": true,
|
||||||
"platform": "kossel_pro_build_platform.3mf",
|
"platform": "kossel_pro_build_platform.3mf",
|
||||||
|
"platform_offset": [0, -0.25, 0],
|
||||||
"machine_extruder_trains": {
|
"machine_extruder_trains": {
|
||||||
"0": "anycubic_kossel_extruder_0"
|
"0": "anycubic_kossel_extruder_0"
|
||||||
}
|
}
|
||||||
|
35
resources/definitions/atom2.def.json
Normal file
35
resources/definitions/atom2.def.json
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
{
|
||||||
|
"name": "Atom 2",
|
||||||
|
"version": 2,
|
||||||
|
"inherits": "fdmprinter",
|
||||||
|
"metadata": {
|
||||||
|
"visible": true,
|
||||||
|
"author": "Victor (Yu Chieh) Lin",
|
||||||
|
"manufacturer": "Layer One",
|
||||||
|
"file_formats": "text/x-gcode",
|
||||||
|
"platform_offset": [0,0,0],
|
||||||
|
"machine_extruder_trains": { "0": "atom2_extruder_0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"overrides": {
|
||||||
|
"machine_name": { "default_value": "Atom 2" },
|
||||||
|
"machine_shape": { "default_value": "elliptic" },
|
||||||
|
"machine_width": { "default_value": 210 },
|
||||||
|
"machine_depth": { "default_value": 210 },
|
||||||
|
"machine_height": { "default_value": 320 },
|
||||||
|
"machine_extruder_count": { "default_value": 1 },
|
||||||
|
"machine_heated_bed": { "default_value": false },
|
||||||
|
"machine_center_is_zero": { "default_value": true },
|
||||||
|
|
||||||
|
"machine_start_gcode": { "default_value": "G21\nG90 \nM107\nG28\nG92 E0\nG1 F200 E3\nG92 E0" },
|
||||||
|
"machine_end_gcode": { "default_value": "M104 S0\nG28\nG91\nG1 E-6 F300\nM84\nG90" },
|
||||||
|
|
||||||
|
"layer_height": { "default_value": 0.2 },
|
||||||
|
"default_material_print_temperature": { "default_value": 210 },
|
||||||
|
"speed_print": { "default_value": 32 },
|
||||||
|
"optimize_wall_printing_order": { "value": "True" },
|
||||||
|
"infill_sparse_density": { "default_value": 10 },
|
||||||
|
"brim_width": { "default_value": 4 }
|
||||||
|
}
|
||||||
|
}
|
@ -85,8 +85,8 @@
|
|||||||
"material_flow": { "value": 100 },
|
"material_flow": { "value": 100 },
|
||||||
"travel_compensate_overlapping_walls_0_enabled": { "value": "False" },
|
"travel_compensate_overlapping_walls_0_enabled": { "value": "False" },
|
||||||
|
|
||||||
"z_seam_type": { "value": "'back'" },
|
"z_seam_type": { "value": "'sharpest_corner'" },
|
||||||
"z_seam_corner": { "value": "'z_seam_corner_weighted'" },
|
"z_seam_corner": { "value": "'z_seam_corner_inner'" },
|
||||||
|
|
||||||
"infill_line_width": { "value": "line_width * 1.2" },
|
"infill_line_width": { "value": "line_width * 1.2" },
|
||||||
"infill_sparse_density": { "value": "20" },
|
"infill_sparse_density": { "value": "20" },
|
||||||
@ -156,7 +156,7 @@
|
|||||||
|
|
||||||
"support_interface_enable": { "value": true },
|
"support_interface_enable": { "value": true },
|
||||||
"support_interface_height": { "value": "layer_height * 4" },
|
"support_interface_height": { "value": "layer_height * 4" },
|
||||||
"support_interface_density": { "value": 33.333 },
|
"support_interface_density": { "value": 75 },
|
||||||
"support_interface_pattern": { "value": "'grid'" },
|
"support_interface_pattern": { "value": "'grid'" },
|
||||||
"support_interface_skip_height": { "value": 0.2 },
|
"support_interface_skip_height": { "value": 0.2 },
|
||||||
"minimum_support_area": { "value": 2 },
|
"minimum_support_area": { "value": 2 },
|
||||||
|
51
resources/definitions/biqu_bx_abl.def.json
Executable file
51
resources/definitions/biqu_bx_abl.def.json
Executable file
@ -0,0 +1,51 @@
|
|||||||
|
{
|
||||||
|
"name": "Biqu BX",
|
||||||
|
"version": 2,
|
||||||
|
"inherits": "biqu_base",
|
||||||
|
"metadata": {
|
||||||
|
"quality_definition": "biqu_base",
|
||||||
|
"visible": true,
|
||||||
|
"has_machine_materials": true,
|
||||||
|
"platform": "BIQU_BX_PLATE.stl",
|
||||||
|
"platform_offset": [
|
||||||
|
7,
|
||||||
|
-7.4,
|
||||||
|
-3
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"overrides": {
|
||||||
|
"coasting_enable": { "value": false },
|
||||||
|
"retraction_amount": { "value": 1 },
|
||||||
|
"retraction_speed": { "value": 40 },
|
||||||
|
"retraction_extrusion_window": { "value": 1 },
|
||||||
|
"retract_at_layer_change": { "value": true },
|
||||||
|
"support_enable": { "value": false },
|
||||||
|
"support_structure": { "value": "'normal'" },
|
||||||
|
"support_type": { "value": "'buildplate'" },
|
||||||
|
"support_angle": { "value": 45 },
|
||||||
|
"support_infill_rate": { "value": 15 },
|
||||||
|
"infill_overlap": { "value": 15.0 },
|
||||||
|
"skin_overlap": { "value": 20.0 },
|
||||||
|
"fill_outline_gaps": { "value": true },
|
||||||
|
"filter_out_tiny_gaps": { "value": true },
|
||||||
|
"roofing_layer_count": { "value": 2 },
|
||||||
|
"xy_offset_layer_0": { "value": -0.1 },
|
||||||
|
"speed_print": { "value": 50 },
|
||||||
|
"machine_name": { "default_value": "Biqu BX" },
|
||||||
|
"machine_width": { "value": 250 },
|
||||||
|
"machine_depth": { "value": 250 },
|
||||||
|
"machine_height": { "value": 250 },
|
||||||
|
"machine_head_with_fans_polygon": { "default_value": [
|
||||||
|
[-33, 35],
|
||||||
|
[-33, -23],
|
||||||
|
[33, -23],
|
||||||
|
[33, 35]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"machine_start_gcode": {
|
||||||
|
"default_value": "; BIQU BX Start G-code\r\n; For inforation on how to tune this profile and get the\r\n; most out of your BX visit: https:\/\/github.com\/looxonline\/Marlin\r\n; For the official github site visit: https:\/\/github.com\/bigtreetech\/BIQU-BX\r\n\r\nM117 Initial homing sequence. ; Home so that the probe is positioned to heat\r\nG28\r\n\r\nM117 Probe heating position\r\nG0 X65 Y5 Z1 ; Move the probe to the heating position.\r\n\r\nM117 Getting the heaters up to temp!\r\nM104 S140 ; Set Extruder temperature, no wait\r\nM140 S60 ; Set Heat Bed temperature\r\nM190 S60 ; Wait for Heat Bed temperature\r\n\r\nM117 Waiting for probe to warm! ; Wait another 90s for the probe to absorb heat.\r\nG4 S90 \r\n\r\nM117 Post warming re-home\r\nG28 ; Home all axes again after warming\r\n\r\nM117 Z-Dance of my people\r\nG34\r\n\r\nM117 ABL Probing\r\nG29\r\n\r\nM900 K0 L0 T0 ;Edit the K and L values if you have calibrated a k factor for your filament\r\nM900 T0 S0\r\n\r\nG1 Z2.0 F3000 ; Move Z Axis up little to prevent scratching of Heat Bed\r\nG1 X4.1 Y10 Z0.3 F5000.0 ; Move to start position\r\n\r\nM117 Getting the extruder up to temp\r\nM140 S{material_bed_temperature_layer_0} ; Set Heat Bed temperature\r\nM104 S{material_print_temperature_layer_0} ; Set Extruder temperature\r\nM109 S{material_print_temperature_layer_0} ; Wait for Extruder temperature\r\nM190 S{material_bed_temperature_layer_0} ; Wait for Heat Bed temperature\r\n\r\nG92 E0 ; Reset Extruder\r\nM117 Purging\r\nG1 X4.1 Y200.0 Z0.3 F1500.0 E15 ; Draw the first line\r\nG1 X4.4 Y200.0 Z0.3 F5000.0 ; Move to side a little\r\nG1 X4.4 Y20 Z0.3 F1500.0 E30 ; Draw the second line\r\nG92 E0 ; Reset Extruder\r\nM117 Lets make\r\nG1 X8 Y20 Z0.3 F5000.0 ; Move over to prevent blob squish"
|
||||||
|
},
|
||||||
|
|
||||||
|
"gantry_height": { "value": 27.5 }
|
||||||
|
}
|
||||||
|
}
|
76
resources/definitions/flsun_sr.def.json
Normal file
76
resources/definitions/flsun_sr.def.json
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
{
|
||||||
|
"version": 2,
|
||||||
|
"name": "FlSun SuperRacer",
|
||||||
|
"inherits": "fdmprinter",
|
||||||
|
"metadata": {
|
||||||
|
"visible": true,
|
||||||
|
"author": "Thushan Fernando",
|
||||||
|
"manufacturer": "Flsun",
|
||||||
|
"platform": "flsun_sr.3mf",
|
||||||
|
"file_formats": "text/x-gcode",
|
||||||
|
"has_materials": true,
|
||||||
|
"has_machine_quality": false,
|
||||||
|
"preferred_quality_type": "fast",
|
||||||
|
"machine_extruder_trains": {
|
||||||
|
"0": "flsun_sr_extruder_0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"overrides": {
|
||||||
|
"machine_extruder_count": {
|
||||||
|
"default_value": 1
|
||||||
|
},
|
||||||
|
"retraction_enable": {
|
||||||
|
"default_value": true
|
||||||
|
},
|
||||||
|
"machine_heated_bed": {
|
||||||
|
"default_value": true
|
||||||
|
},
|
||||||
|
"machine_width": {
|
||||||
|
"default_value": 260
|
||||||
|
},
|
||||||
|
"machine_depth": {
|
||||||
|
"default_value": 260
|
||||||
|
},
|
||||||
|
"machine_height": {
|
||||||
|
"default_value": 330
|
||||||
|
},
|
||||||
|
"machine_center_is_zero": {
|
||||||
|
"default_value": true
|
||||||
|
},
|
||||||
|
"machine_head_with_fans_polygon": {
|
||||||
|
"default_value": [
|
||||||
|
[0, 0],
|
||||||
|
[0, 0],
|
||||||
|
[0, 0],
|
||||||
|
[0, 0]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"z_seam_type": {
|
||||||
|
"value": "'back'"
|
||||||
|
},
|
||||||
|
"gantry_height": {
|
||||||
|
"value": "0"
|
||||||
|
},
|
||||||
|
"machine_shape": {
|
||||||
|
"default_value": "elliptic"
|
||||||
|
},
|
||||||
|
"machine_nozzle_size": {
|
||||||
|
"default_value": 0.4
|
||||||
|
},
|
||||||
|
"material_diameter": {
|
||||||
|
"default_value": 1.75
|
||||||
|
},
|
||||||
|
"infill_sparse_density": {
|
||||||
|
"default_value": 15
|
||||||
|
},
|
||||||
|
"machine_start_gcode": {
|
||||||
|
"default_value": "G21\nG90\nM82\nM107 T0\nM140 S{material_bed_temperature}\nM104 S{material_print_temperature} T0\nM190 S{material_bed_temperature}\nM109 S{material_print_temperature} T0\nG28\nG92 E0\n"
|
||||||
|
},
|
||||||
|
"machine_end_gcode": {
|
||||||
|
"default_value": "M107 T0\nM104 S0\nM104 S0 T1\nM140 S0\nG92 E0\nG91\nG1 E-1 F300 \nG1 Z+0.5 E-5 F6000\nG28 X0 Y0\nG90 ;absolute positioning\n"
|
||||||
|
},
|
||||||
|
"machine_gcode_flavor": {
|
||||||
|
"default_value": "RepRap (Marlin/Sprinter)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -4,7 +4,7 @@
|
|||||||
"inherits": "fdmprinter",
|
"inherits": "fdmprinter",
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"author": "William & Cataldo URSO",
|
"author": "William & Cataldo URSO",
|
||||||
"manufacturer": "Shenzhen Geeetech Technology",
|
"manufacturer": "Geeetech",
|
||||||
"file_formats": "text/x-gcode",
|
"file_formats": "text/x-gcode",
|
||||||
"visible": true,
|
"visible": true,
|
||||||
"has_materials": true,
|
"has_materials": true,
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
"name": "MP Mini Delta",
|
"name": "MP Mini Delta",
|
||||||
"inherits": "fdmprinter",
|
"inherits": "fdmprinter",
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"author": "MPMD Facebook Group",
|
"author": "MPMD V1 Facebook Group",
|
||||||
"manufacturer": "Monoprice",
|
"manufacturer": "Monoprice",
|
||||||
"file_formats": "text/x-gcode",
|
"file_formats": "text/x-gcode",
|
||||||
"platform": "mp_mini_delta_platform.3mf",
|
"platform": "mp_mini_delta_platform.3mf",
|
||||||
@ -25,7 +25,7 @@
|
|||||||
"overrides": {
|
"overrides": {
|
||||||
"machine_start_gcode":
|
"machine_start_gcode":
|
||||||
{
|
{
|
||||||
"default_value": ";MPMD Basic Calibration Tutorial: \n; https://www.thingiverse.com/thing:3892011 \n; \n; If you want to put calibration values in your \n; Start Gcode, put them here. \n; \n;If on stock firmware, at minimum, consider adding \n;M665 R here since there is a firmware bug. \n; \n; Calibration part ends here \n; \nG90 ; switch to absolute positioning \nG92 E0 ; reset extrusion distance \nG1 E20 F200 ; purge 20mm of filament to prime nozzle. \nG92 E0 ; reset extrusion distance \nG4 S5 ; Pause for 5 seconds to allow time for removing extruded filament \nG28 ; start from home position \nG1 E-6 F900 ; retract 6mm of filament before starting the bed leveling process \nG92 E0 ; reset extrusion distance \nG4 S5 ; pause for 5 seconds to allow time for removing extruded filament \nG29 P2 Z0.28 ; Auto-level ; ADJUST Z higher or lower to set first layer height. Start with 0.02 adjustments. \nG1 Z30 ; raise Z 30mm to prepare for priming the nozzle \nG1 E5 F200 ; extrude 5mm of filament to help prime the nozzle just prior to the start of the print \nG92 E0 ; reset extrusion distance \nG4 S5 ; pause for 5 seconds to allow time for cleaning the nozzle and build plate if needed "
|
"default_value": ";MPMD V1 Basic Calibration Tutorial: \n; https://www.thingiverse.com/thing:3892011 \n; \n; If you want to put calibration values in your \n; Start Gcode, put them here. \n; \n;If on stock firmware, at minimum, consider adding \n;M665 R here since there is a firmware bug. \n; \n; Calibration part ends here \n; \nG90 ; switch to absolute positioning \nG92 E0 ; reset extrusion distance \nG1 E20 F200 ; purge 20mm of filament to prime nozzle. \nG92 E0 ; reset extrusion distance \nG4 S5 ; Pause for 5 seconds to allow time for removing extruded filament \nG28 ; start from home position \nG1 E-6 F900 ; retract 6mm of filament before starting the bed leveling process \nG92 E0 ; reset extrusion distance \nG4 S5 ; pause for 5 seconds to allow time for removing extruded filament \nG29 P2 Z0.28 ; Auto-level ; ADJUST Z higher or lower to set first layer height. Start with 0.02 adjustments. \nG1 Z30 ; raise Z 30mm to prepare for priming the nozzle \nG1 E5 F200 ; extrude 5mm of filament to help prime the nozzle just prior to the start of the print \nG92 E0 ; reset extrusion distance \nG4 S5 ; pause for 5 seconds to allow time for cleaning the nozzle and build plate if needed "
|
||||||
},
|
},
|
||||||
"machine_end_gcode":
|
"machine_end_gcode":
|
||||||
{
|
{
|
||||||
@ -47,9 +47,9 @@
|
|||||||
"default_value": 0.21
|
"default_value": 0.21
|
||||||
},
|
},
|
||||||
"material_bed_temperature": { "value": 40 },
|
"material_bed_temperature": { "value": 40 },
|
||||||
"line_width": { "value": "round(machine_nozzle_size * 0.875, 2)" },
|
"line_width": { "value": "round(machine_nozzle_size, 2)" },
|
||||||
"material_print_temperature_layer_0": { "value": "material_print_temperature + 5" },
|
"material_print_temperature_layer_0": { "value": "material_print_temperature" },
|
||||||
"material_bed_temperature_layer_0": { "value": "material_bed_temperature + 5" },
|
"material_bed_temperature_layer_0": { "value": "material_bed_temperature" },
|
||||||
"machine_gcode_flavor": { "default_value": "RepRap (Marlin/Sprinter)" },
|
"machine_gcode_flavor": { "default_value": "RepRap (Marlin/Sprinter)" },
|
||||||
"machine_max_feedrate_x": { "default_value": 150 },
|
"machine_max_feedrate_x": { "default_value": 150 },
|
||||||
"machine_max_feedrate_y": { "default_value": 150 },
|
"machine_max_feedrate_y": { "default_value": 150 },
|
||||||
|
51
resources/definitions/mp_mini_delta_v2.def.json
Normal file
51
resources/definitions/mp_mini_delta_v2.def.json
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
{
|
||||||
|
"version": 2,
|
||||||
|
"name": "MP Mini Delta V2",
|
||||||
|
"inherits": "fdmprinter",
|
||||||
|
"metadata": {
|
||||||
|
"author": "mpminidelta subreddit",
|
||||||
|
"manufacturer": "Monoprice",
|
||||||
|
"file_formats": "text/x-gcode",
|
||||||
|
"platform": "mp_mini_delta_platform.3mf",
|
||||||
|
"supports_usb_connection": true,
|
||||||
|
"has_machine_quality": false,
|
||||||
|
"visible": true,
|
||||||
|
"platform_offset": [0, 0, 0],
|
||||||
|
"has_materials": true,
|
||||||
|
"has_variants": false,
|
||||||
|
"has_machine_materials": false,
|
||||||
|
"has_variant_materials": false,
|
||||||
|
"preferred_quality_type": "normal",
|
||||||
|
"machine_extruder_trains":
|
||||||
|
{
|
||||||
|
"0": "mp_mini_delta_v2_extruder_0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"overrides": {
|
||||||
|
"machine_start_gcode":
|
||||||
|
{
|
||||||
|
"default_value": ";(**** start.gcode for MP Mini Delta V2****)\nG21\nG90\nM82\nM107\nM104 S170\nG28 X0 Y0\nG28 Z0\nG29 Z0.4\nG1 Z15 F300\nM109 S{material_print_temperature_layer_0}\nG92 E0\nG1 F200 E3\nG92 E0\nG1 F2000\n"
|
||||||
|
},
|
||||||
|
"machine_end_gcode": {
|
||||||
|
"default_value": ";(**** end.gcode for MP Mini Delta V2****)\nG28;(Stick out the part)\nM190 S0;(Turn off heat bed, don't wait.)\nG92 E10;(Set extruder to 10)\nG1 E7 F200;(retract 3mm)\nM104 S0;(Turn off nozzle, don't wait)\nG4 S300;(Delay 5 minutes)\nM107;(Turn off part fan)\nM84;(Turn off stepper motors.)"
|
||||||
|
},
|
||||||
|
"material_print_temp_prepend":{"default_value":false},
|
||||||
|
"material_bed_temperature": { "value": 40 },
|
||||||
|
"machine_width": { "default_value": 110 },
|
||||||
|
"machine_depth": { "default_value": 110 },
|
||||||
|
"machine_height": { "default_value": 120 },
|
||||||
|
"machine_heated_bed": { "default_value": true },
|
||||||
|
"machine_shape": { "default_value": "elliptic" },
|
||||||
|
"machine_center_is_zero": { "default_value": true },
|
||||||
|
"machine_nozzle_size": {
|
||||||
|
"default_value": 0.4
|
||||||
|
},
|
||||||
|
"line_width": { "value": "round(machine_nozzle_size, 2)" },
|
||||||
|
"machine_gcode_flavor": { "default_value": "RepRap (Marlin/Sprinter)" },
|
||||||
|
"retraction_amount": { "default_value": 5 },
|
||||||
|
"retraction_speed": { "default_value": 28 },
|
||||||
|
"retraction_hop_enabled": { "default_value": false },
|
||||||
|
"retract_at_layer_change": { "default_value": true }
|
||||||
|
}
|
||||||
|
}
|
@ -22,7 +22,8 @@
|
|||||||
"0": "ultimaker2_plus_connect_extruder_0"
|
"0": "ultimaker2_plus_connect_extruder_0"
|
||||||
},
|
},
|
||||||
"supports_usb_connection": false,
|
"supports_usb_connection": false,
|
||||||
"supports_network_connection": true
|
"supports_network_connection": true,
|
||||||
|
"supports_material_export": true
|
||||||
},
|
},
|
||||||
|
|
||||||
"overrides": {
|
"overrides": {
|
||||||
|
@ -27,6 +27,7 @@
|
|||||||
"first_start_actions": [ "DiscoverUM3Action" ],
|
"first_start_actions": [ "DiscoverUM3Action" ],
|
||||||
"supported_actions": [ "DiscoverUM3Action" ],
|
"supported_actions": [ "DiscoverUM3Action" ],
|
||||||
"supports_usb_connection": false,
|
"supports_usb_connection": false,
|
||||||
|
"supports_material_export": true,
|
||||||
"weight": -1,
|
"weight": -1,
|
||||||
"firmware_update_info": {
|
"firmware_update_info": {
|
||||||
"id": 213482,
|
"id": 213482,
|
||||||
|
@ -28,6 +28,7 @@
|
|||||||
"supported_actions": [ "DiscoverUM3Action" ],
|
"supported_actions": [ "DiscoverUM3Action" ],
|
||||||
"supports_usb_connection": false,
|
"supports_usb_connection": false,
|
||||||
"supports_network_connection": true,
|
"supports_network_connection": true,
|
||||||
|
"supports_material_export": true,
|
||||||
"weight": -2,
|
"weight": -2,
|
||||||
"firmware_update_info": {
|
"firmware_update_info": {
|
||||||
"id": 9051,
|
"id": 9051,
|
||||||
|
15
resources/extruders/atom2_extruder_0.def.json
Normal file
15
resources/extruders/atom2_extruder_0.def.json
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"version": 2,
|
||||||
|
"name": "Extruder 1",
|
||||||
|
"inherits": "fdmextruder",
|
||||||
|
"metadata": {
|
||||||
|
"machine": "atom2",
|
||||||
|
"position": "0"
|
||||||
|
},
|
||||||
|
|
||||||
|
"overrides": {
|
||||||
|
"extruder_nr": { "default_value": 0 },
|
||||||
|
"machine_nozzle_size": { "default_value": 0.4 },
|
||||||
|
"material_diameter": { "default_value": 1.75 }
|
||||||
|
}
|
||||||
|
}
|
20
resources/extruders/flsun_sr_extruder_0.def.json
Normal file
20
resources/extruders/flsun_sr_extruder_0.def.json
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"version": 2,
|
||||||
|
"name": "Extruder 1",
|
||||||
|
"inherits": "fdmextruder",
|
||||||
|
"metadata": {
|
||||||
|
"machine": "flsun_sr",
|
||||||
|
"position": "0"
|
||||||
|
},
|
||||||
|
"overrides": {
|
||||||
|
"extruder_nr": {
|
||||||
|
"default_value": 0
|
||||||
|
},
|
||||||
|
"machine_nozzle_size": {
|
||||||
|
"default_value": 0.4
|
||||||
|
},
|
||||||
|
"material_diameter": {
|
||||||
|
"default_value": 1.75
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
15
resources/extruders/mp_mini_delta_v2_extruder_0.def.json
Normal file
15
resources/extruders/mp_mini_delta_v2_extruder_0.def.json
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"version": 2,
|
||||||
|
"name": "Extruder 0",
|
||||||
|
"inherits": "fdmextruder",
|
||||||
|
"metadata": {
|
||||||
|
"machine": "mp_mini_delta_v2",
|
||||||
|
"position": "0"
|
||||||
|
},
|
||||||
|
|
||||||
|
"overrides": {
|
||||||
|
"extruder_nr": { "default_value": 0 },
|
||||||
|
"machine_nozzle_size": { "default_value": 0.4 },
|
||||||
|
"material_diameter": { "default_value": 1.75 }
|
||||||
|
}
|
||||||
|
}
|
@ -1177,7 +1177,7 @@ msgstr "Hinten links"
|
|||||||
#: fdmprinter.def.json
|
#: fdmprinter.def.json
|
||||||
msgctxt "z_seam_position option back"
|
msgctxt "z_seam_position option back"
|
||||||
msgid "Back"
|
msgid "Back"
|
||||||
msgstr "Zurück"
|
msgstr "Hinten"
|
||||||
|
|
||||||
#: fdmprinter.def.json
|
#: fdmprinter.def.json
|
||||||
msgctxt "z_seam_position option backright"
|
msgctxt "z_seam_position option backright"
|
||||||
|
BIN
resources/meshes/BIQU_BX_PLATE.stl
Normal file
BIN
resources/meshes/BIQU_BX_PLATE.stl
Normal file
Binary file not shown.
BIN
resources/meshes/flsun_sr.3mf
Normal file
BIN
resources/meshes/flsun_sr.3mf
Normal file
Binary file not shown.
@ -18,6 +18,7 @@ Button
|
|||||||
property alias textFont: buttonText.font
|
property alias textFont: buttonText.font
|
||||||
property alias cornerRadius: backgroundRect.radius
|
property alias cornerRadius: backgroundRect.radius
|
||||||
property alias tooltip: tooltip.tooltipText
|
property alias tooltip: tooltip.tooltipText
|
||||||
|
property alias tooltipWidth: tooltip.width
|
||||||
|
|
||||||
property color color: UM.Theme.getColor("primary")
|
property color color: UM.Theme.getColor("primary")
|
||||||
property color hoverColor: UM.Theme.getColor("primary_hover")
|
property color hoverColor: UM.Theme.getColor("primary_hover")
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright (c) 2018 Ultimaker B.V.
|
// Copyright (c) 2021 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.
|
||||||
|
|
||||||
pragma Singleton
|
pragma Singleton
|
||||||
@ -122,7 +122,15 @@ Item
|
|||||||
Action
|
Action
|
||||||
{
|
{
|
||||||
id: quitAction
|
id: quitAction
|
||||||
text: catalog.i18nc("@action:inmenu menubar:file","&Quit")
|
|
||||||
|
//On MacOS, don't translate the "Quit" word.
|
||||||
|
//Qt moves the "quit" entry to a different place, and if it got renamed can't find it again when it attempts to
|
||||||
|
//delete the item upon closing the application, causing a crash.
|
||||||
|
//In the new location, these items are translated automatically according to the system's language.
|
||||||
|
//For more information, see:
|
||||||
|
//- https://doc.qt.io/qt-5/macos-issues.html#menu-bar
|
||||||
|
//- https://doc.qt.io/qt-5/qmenubar.html#qmenubar-as-a-global-menu-bar
|
||||||
|
text: (Qt.platform.os == "osx") ? "&Quit" : catalog.i18nc("@action:inmenu menubar:file", "&Quit")
|
||||||
iconName: "application-exit"
|
iconName: "application-exit"
|
||||||
shortcut: StandardKey.Quit
|
shortcut: StandardKey.Quit
|
||||||
}
|
}
|
||||||
@ -172,7 +180,14 @@ Item
|
|||||||
Action
|
Action
|
||||||
{
|
{
|
||||||
id: preferencesAction
|
id: preferencesAction
|
||||||
text: catalog.i18nc("@action:inmenu", "Configure Cura...")
|
//On MacOS, don't translate the "Configure" word.
|
||||||
|
//Qt moves the "configure" entry to a different place, and if it got renamed can't find it again when it
|
||||||
|
//attempts to delete the item upon closing the application, causing a crash.
|
||||||
|
//In the new location, these items are translated automatically according to the system's language.
|
||||||
|
//For more information, see:
|
||||||
|
//- https://doc.qt.io/qt-5/macos-issues.html#menu-bar
|
||||||
|
//- https://doc.qt.io/qt-5/qmenubar.html#qmenubar-as-a-global-menu-bar
|
||||||
|
text: (Qt.platform.os == "osx") ? "Configure Cura..." : catalog.i18nc("@action:inmenu", "Configure Cura...")
|
||||||
iconName: "configure"
|
iconName: "configure"
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -263,7 +278,15 @@ Item
|
|||||||
Action
|
Action
|
||||||
{
|
{
|
||||||
id: aboutAction;
|
id: aboutAction;
|
||||||
text: catalog.i18nc("@action:inmenu menubar:help", "About...");
|
|
||||||
|
//On MacOS, don't translate the "About" word.
|
||||||
|
//Qt moves the "about" entry to a different place, and if it got renamed can't find it again when it
|
||||||
|
//attempts to delete the item upon closing the application, causing a crash.
|
||||||
|
//In the new location, these items are translated automatically according to the system's language.
|
||||||
|
//For more information, see:
|
||||||
|
//- https://doc.qt.io/qt-5/macos-issues.html#menu-bar
|
||||||
|
//- https://doc.qt.io/qt-5/qmenubar.html#qmenubar-as-a-global-menu-bar
|
||||||
|
text: (Qt.platform.os == "osx") ? "About..." : catalog.i18nc("@action:inmenu menubar:help", "About...");
|
||||||
iconName: "help-about";
|
iconName: "help-about";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -417,6 +417,7 @@ UM.MainWindow
|
|||||||
Cura.PrimaryButton
|
Cura.PrimaryButton
|
||||||
{
|
{
|
||||||
text: model.name
|
text: model.name
|
||||||
|
iconSource: UM.Theme.getIcon(model.icon)
|
||||||
height: UM.Theme.getSize("message_action_button").height
|
height: UM.Theme.getSize("message_action_button").height
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -426,6 +427,7 @@ UM.MainWindow
|
|||||||
Cura.SecondaryButton
|
Cura.SecondaryButton
|
||||||
{
|
{
|
||||||
text: model.name
|
text: model.name
|
||||||
|
iconSource: UM.Theme.getIcon(model.icon)
|
||||||
height: UM.Theme.getSize("message_action_button").height
|
height: UM.Theme.getSize("message_action_button").height
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -434,6 +436,14 @@ UM.MainWindow
|
|||||||
Cura.TertiaryButton
|
Cura.TertiaryButton
|
||||||
{
|
{
|
||||||
text: model.name
|
text: model.name
|
||||||
|
iconSource:
|
||||||
|
{
|
||||||
|
if (model.icon == null || model.icon == "")
|
||||||
|
{
|
||||||
|
return UM.Theme.getIcon("LinkExternal")
|
||||||
|
}
|
||||||
|
return UM.Theme.getIcon(model.icon)
|
||||||
|
}
|
||||||
height: UM.Theme.getSize("message_action_button").height
|
height: UM.Theme.getSize("message_action_button").height
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -167,7 +167,7 @@ Item
|
|||||||
verticalCenter: parent.verticalCenter
|
verticalCenter: parent.verticalCenter
|
||||||
margins: background.padding
|
margins: background.padding
|
||||||
}
|
}
|
||||||
source: expanded ? UM.Theme.getIcon("ChevronSingleDown") : UM.Theme.getIcon("ChevronSingleLeft")
|
source: UM.Theme.getIcon("ChevronSingleDown")
|
||||||
visible: source != ""
|
visible: source != ""
|
||||||
width: UM.Theme.getSize("standard_arrow").width
|
width: UM.Theme.getSize("standard_arrow").width
|
||||||
height: UM.Theme.getSize("standard_arrow").height
|
height: UM.Theme.getSize("standard_arrow").height
|
||||||
|
@ -180,7 +180,7 @@ Item
|
|||||||
verticalCenter: parent.verticalCenter
|
verticalCenter: parent.verticalCenter
|
||||||
margins: background.padding
|
margins: background.padding
|
||||||
}
|
}
|
||||||
source: expanded ? UM.Theme.getIcon("ChevronSingleDown") : UM.Theme.getIcon("ChevronSingleLeft")
|
source: UM.Theme.getIcon("ChevronSingleDown")
|
||||||
visible: source != ""
|
visible: source != ""
|
||||||
width: UM.Theme.getSize("standard_arrow").width
|
width: UM.Theme.getSize("standard_arrow").width
|
||||||
height: UM.Theme.getSize("standard_arrow").height
|
height: UM.Theme.getSize("standard_arrow").height
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright (c) 2018 Ultimaker B.V.
|
// Copyright (c) 2021 Ultimaker B.V.
|
||||||
// Cura is released under the terms of the LGPLv3 or higher.
|
// Cura is released under the terms of the LGPLv3 or higher.
|
||||||
|
|
||||||
import QtQuick 2.7
|
import QtQuick 2.7
|
||||||
@ -48,7 +48,17 @@ Item
|
|||||||
|
|
||||||
ViewMenu { title: catalog.i18nc("@title:menu menubar:toplevel", "&View") }
|
ViewMenu { title: catalog.i18nc("@title:menu menubar:toplevel", "&View") }
|
||||||
|
|
||||||
SettingsMenu { title: catalog.i18nc("@title:menu menubar:toplevel", "&Settings") }
|
SettingsMenu
|
||||||
|
{
|
||||||
|
//On MacOS, don't translate the "Settings" word.
|
||||||
|
//Qt moves the "settings" entry to a different place, and if it got renamed can't find it again when it
|
||||||
|
//attempts to delete the item upon closing the application, causing a crash.
|
||||||
|
//In the new location, these items are translated automatically according to the system's language.
|
||||||
|
//For more information, see:
|
||||||
|
//- https://doc.qt.io/qt-5/macos-issues.html#menu-bar
|
||||||
|
//- https://doc.qt.io/qt-5/qmenubar.html#qmenubar-as-a-global-menu-bar
|
||||||
|
title: (Qt.platform.os == "osx") ? "&Settings" : catalog.i18nc("@title:menu menubar:toplevel", "&Settings")
|
||||||
|
}
|
||||||
|
|
||||||
Menu
|
Menu
|
||||||
{
|
{
|
||||||
@ -91,7 +101,15 @@ Item
|
|||||||
Menu
|
Menu
|
||||||
{
|
{
|
||||||
id: preferencesMenu
|
id: preferencesMenu
|
||||||
title: catalog.i18nc("@title:menu menubar:toplevel", "P&references")
|
|
||||||
|
//On MacOS, don't translate the "Preferences" word.
|
||||||
|
//Qt moves the "preferences" entry to a different place, and if it got renamed can't find it again when it
|
||||||
|
//attempts to delete the item upon closing the application, causing a crash.
|
||||||
|
//In the new location, these items are translated automatically according to the system's language.
|
||||||
|
//For more information, see:
|
||||||
|
//- https://doc.qt.io/qt-5/macos-issues.html#menu-bar
|
||||||
|
//- https://doc.qt.io/qt-5/qmenubar.html#qmenubar-as-a-global-menu-bar
|
||||||
|
title: (Qt.platform.os == "osx") ? "&Preferences" : catalog.i18nc("@title:menu menubar:toplevel", "P&references")
|
||||||
|
|
||||||
MenuItem { action: Cura.Actions.preferences }
|
MenuItem { action: Cura.Actions.preferences }
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
// Copyright (c) 2018 Ultimaker B.V.
|
// Copyright (c) 2021 Ultimaker B.V.
|
||||||
// Uranium is released under the terms of the LGPLv3 or higher.
|
// Cura is released under the terms of the LGPLv3 or higher.
|
||||||
|
|
||||||
import QtQuick 2.7
|
import QtQuick 2.7
|
||||||
import QtQuick.Controls 1.4
|
import QtQuick.Controls 1.4
|
||||||
@ -191,6 +191,21 @@ Item
|
|||||||
}
|
}
|
||||||
enabled: base.hasCurrentItem
|
enabled: base.hasCurrentItem
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Sync button.
|
||||||
|
Button
|
||||||
|
{
|
||||||
|
id: syncMaterialsButton
|
||||||
|
text: catalog.i18nc("@action:button Sending materials to printers", "Sync with Printers")
|
||||||
|
iconName: "sync-synchronizing"
|
||||||
|
onClicked:
|
||||||
|
{
|
||||||
|
forceActiveFocus();
|
||||||
|
exportAllMaterialsDialog.folder = base.materialManagementModel.getPreferredExportAllPath();
|
||||||
|
exportAllMaterialsDialog.open();
|
||||||
|
}
|
||||||
|
visible: Cura.MachineManager.activeMachine.supportsMaterialExport
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
@ -368,6 +383,19 @@ Item
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FileDialog
|
||||||
|
{
|
||||||
|
id: exportAllMaterialsDialog
|
||||||
|
title: catalog.i18nc("@title:window", "Export All Materials")
|
||||||
|
selectExisting: false
|
||||||
|
nameFilters: ["Material archives (*.umm)", "All files (*)"]
|
||||||
|
onAccepted:
|
||||||
|
{
|
||||||
|
base.materialManagementModel.exportAll(fileUrl);
|
||||||
|
CuraApplication.setDefaultPath("dialog_material_path", folder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
MessageDialog
|
MessageDialog
|
||||||
{
|
{
|
||||||
id: messageDialog
|
id: messageDialog
|
||||||
|
@ -95,7 +95,7 @@ UM.PreferencesPage
|
|||||||
|
|
||||||
placeholderText: catalog.i18nc("@label:textbox", "Filter...")
|
placeholderText: catalog.i18nc("@label:textbox", "Filter...")
|
||||||
|
|
||||||
onTextChanged: definitionsModel.filter = {"i18n_label": "*" + text}
|
onTextChanged: definitionsModel.filter = {"i18n_label|i18n_description": "*" + text}
|
||||||
}
|
}
|
||||||
|
|
||||||
NewControls.ComboBox
|
NewControls.ComboBox
|
||||||
|
@ -54,7 +54,7 @@ Item
|
|||||||
{
|
{
|
||||||
id: networkPrinterScrollView
|
id: networkPrinterScrollView
|
||||||
|
|
||||||
maxItemCountAtOnce: 10 // show at max 10 items at once, otherwise you need to scroll.
|
maxItemCountAtOnce: 9 // show at max 9 items at once, otherwise you need to scroll.
|
||||||
|
|
||||||
onRefreshButtonClicked:
|
onRefreshButtonClicked:
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright (c) 2019 Ultimaker B.V.
|
// Copyright (c) 2021 Ultimaker B.V.
|
||||||
// Cura is released under the terms of the LGPLv3 or higher.
|
// Cura is released under the terms of the LGPLv3 or higher.
|
||||||
|
|
||||||
import QtQuick 2.10
|
import QtQuick 2.10
|
||||||
@ -214,16 +214,16 @@ Item
|
|||||||
id: troubleshootingButton
|
id: troubleshootingButton
|
||||||
|
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
anchors.rightMargin: UM.Theme.getSize("default_margin").width
|
anchors.rightMargin: UM.Theme.getSize("thin_margin").width
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
height: troubleshootingLinkIcon.height
|
height: troubleshootingLinkIcon.height
|
||||||
width: troubleshootingLinkIcon.width + troubleshootingLabel.width + UM.Theme.getSize("default_margin").width
|
width: troubleshootingLinkIcon.width + troubleshootingLabel.width + UM.Theme.getSize("thin_margin").width
|
||||||
|
|
||||||
UM.RecolorImage
|
UM.RecolorImage
|
||||||
{
|
{
|
||||||
id: troubleshootingLinkIcon
|
id: troubleshootingLinkIcon
|
||||||
anchors.right: troubleshootingLabel.left
|
anchors.right: troubleshootingLabel.left
|
||||||
anchors.rightMargin: UM.Theme.getSize("default_margin").width
|
anchors.rightMargin: UM.Theme.getSize("thin_margin").width
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
height: troubleshootingLabel.height
|
height: troubleshootingLabel.height
|
||||||
width: height
|
width: height
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright (c) 2019 Ultimaker B.V.
|
// Copyright (c) 2021 Ultimaker B.V.
|
||||||
// Cura is released under the terms of the LGPLv3 or higher.
|
// Cura is released under the terms of the LGPLv3 or higher.
|
||||||
|
|
||||||
import QtQuick 2.10
|
import QtQuick 2.10
|
||||||
@ -15,6 +15,8 @@ TextField
|
|||||||
{
|
{
|
||||||
id: textField
|
id: textField
|
||||||
|
|
||||||
|
property alias leftIcon: iconLeft.source
|
||||||
|
|
||||||
UM.I18nCatalog { id: catalog; name: "cura" }
|
UM.I18nCatalog { id: catalog; name: "cura" }
|
||||||
|
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
@ -22,6 +24,7 @@ TextField
|
|||||||
font: UM.Theme.getFont("default")
|
font: UM.Theme.getFont("default")
|
||||||
color: UM.Theme.getColor("text")
|
color: UM.Theme.getColor("text")
|
||||||
renderType: Text.NativeRendering
|
renderType: Text.NativeRendering
|
||||||
|
leftPadding: iconLeft.visible ? iconLeft.width + UM.Theme.getSize("default_margin").width * 2 : UM.Theme.getSize("thin_margin").width
|
||||||
|
|
||||||
states: [
|
states: [
|
||||||
State
|
State
|
||||||
@ -52,7 +55,6 @@ TextField
|
|||||||
|
|
||||||
color: UM.Theme.getColor("main_background")
|
color: UM.Theme.getColor("main_background")
|
||||||
|
|
||||||
anchors.margins: Math.round(UM.Theme.getSize("default_lining").width)
|
|
||||||
radius: UM.Theme.getSize("setting_control_radius").width
|
radius: UM.Theme.getSize("setting_control_radius").width
|
||||||
|
|
||||||
border.color:
|
border.color:
|
||||||
@ -67,5 +69,23 @@ TextField
|
|||||||
}
|
}
|
||||||
return UM.Theme.getColor("setting_control_border")
|
return UM.Theme.getColor("setting_control_border")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Optional icon added on the left hand side.
|
||||||
|
UM.RecolorImage
|
||||||
|
{
|
||||||
|
id: iconLeft
|
||||||
|
|
||||||
|
anchors
|
||||||
|
{
|
||||||
|
verticalCenter: parent.verticalCenter
|
||||||
|
left: parent.left
|
||||||
|
leftMargin: UM.Theme.getSize("default_margin").width
|
||||||
|
}
|
||||||
|
|
||||||
|
visible: source != ""
|
||||||
|
height: UM.Theme.getSize("small_button_icon").height
|
||||||
|
width: visible ? height : 0
|
||||||
|
color: textField.color
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
"primary_text": [255, 255, 255, 204],
|
"primary_text": [255, 255, 255, 204],
|
||||||
"secondary": [95, 95, 95, 255],
|
"secondary": [95, 95, 95, 255],
|
||||||
|
|
||||||
"secondary_button": [0, 0, 0, 0],
|
"secondary_button": [39, 44, 48, 255],
|
||||||
"secondary_button_hover": [85, 85, 87, 255],
|
"secondary_button_hover": [85, 85, 87, 255],
|
||||||
"secondary_button_text": [255, 255, 255, 255],
|
"secondary_button_text": [255, 255, 255, 255],
|
||||||
|
|
||||||
|
@ -6,8 +6,8 @@
|
|||||||
.st0{fill:#231F20;}
|
.st0{fill:#231F20;}
|
||||||
</style>
|
</style>
|
||||||
<g id="Layer_1_1_">
|
<g id="Layer_1_1_">
|
||||||
<path class="st0" d="M15,26C8.9,26,4,21.1,4,15S8.9,4,15,4s11,4.9,11,11S21.1,26,15,26z M15,7c-4.4,0-8,3.6-8,8s3.6,8,8,8
|
<path class="st0" d="M15,26C8.9,26,4,21.1,4,15S8.9,4,15,4s11,4.9,11,11S21.1,26,15,26z M15,8c-3.8,0-7,3.2-7,7s3.2,7,7,7
|
||||||
s8-3.6,8-8S19.4,7,15,7z"/>
|
s7-3.1,7-7S18.9,8,15,8z"/>
|
||||||
</g>
|
</g>
|
||||||
<g id="Comments">
|
<g id="Comments">
|
||||||
</g>
|
</g>
|
||||||
|
Before Width: | Height: | Size: 603 B After Width: | Height: | Size: 603 B |
@ -193,7 +193,7 @@
|
|||||||
"primary_button_hover": [16, 70, 156, 255],
|
"primary_button_hover": [16, 70, 156, 255],
|
||||||
"primary_button_text": [255, 255, 255, 255],
|
"primary_button_text": [255, 255, 255, 255],
|
||||||
|
|
||||||
"secondary_button": [255, 255, 255, 0],
|
"secondary_button": [255, 255, 255, 255],
|
||||||
"secondary_button_shadow": [216, 216, 216, 255],
|
"secondary_button_shadow": [216, 216, 216, 255],
|
||||||
"secondary_button_hover": [232, 240, 253, 255],
|
"secondary_button_hover": [232, 240, 253, 255],
|
||||||
"secondary_button_text": [25, 110, 240, 255],
|
"secondary_button_text": [25, 110, 240, 255],
|
||||||
|
12
resources/variants/biqu_bx_abl_0.2.inst.cfg
Executable file
12
resources/variants/biqu_bx_abl_0.2.inst.cfg
Executable file
@ -0,0 +1,12 @@
|
|||||||
|
[general]
|
||||||
|
name = 0.2mm Nozzle
|
||||||
|
version = 4
|
||||||
|
definition = biqu_bx_abl
|
||||||
|
|
||||||
|
[metadata]
|
||||||
|
setting_version = 17
|
||||||
|
type = variant
|
||||||
|
hardware_type = nozzle
|
||||||
|
|
||||||
|
[values]
|
||||||
|
machine_nozzle_size = 0.2
|
12
resources/variants/biqu_bx_abl_0.3.inst.cfg
Executable file
12
resources/variants/biqu_bx_abl_0.3.inst.cfg
Executable file
@ -0,0 +1,12 @@
|
|||||||
|
[general]
|
||||||
|
name = 0.3mm Nozzle
|
||||||
|
version = 4
|
||||||
|
definition = biqu_bx_abl
|
||||||
|
|
||||||
|
[metadata]
|
||||||
|
setting_version = 17
|
||||||
|
type = variant
|
||||||
|
hardware_type = nozzle
|
||||||
|
|
||||||
|
[values]
|
||||||
|
machine_nozzle_size = 0.3
|
12
resources/variants/biqu_bx_abl_0.4.inst.cfg
Executable file
12
resources/variants/biqu_bx_abl_0.4.inst.cfg
Executable file
@ -0,0 +1,12 @@
|
|||||||
|
[general]
|
||||||
|
name = 0.4mm Nozzle
|
||||||
|
version = 4
|
||||||
|
definition = biqu_bx_abl
|
||||||
|
|
||||||
|
[metadata]
|
||||||
|
setting_version = 17
|
||||||
|
type = variant
|
||||||
|
hardware_type = nozzle
|
||||||
|
|
||||||
|
[values]
|
||||||
|
machine_nozzle_size = 0.4
|
12
resources/variants/biqu_bx_abl_0.5.inst.cfg
Executable file
12
resources/variants/biqu_bx_abl_0.5.inst.cfg
Executable file
@ -0,0 +1,12 @@
|
|||||||
|
[general]
|
||||||
|
name = 0.5mm Nozzle
|
||||||
|
version = 4
|
||||||
|
definition = biqu_bx_abl
|
||||||
|
|
||||||
|
[metadata]
|
||||||
|
setting_version = 17
|
||||||
|
type = variant
|
||||||
|
hardware_type = nozzle
|
||||||
|
|
||||||
|
[values]
|
||||||
|
machine_nozzle_size = 0.5
|
12
resources/variants/biqu_bx_abl_0.6.inst.cfg
Executable file
12
resources/variants/biqu_bx_abl_0.6.inst.cfg
Executable file
@ -0,0 +1,12 @@
|
|||||||
|
[general]
|
||||||
|
name = 0.6mm Nozzle
|
||||||
|
version = 4
|
||||||
|
definition = biqu_bx_abl
|
||||||
|
|
||||||
|
[metadata]
|
||||||
|
setting_version = 17
|
||||||
|
type = variant
|
||||||
|
hardware_type = nozzle
|
||||||
|
|
||||||
|
[values]
|
||||||
|
machine_nozzle_size = 0.6
|
12
resources/variants/biqu_bx_abl_0.8.inst.cfg
Executable file
12
resources/variants/biqu_bx_abl_0.8.inst.cfg
Executable file
@ -0,0 +1,12 @@
|
|||||||
|
[general]
|
||||||
|
name = 0.8mm Nozzle
|
||||||
|
version = 4
|
||||||
|
definition = biqu_bx_abl
|
||||||
|
|
||||||
|
[metadata]
|
||||||
|
setting_version = 17
|
||||||
|
type = variant
|
||||||
|
hardware_type = nozzle
|
||||||
|
|
||||||
|
[values]
|
||||||
|
machine_nozzle_size = 0.8
|
Loading…
x
Reference in New Issue
Block a user