mirror of
https://git.mirrors.martin98.com/https://github.com/Ultimaker/Cura
synced 2025-08-12 17:28:59 +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)
|
||||
* 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`
|
||||
* `$USER/Library/Application Support/cura/<Cura version>/cura.log` (OSX)
|
||||
* `$USER/.local/share/cura/<Cura version>/cura.log` (Ubuntu/Linux)
|
||||
* `%APPDATA%\cura\<Cura version>\cura.log` (Windows), or usually `C:\Users\<your username>\AppData\Roaming\cura\<Cura version>\cura.log`
|
||||
* `$HOME/Library/Application Support/cura/<Cura version>/cura.log` (OSX)
|
||||
* `$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
|
||||
|
||||
|
@ -109,7 +109,6 @@ class Account(QObject):
|
||||
self._authorization_service.accessTokenChanged.connect(self._onAccessTokenChanged)
|
||||
self._authorization_service.loadAuthDataFromPreferences()
|
||||
|
||||
|
||||
@pyqtProperty(int, notify=syncStateChanged)
|
||||
def syncState(self):
|
||||
return self._sync_state
|
||||
@ -178,6 +177,7 @@ class Account(QObject):
|
||||
if error_message:
|
||||
if self._error_message:
|
||||
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.show()
|
||||
self._logged_in = False
|
||||
|
@ -2,9 +2,10 @@
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
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
|
||||
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.Logger import Logger
|
||||
@ -20,11 +21,6 @@ if TYPE_CHECKING:
|
||||
catalog = i18nCatalog("cura")
|
||||
|
||||
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)
|
||||
"""Triggered when a favorite is added or removed.
|
||||
|
||||
@ -264,3 +260,40 @@ class MaterialManagementModel(QObject):
|
||||
self.favoritesChanged.emit(material_base_file)
|
||||
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))
|
||||
|
||||
@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 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.
|
||||
Logger.log("d", "Auth web server was already running. Updating the verification code")
|
||||
self._web_server.setVerificationCode(verification_code)
|
||||
return
|
||||
|
||||
@ -85,6 +86,7 @@ class LocalAuthorizationServer:
|
||||
except OSError:
|
||||
# OS error can happen if the socket was already closed. We really don't care about that case.
|
||||
pass
|
||||
Logger.log("d", "Local oauth2 web server was shut down")
|
||||
self._web_server = None
|
||||
self._web_server_thread = None
|
||||
|
||||
@ -96,12 +98,13 @@ class LocalAuthorizationServer:
|
||||
|
||||
:return: None
|
||||
"""
|
||||
Logger.log("d", "Local web server for authorization has started")
|
||||
if self._web_server:
|
||||
if sys.platform == "win32":
|
||||
try:
|
||||
self._web_server.serve_forever()
|
||||
except OSError as e:
|
||||
Logger.warning(str(e))
|
||||
except OSError:
|
||||
Logger.logException("w", "An exception happened while serving the auth server")
|
||||
else:
|
||||
# Leave the default behavior in non-windows platforms
|
||||
self._web_server.serve_forever()
|
||||
|
@ -86,6 +86,14 @@ class GlobalStack(CuraContainerStack):
|
||||
def supportsNetworkConnection(self):
|
||||
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
|
||||
def getLoadingPriority(cls) -> int:
|
||||
return 2
|
||||
|
@ -4,12 +4,12 @@
|
||||
import argparse #To run the engine in debug mode if the front-end is in debug mode.
|
||||
from collections import defaultdict
|
||||
import os
|
||||
from PyQt5.QtCore import QObject, QTimer, pyqtSlot
|
||||
from PyQt5.QtCore import QObject, QTimer, QUrl, pyqtSlot
|
||||
import sys
|
||||
from time import time
|
||||
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.Scene.SceneNode import SceneNode
|
||||
@ -157,6 +157,18 @@ class CuraEngineBackend(QObject, Backend):
|
||||
self.determineAutoSlicing()
|
||||
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]
|
||||
|
||||
application.initializationFinished.connect(self.initialize)
|
||||
@ -922,9 +934,22 @@ class CuraEngineBackend(QObject, Backend):
|
||||
|
||||
if not self._restart:
|
||||
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
|
||||
|
||||
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:
|
||||
"""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
|
||||
{
|
||||
regExp: /^[^\\\/\*\?\|\[\]]{0,96}$/
|
||||
regExp: /^[^\\\/\*\?\|\[\]]{0,99}$/
|
||||
}
|
||||
|
||||
text: PrintInformation.jobName
|
||||
@ -148,7 +148,7 @@ Popup
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.right: parent.right
|
||||
text: "Create"
|
||||
enabled: newProjectNameTextField.text != "" && !busy
|
||||
enabled: newProjectNameTextField.text.length >= 2 && !busy
|
||||
|
||||
onClicked:
|
||||
{
|
||||
|
@ -63,7 +63,7 @@ Item
|
||||
anchors.topMargin: UM.Theme.getSize("thin_margin").height
|
||||
validator: RegExpValidator
|
||||
{
|
||||
regExp: /^[^\\\/\*\?\|\[\]]{0,96}$/
|
||||
regExp: /^[\w\-\. ()]{0,255}$/
|
||||
}
|
||||
|
||||
text: PrintInformation.jobName
|
||||
@ -200,7 +200,7 @@ Item
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.right: parent.right
|
||||
text: "Save"
|
||||
enabled: (asProjectCheckbox.checked || asSlicedCheckbox.checked) && dfFilenameTextfield.text != ""
|
||||
enabled: (asProjectCheckbox.checked || asSlicedCheckbox.checked) && dfFilenameTextfield.text.length >= 1
|
||||
|
||||
onClicked:
|
||||
{
|
||||
|
@ -1,10 +1,12 @@
|
||||
// Copyright (C) 2021 Ultimaker B.V.
|
||||
// Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import QtQuick 2.10
|
||||
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 2.3
|
||||
import QtQuick.Controls.Styles 1.4
|
||||
import QtQuick.Layouts 1.1
|
||||
|
||||
import UM 1.2 as UM
|
||||
import Cura 1.6 as Cura
|
||||
@ -18,7 +20,7 @@ Item
|
||||
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
property alias createNewProjectButtonVisible: createNewProjectButton.visible
|
||||
property bool createNewProjectButtonVisible: true
|
||||
|
||||
anchors
|
||||
{
|
||||
@ -29,31 +31,58 @@ Item
|
||||
margins: UM.Theme.getSize("default_margin").width
|
||||
}
|
||||
|
||||
Label
|
||||
RowLayout
|
||||
{
|
||||
id: selectProjectLabel
|
||||
id: headerRow
|
||||
|
||||
text: "Select Project"
|
||||
font: UM.Theme.getFont("medium")
|
||||
color: UM.Theme.getColor("small_button_text")
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
visible: projectListContainer.visible
|
||||
}
|
||||
|
||||
Cura.SecondaryButton
|
||||
{
|
||||
id: createNewProjectButton
|
||||
|
||||
anchors.verticalCenter: selectProjectLabel.verticalCenter
|
||||
anchors.right: parent.right
|
||||
text: "New Library project"
|
||||
|
||||
onClicked:
|
||||
anchors
|
||||
{
|
||||
createNewProjectPopup.open()
|
||||
top: parent.top
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
}
|
||||
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
|
||||
{
|
||||
id: createNewProjectButton
|
||||
|
||||
text: "New Library project"
|
||||
visible: createNewProjectButtonVisible && manager.userAccountCanCreateNewLibraryProject && (manager.retrievingProjectsStatus == DF.RetrievalStatus.Success || manager.retrievingProjectsStatus == DF.RetrievalStatus.Failed)
|
||||
|
||||
onClicked:
|
||||
{
|
||||
createNewProjectPopup.open()
|
||||
}
|
||||
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")
|
||||
}
|
||||
busy: manager.creatingNewProjectStatus == DF.RetrievalStatus.InProgress
|
||||
}
|
||||
|
||||
Item
|
||||
@ -76,19 +105,18 @@ Item
|
||||
{
|
||||
id: digitalFactoryImage
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
source: "../images/digital_factory.svg"
|
||||
source: searchBar.text === "" ? "../images/digital_factory.svg" : "../images/projects_not_found.svg"
|
||||
fillMode: Image.PreserveAspectFit
|
||||
width: parent.width - 2 * UM.Theme.getSize("thick_margin").width
|
||||
sourceSize.width: width
|
||||
sourceSize.height: height
|
||||
}
|
||||
|
||||
Label
|
||||
{
|
||||
id: noLibraryProjectsLabel
|
||||
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")
|
||||
color: UM.Theme.getColor("text")
|
||||
}
|
||||
|
||||
Cura.TertiaryButton
|
||||
@ -97,6 +125,7 @@ Item
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
text: "Visit Digital 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
|
||||
anchors
|
||||
{
|
||||
top: selectProjectLabel.bottom
|
||||
top: headerRow.bottom
|
||||
topMargin: UM.Theme.getSize("default_margin").height
|
||||
bottom: parent.bottom
|
||||
left: parent.left
|
||||
|
@ -22,6 +22,7 @@ from .DFFileUploader import DFFileUploader
|
||||
from .DFLibraryFileUploadRequest import DFLibraryFileUploadRequest
|
||||
from .DFLibraryFileUploadResponse import DFLibraryFileUploadResponse
|
||||
from .DFPrintJobUploadRequest import DFPrintJobUploadRequest
|
||||
from .DigitalFactoryFeatureBudgetResponse import DigitalFactoryFeatureBudgetResponse
|
||||
from .DigitalFactoryFileResponse import DigitalFactoryFileResponse
|
||||
from .DigitalFactoryProjectResponse import DigitalFactoryProjectResponse
|
||||
from .PaginationLinks import PaginationLinks
|
||||
@ -54,9 +55,67 @@ class DigitalFactoryApiClient:
|
||||
self._http = HttpRequestManager.getInstance()
|
||||
self._on_error = on_error
|
||||
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]
|
||||
|
||||
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:
|
||||
"""
|
||||
Retrieves a digital factory project by its library project id.
|
||||
@ -73,7 +132,7 @@ class DigitalFactoryApiClient:
|
||||
error_callback = failed,
|
||||
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.
|
||||
|
||||
@ -81,13 +140,18 @@ class DigitalFactoryApiClient:
|
||||
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).
|
||||
|
||||
: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 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:
|
||||
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,
|
||||
scope = self._scope,
|
||||
@ -301,12 +365,10 @@ class DigitalFactoryApiClient:
|
||||
: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.
|
||||
"""
|
||||
|
||||
display_name = re.sub(r"[^a-zA-Z0-9- ./™®ö+']", " ", project_name)
|
||||
Logger.log("i", "Attempt to create new DF project '{}'.".format(display_name))
|
||||
Logger.log("i", "Attempt to create new DF project '{}'.".format(project_name))
|
||||
|
||||
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,
|
||||
scope = self._scope,
|
||||
data = data,
|
||||
|
@ -1,4 +1,6 @@
|
||||
# Copyright (c) 2021 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import json
|
||||
import math
|
||||
import os
|
||||
@ -8,7 +10,7 @@ from enum import IntEnum
|
||||
from pathlib import Path
|
||||
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.QtQml import qmlRegisterType, qmlRegisterUncreatableType
|
||||
|
||||
@ -89,6 +91,12 @@ class DigitalFactoryController(QObject):
|
||||
uploadFileError = 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:
|
||||
super().__init__(parent = None)
|
||||
|
||||
@ -106,12 +114,18 @@ class DigitalFactoryController(QObject):
|
||||
self._has_more_projects_to_load = False
|
||||
|
||||
self._account = self._application.getInstance().getCuraAPI().account # type: Account
|
||||
self._account.loginStateChanged.connect(self._onLoginStateChanged)
|
||||
self._current_workspace_information = CuraApplication.getInstance().getCurrentWorkspaceInformation()
|
||||
|
||||
# Initialize the project model
|
||||
self._project_model = DigitalFactoryProjectModel()
|
||||
self._selected_project_idx = -1
|
||||
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
|
||||
self._file_model = DigitalFactoryFileModel()
|
||||
@ -131,6 +145,9 @@ class DigitalFactoryController(QObject):
|
||||
self._application.engineCreatedSignal.connect(self._onEngineCreated)
|
||||
self._application.initializationFinished.connect(self._applicationInitializationFinished)
|
||||
|
||||
self._user_has_access = False
|
||||
self._user_account_can_create_new_project = False
|
||||
|
||||
def clear(self) -> None:
|
||||
self._project_model.clearProjects()
|
||||
self._api.clear()
|
||||
@ -143,16 +160,22 @@ class DigitalFactoryController(QObject):
|
||||
|
||||
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:
|
||||
"""
|
||||
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
|
||||
"""
|
||||
subscriptions = [] # type: List[Dict[str, Any]]
|
||||
if self._account.userProfile:
|
||||
subscriptions = self._account.userProfile.get("subscriptions", [])
|
||||
return len(subscriptions) > 0
|
||||
if self._user_has_access:
|
||||
self._api.checkUserCanCreateNewLibraryProject(callback = self.setCanCreateNewLibraryProject)
|
||||
return self._user_has_access
|
||||
|
||||
def initialize(self, preselected_project_id: Optional[str] = None) -> None:
|
||||
self.clear()
|
||||
@ -162,7 +185,7 @@ class DigitalFactoryController(QObject):
|
||||
if preselected_project_id:
|
||||
self._api.getProject(preselected_project_id, on_finished = self.setProjectAsPreselected, failed = self._onGetProjectFailed)
|
||||
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:
|
||||
"""
|
||||
@ -288,6 +311,38 @@ class DigitalFactoryController(QObject):
|
||||
self._selected_file_indices = 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)
|
||||
def digitalFactoryProjectModel(self) -> "DigitalFactoryProjectModel":
|
||||
return self._project_model
|
||||
@ -502,7 +557,8 @@ class DigitalFactoryController(QObject):
|
||||
# false, we also need to clean it from the projects model
|
||||
self._project_model.clearProjects()
|
||||
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._has_preselected_project = new_has_preselected_project
|
||||
self.preselectedProjectChanged.emit()
|
||||
@ -511,6 +567,14 @@ class DigitalFactoryController(QObject):
|
||||
def hasPreselectedProject(self) -> bool:
|
||||
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")
|
||||
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._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.priority = 10
|
||||
|
||||
@ -53,7 +53,7 @@ class DigitalFactoryFileProvider(FileProvider):
|
||||
if not self._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
|
||||
:param logged_in: The new login status
|
||||
|
@ -45,7 +45,7 @@ class DigitalFactoryOutputDevice(ProjectOutputDevice):
|
||||
self._writing = False
|
||||
|
||||
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._current_workspace_information = CuraApplication.getInstance().getCurrentWorkspaceInformation()
|
||||
@ -97,7 +97,7 @@ class DigitalFactoryOutputDevice(ProjectOutputDevice):
|
||||
if not self._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
|
||||
: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
|
||||
|
||||
import pytest
|
||||
@ -37,7 +40,7 @@ def test_getProjectsFirstPage(api_client):
|
||||
failed_callback = MagicMock()
|
||||
|
||||
# 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
|
||||
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]
|
||||
|
||||
# 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
|
||||
http_manager.get.reset_mock()
|
||||
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]
|
||||
|
||||
# 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):
|
||||
|
@ -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.
|
||||
|
||||
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.
|
||||
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 settings.getInstance(property_key) is None:
|
||||
definition = stack.getSettingDefinition(property_key)
|
||||
new_instance = SettingInstance(definition, settings)
|
||||
# We just want the wall_line count to be there in case it was overriden in the global stack.
|
||||
# as such, we don't need to set a value.
|
||||
if property_key != "wall_line_count":
|
||||
new_instance.setProperty("value", 0)
|
||||
new_instance.setProperty("value", specialized_settings[property_key])
|
||||
new_instance.resetState() # Ensure that the state is not seen as a user state.
|
||||
settings.addInstance(new_instance)
|
||||
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_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.
|
||||
|
||||
import QtQuick 2.7
|
||||
import QtQuick 2.9
|
||||
import QtQuick.Layouts 1.1
|
||||
import QtQuick.Controls 2.3
|
||||
|
||||
@ -13,6 +13,8 @@ Item
|
||||
{
|
||||
id: prepareMenu
|
||||
|
||||
property var fileProviderModel: CuraApplication.getFileProviderModel()
|
||||
|
||||
UM.I18nCatalog
|
||||
{
|
||||
id: catalog
|
||||
@ -36,9 +38,9 @@ Item
|
||||
{
|
||||
id: itemRow
|
||||
|
||||
anchors.left: openFileButton.right
|
||||
anchors.left: parent.left
|
||||
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)
|
||||
|
||||
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
|
||||
{
|
||||
id: openFileButton
|
||||
height: UM.Theme.getSize("stage_menu").height
|
||||
width: UM.Theme.getSize("stage_menu").height
|
||||
visible: prepareMenu.fileProviderModel.count <= 1
|
||||
|
||||
height: parent.height
|
||||
width: visible ? height : 0 //Square button (and don't take up space if invisible).
|
||||
onClicked: Cura.Actions.open.trigger()
|
||||
enabled: visible && prepareMenu.fileProviderModel.count > 0
|
||||
hoverEnabled: true
|
||||
|
||||
contentItem: Item
|
||||
{
|
||||
anchors.fill: parent
|
||||
UM.RecolorImage
|
||||
{
|
||||
id: buttonIcon
|
||||
anchors.centerIn: parent
|
||||
source: UM.Theme.getIcon("Folder", "medium")
|
||||
anchors.centerIn: parent
|
||||
width: UM.Theme.getSize("button_icon").width
|
||||
height: UM.Theme.getSize("button_icon").height
|
||||
color: UM.Theme.getColor("icon")
|
||||
@ -101,8 +197,8 @@ Item
|
||||
background: Rectangle
|
||||
{
|
||||
id: background
|
||||
height: UM.Theme.getSize("stage_menu").height
|
||||
width: UM.Theme.getSize("stage_menu").height
|
||||
height: parent.height
|
||||
width: parent.width
|
||||
border.color: UM.Theme.getColor("lining")
|
||||
border.width: UM.Theme.getSize("default_lining").width
|
||||
|
||||
|
@ -203,16 +203,16 @@ Cura.ExpandableComponent
|
||||
|
||||
style: UM.Theme.styles.checkbox
|
||||
|
||||
|
||||
UM.RecolorImage
|
||||
Rectangle
|
||||
{
|
||||
id: swatch
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.right: extrudersModelCheckBox.right
|
||||
width: UM.Theme.getSize("layerview_legend_size").width
|
||||
height: UM.Theme.getSize("layerview_legend_size").height
|
||||
source: UM.Theme.getIcon("Extruder", "medium")
|
||||
color: model.color
|
||||
border.width: UM.Theme.getSize("default_lining").width
|
||||
border.color: UM.Theme.getColor("lining")
|
||||
}
|
||||
|
||||
Label
|
||||
|
@ -256,7 +256,16 @@ class CloudOutputDevice(UltimakerNetworkedPrinterOutputDevice):
|
||||
"""
|
||||
self._uploaded_print_job = self._pre_upload_print_job
|
||||
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()
|
||||
|
||||
def _onPrintUploadSpecificError(self, reply: "QNetworkReply", _: "QNetworkReply.NetworkError"):
|
||||
|
@ -13,6 +13,5 @@ class PrintJobUploadSuccessMessage(Message):
|
||||
def __init__(self) -> None:
|
||||
super().__init__(
|
||||
text = I18N_CATALOG.i18nc("@info:status", "Print job was successfully sent to the printer."),
|
||||
title = I18N_CATALOG.i18nc("@info:title", "Data Sent"),
|
||||
lifetime = 5
|
||||
title = I18N_CATALOG.i18nc("@info:title", "Data Sent")
|
||||
)
|
||||
|
@ -115,6 +115,7 @@
|
||||
"skirt_gap": { "value": 5.0 },
|
||||
"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_pattern": { "value": "'zigzag'" },
|
||||
|
@ -5,6 +5,7 @@
|
||||
"metadata": {
|
||||
"visible": true,
|
||||
"platform": "kossel_pro_build_platform.3mf",
|
||||
"platform_offset": [0, -0.25, 0],
|
||||
"machine_extruder_trains": {
|
||||
"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 },
|
||||
"travel_compensate_overlapping_walls_0_enabled": { "value": "False" },
|
||||
|
||||
"z_seam_type": { "value": "'back'" },
|
||||
"z_seam_corner": { "value": "'z_seam_corner_weighted'" },
|
||||
"z_seam_type": { "value": "'sharpest_corner'" },
|
||||
"z_seam_corner": { "value": "'z_seam_corner_inner'" },
|
||||
|
||||
"infill_line_width": { "value": "line_width * 1.2" },
|
||||
"infill_sparse_density": { "value": "20" },
|
||||
@ -156,7 +156,7 @@
|
||||
|
||||
"support_interface_enable": { "value": true },
|
||||
"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_skip_height": { "value": 0.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",
|
||||
"metadata": {
|
||||
"author": "William & Cataldo URSO",
|
||||
"manufacturer": "Shenzhen Geeetech Technology",
|
||||
"manufacturer": "Geeetech",
|
||||
"file_formats": "text/x-gcode",
|
||||
"visible": true,
|
||||
"has_materials": true,
|
||||
|
@ -3,7 +3,7 @@
|
||||
"name": "MP Mini Delta",
|
||||
"inherits": "fdmprinter",
|
||||
"metadata": {
|
||||
"author": "MPMD Facebook Group",
|
||||
"author": "MPMD V1 Facebook Group",
|
||||
"manufacturer": "Monoprice",
|
||||
"file_formats": "text/x-gcode",
|
||||
"platform": "mp_mini_delta_platform.3mf",
|
||||
@ -25,7 +25,7 @@
|
||||
"overrides": {
|
||||
"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":
|
||||
{
|
||||
@ -47,9 +47,9 @@
|
||||
"default_value": 0.21
|
||||
},
|
||||
"material_bed_temperature": { "value": 40 },
|
||||
"line_width": { "value": "round(machine_nozzle_size * 0.875, 2)" },
|
||||
"material_print_temperature_layer_0": { "value": "material_print_temperature + 5" },
|
||||
"material_bed_temperature_layer_0": { "value": "material_bed_temperature + 5" },
|
||||
"line_width": { "value": "round(machine_nozzle_size, 2)" },
|
||||
"material_print_temperature_layer_0": { "value": "material_print_temperature" },
|
||||
"material_bed_temperature_layer_0": { "value": "material_bed_temperature" },
|
||||
"machine_gcode_flavor": { "default_value": "RepRap (Marlin/Sprinter)" },
|
||||
"machine_max_feedrate_x": { "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"
|
||||
},
|
||||
"supports_usb_connection": false,
|
||||
"supports_network_connection": true
|
||||
"supports_network_connection": true,
|
||||
"supports_material_export": true
|
||||
},
|
||||
|
||||
"overrides": {
|
||||
|
@ -27,6 +27,7 @@
|
||||
"first_start_actions": [ "DiscoverUM3Action" ],
|
||||
"supported_actions": [ "DiscoverUM3Action" ],
|
||||
"supports_usb_connection": false,
|
||||
"supports_material_export": true,
|
||||
"weight": -1,
|
||||
"firmware_update_info": {
|
||||
"id": 213482,
|
||||
|
@ -28,6 +28,7 @@
|
||||
"supported_actions": [ "DiscoverUM3Action" ],
|
||||
"supports_usb_connection": false,
|
||||
"supports_network_connection": true,
|
||||
"supports_material_export": true,
|
||||
"weight": -2,
|
||||
"firmware_update_info": {
|
||||
"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
|
||||
msgctxt "z_seam_position option back"
|
||||
msgid "Back"
|
||||
msgstr "Zurück"
|
||||
msgstr "Hinten"
|
||||
|
||||
#: fdmprinter.def.json
|
||||
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 cornerRadius: backgroundRect.radius
|
||||
property alias tooltip: tooltip.tooltipText
|
||||
property alias tooltipWidth: tooltip.width
|
||||
|
||||
property color color: UM.Theme.getColor("primary")
|
||||
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.
|
||||
|
||||
pragma Singleton
|
||||
@ -122,7 +122,15 @@ Item
|
||||
Action
|
||||
{
|
||||
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"
|
||||
shortcut: StandardKey.Quit
|
||||
}
|
||||
@ -172,7 +180,14 @@ Item
|
||||
Action
|
||||
{
|
||||
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"
|
||||
}
|
||||
|
||||
@ -263,7 +278,15 @@ Item
|
||||
Action
|
||||
{
|
||||
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";
|
||||
}
|
||||
|
||||
|
@ -417,6 +417,7 @@ UM.MainWindow
|
||||
Cura.PrimaryButton
|
||||
{
|
||||
text: model.name
|
||||
iconSource: UM.Theme.getIcon(model.icon)
|
||||
height: UM.Theme.getSize("message_action_button").height
|
||||
}
|
||||
}
|
||||
@ -426,6 +427,7 @@ UM.MainWindow
|
||||
Cura.SecondaryButton
|
||||
{
|
||||
text: model.name
|
||||
iconSource: UM.Theme.getIcon(model.icon)
|
||||
height: UM.Theme.getSize("message_action_button").height
|
||||
}
|
||||
}
|
||||
@ -434,6 +436,14 @@ UM.MainWindow
|
||||
Cura.TertiaryButton
|
||||
{
|
||||
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
|
||||
}
|
||||
}
|
||||
|
@ -167,7 +167,7 @@ Item
|
||||
verticalCenter: parent.verticalCenter
|
||||
margins: background.padding
|
||||
}
|
||||
source: expanded ? UM.Theme.getIcon("ChevronSingleDown") : UM.Theme.getIcon("ChevronSingleLeft")
|
||||
source: UM.Theme.getIcon("ChevronSingleDown")
|
||||
visible: source != ""
|
||||
width: UM.Theme.getSize("standard_arrow").width
|
||||
height: UM.Theme.getSize("standard_arrow").height
|
||||
|
@ -180,7 +180,7 @@ Item
|
||||
verticalCenter: parent.verticalCenter
|
||||
margins: background.padding
|
||||
}
|
||||
source: expanded ? UM.Theme.getIcon("ChevronSingleDown") : UM.Theme.getIcon("ChevronSingleLeft")
|
||||
source: UM.Theme.getIcon("ChevronSingleDown")
|
||||
visible: source != ""
|
||||
width: UM.Theme.getSize("standard_arrow").width
|
||||
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.
|
||||
|
||||
import QtQuick 2.7
|
||||
@ -48,7 +48,17 @@ Item
|
||||
|
||||
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
|
||||
{
|
||||
@ -91,7 +101,15 @@ Item
|
||||
Menu
|
||||
{
|
||||
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 }
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Copyright (c) 2018 Ultimaker B.V.
|
||||
// Uranium is released under the terms of the LGPLv3 or higher.
|
||||
// Copyright (c) 2021 Ultimaker B.V.
|
||||
// Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import QtQuick 2.7
|
||||
import QtQuick.Controls 1.4
|
||||
@ -191,6 +191,21 @@ Item
|
||||
}
|
||||
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 {
|
||||
@ -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
|
||||
{
|
||||
id: messageDialog
|
||||
|
@ -95,7 +95,7 @@ UM.PreferencesPage
|
||||
|
||||
placeholderText: catalog.i18nc("@label:textbox", "Filter...")
|
||||
|
||||
onTextChanged: definitionsModel.filter = {"i18n_label": "*" + text}
|
||||
onTextChanged: definitionsModel.filter = {"i18n_label|i18n_description": "*" + text}
|
||||
}
|
||||
|
||||
NewControls.ComboBox
|
||||
|
@ -54,7 +54,7 @@ Item
|
||||
{
|
||||
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:
|
||||
{
|
||||
|
@ -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.
|
||||
|
||||
import QtQuick 2.10
|
||||
@ -214,16 +214,16 @@ Item
|
||||
id: troubleshootingButton
|
||||
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: UM.Theme.getSize("default_margin").width
|
||||
anchors.rightMargin: UM.Theme.getSize("thin_margin").width
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
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
|
||||
{
|
||||
id: troubleshootingLinkIcon
|
||||
anchors.right: troubleshootingLabel.left
|
||||
anchors.rightMargin: UM.Theme.getSize("default_margin").width
|
||||
anchors.rightMargin: UM.Theme.getSize("thin_margin").width
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
height: troubleshootingLabel.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.
|
||||
|
||||
import QtQuick 2.10
|
||||
@ -15,6 +15,8 @@ TextField
|
||||
{
|
||||
id: textField
|
||||
|
||||
property alias leftIcon: iconLeft.source
|
||||
|
||||
UM.I18nCatalog { id: catalog; name: "cura" }
|
||||
|
||||
hoverEnabled: true
|
||||
@ -22,6 +24,7 @@ TextField
|
||||
font: UM.Theme.getFont("default")
|
||||
color: UM.Theme.getColor("text")
|
||||
renderType: Text.NativeRendering
|
||||
leftPadding: iconLeft.visible ? iconLeft.width + UM.Theme.getSize("default_margin").width * 2 : UM.Theme.getSize("thin_margin").width
|
||||
|
||||
states: [
|
||||
State
|
||||
@ -52,7 +55,6 @@ TextField
|
||||
|
||||
color: UM.Theme.getColor("main_background")
|
||||
|
||||
anchors.margins: Math.round(UM.Theme.getSize("default_lining").width)
|
||||
radius: UM.Theme.getSize("setting_control_radius").width
|
||||
|
||||
border.color:
|
||||
@ -67,5 +69,23 @@ TextField
|
||||
}
|
||||
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],
|
||||
"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_text": [255, 255, 255, 255],
|
||||
|
||||
|
@ -6,8 +6,8 @@
|
||||
.st0{fill:#231F20;}
|
||||
</style>
|
||||
<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
|
||||
s8-3.6,8-8S19.4,7,15,7z"/>
|
||||
<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
|
||||
s7-3.1,7-7S18.9,8,15,8z"/>
|
||||
</g>
|
||||
<g id="Comments">
|
||||
</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_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_hover": [232, 240, 253, 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