mirror of
https://git.mirrors.martin98.com/https://github.com/Ultimaker/Cura
synced 2025-08-12 18:49:02 +08:00
Merge remote-tracking branch 'upstream/master' into dagoma_discoultimate
This commit is contained in:
commit
173a05ddb5
@ -56,6 +56,13 @@ function(cura_add_test)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
#Add test for import statements which are not compatible with all builds
|
||||
add_test(
|
||||
NAME "invalid-imports"
|
||||
COMMAND ${Python3_EXECUTABLE} scripts/check_invalid_imports.py
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||
)
|
||||
|
||||
cura_add_test(NAME pytest-main DIRECTORY ${CMAKE_SOURCE_DIR}/tests PYTHONPATH "${CMAKE_SOURCE_DIR}|${URANIUM_DIR}")
|
||||
|
||||
file(GLOB_RECURSE _plugins plugins/*/__init__.py)
|
||||
|
@ -28,9 +28,10 @@ class CuraAPI(QObject):
|
||||
# The main reason for this is that we want to prevent consumers of API to have a dependency on CuraApplication.
|
||||
# Since the API is intended to be used by plugins, the cura application should have already created this.
|
||||
def __new__(cls, application: Optional["CuraApplication"] = None):
|
||||
if cls.__instance is None:
|
||||
if cls.__instance is not None:
|
||||
raise RuntimeError("Tried to create singleton '{class_name}' more than once.".format(class_name = CuraAPI.__name__))
|
||||
if application is None:
|
||||
raise Exception("Upon first time creation, the application must be set.")
|
||||
raise RuntimeError("Upon first time creation, the application must be set.")
|
||||
cls.__instance = super(CuraAPI, cls).__new__(cls)
|
||||
cls._application = application
|
||||
return cls.__instance
|
||||
|
@ -145,6 +145,14 @@ class Backup:
|
||||
# \return Whether we had success or not.
|
||||
@staticmethod
|
||||
def _extractArchive(archive: "ZipFile", target_path: str) -> bool:
|
||||
|
||||
# Implement security recommendations: Sanity check on zip files will make it harder to spoof.
|
||||
from cura.CuraApplication import CuraApplication
|
||||
config_filename = CuraApplication.getInstance().getApplicationName() + ".cfg" # Should be there if valid.
|
||||
if config_filename not in [file.filename for file in archive.filelist]:
|
||||
Logger.logException("e", "Unable to extract the backup due to corruption of compressed file(s).")
|
||||
return False
|
||||
|
||||
Logger.log("d", "Removing current data in location: %s", target_path)
|
||||
Resources.factoryReset()
|
||||
Logger.log("d", "Extracting backup to location: %s", target_path)
|
||||
|
@ -1,4 +1,4 @@
|
||||
# Copyright (c) 2019 Ultimaker B.V.
|
||||
# Copyright (c) 2020 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import os
|
||||
@ -191,8 +191,6 @@ class CuraApplication(QtApplication):
|
||||
|
||||
self._cura_formula_functions = None # type: Optional[CuraFormulaFunctions]
|
||||
|
||||
self._cura_package_manager = None
|
||||
|
||||
self._machine_action_manager = None # type: Optional[MachineActionManager.MachineActionManager]
|
||||
|
||||
self.empty_container = None # type: EmptyInstanceContainer
|
||||
@ -632,6 +630,12 @@ class CuraApplication(QtApplication):
|
||||
def showPreferences(self) -> None:
|
||||
self.showPreferencesWindow.emit()
|
||||
|
||||
# This is called by drag-and-dropping curapackage files.
|
||||
@pyqtSlot(QUrl)
|
||||
def installPackageViaDragAndDrop(self, file_url: str) -> Optional[str]:
|
||||
filename = QUrl(file_url).toLocalFile()
|
||||
return self._package_manager.installPackage(filename)
|
||||
|
||||
@override(Application)
|
||||
def getGlobalContainerStack(self) -> Optional["GlobalStack"]:
|
||||
return self._global_container_stack
|
||||
@ -1827,11 +1831,17 @@ class CuraApplication(QtApplication):
|
||||
|
||||
def _onContextMenuRequested(self, x: float, y: float) -> None:
|
||||
# Ensure we select the object if we request a context menu over an object without having a selection.
|
||||
if not Selection.hasSelection():
|
||||
node = self.getController().getScene().findObject(cast(SelectionPass, self.getRenderer().getRenderPass("selection")).getIdAtPosition(x, y))
|
||||
if node:
|
||||
if Selection.hasSelection():
|
||||
return
|
||||
selection_pass = cast(SelectionPass, self.getRenderer().getRenderPass("selection"))
|
||||
if not selection_pass: # If you right-click before the rendering has been initialised there might not be a selection pass yet.
|
||||
print("--------------ding! Got the crash.")
|
||||
return
|
||||
node = self.getController().getScene().findObject(selection_pass.getIdAtPosition(x, y))
|
||||
if not node:
|
||||
return
|
||||
parent = node.getParent()
|
||||
while(parent and parent.callDecoration("isGroup")):
|
||||
while parent and parent.callDecoration("isGroup"):
|
||||
node = parent
|
||||
parent = node.getParent()
|
||||
|
||||
|
@ -115,9 +115,10 @@ class AuthorizationHelpers:
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
## Generate a 16-character verification code.
|
||||
# \param code_length: How long should the code be?
|
||||
def generateVerificationCode(code_length: int = 16) -> str:
|
||||
## Generate a verification code of arbitrary length.
|
||||
# \param code_length: How long should the code be? This should never be lower than 16, but it's probably better to
|
||||
# leave it at 32
|
||||
def generateVerificationCode(code_length: int = 32) -> str:
|
||||
return "".join(random.choice("0123456789ABCDEF") for i in range(code_length))
|
||||
|
||||
@staticmethod
|
||||
|
@ -25,6 +25,8 @@ class AuthorizationRequestHandler(BaseHTTPRequestHandler):
|
||||
self.authorization_callback = None # type: Optional[Callable[[AuthenticationResponse], None]]
|
||||
self.verification_code = None # type: Optional[str]
|
||||
|
||||
self.state = None # type: Optional[str]
|
||||
|
||||
# CURA-6609: Some browser seems to issue a HEAD instead of GET request as the callback.
|
||||
def do_HEAD(self) -> None:
|
||||
self.do_GET()
|
||||
@ -58,7 +60,14 @@ class AuthorizationRequestHandler(BaseHTTPRequestHandler):
|
||||
# \return HTTP ResponseData containing a success page to show to the user.
|
||||
def _handleCallback(self, query: Dict[Any, List]) -> Tuple[ResponseData, Optional[AuthenticationResponse]]:
|
||||
code = self._queryGet(query, "code")
|
||||
if code and self.authorization_helpers is not None and self.verification_code is not None:
|
||||
state = self._queryGet(query, "state")
|
||||
if state != self.state:
|
||||
token_response = AuthenticationResponse(
|
||||
success = False,
|
||||
err_message=catalog.i18nc("@message",
|
||||
"The provided state is not correct.")
|
||||
)
|
||||
elif code and self.authorization_helpers is not None and self.verification_code is not None:
|
||||
# If the code was returned we get the access token.
|
||||
token_response = self.authorization_helpers.getAccessTokenUsingAuthorizationCode(
|
||||
code, self.verification_code)
|
||||
|
@ -25,3 +25,6 @@ class AuthorizationRequestServer(HTTPServer):
|
||||
## Set the verification code on the request handler.
|
||||
def setVerificationCode(self, verification_code: str) -> None:
|
||||
self.RequestHandlerClass.verification_code = verification_code # type: ignore
|
||||
|
||||
def setState(self, state: str) -> None:
|
||||
self.RequestHandlerClass.state = state # type: ignore
|
||||
|
@ -153,13 +153,15 @@ class AuthorizationService:
|
||||
verification_code = self._auth_helpers.generateVerificationCode()
|
||||
challenge_code = self._auth_helpers.generateVerificationCodeChallenge(verification_code)
|
||||
|
||||
state = AuthorizationHelpers.generateVerificationCode()
|
||||
|
||||
# Create the query string needed for the OAuth2 flow.
|
||||
query_string = urlencode({
|
||||
"client_id": self._settings.CLIENT_ID,
|
||||
"redirect_uri": self._settings.CALLBACK_URL,
|
||||
"scope": self._settings.CLIENT_SCOPES,
|
||||
"response_type": "code",
|
||||
"state": "(.Y.)",
|
||||
"state": state, # Forever in our Hearts, RIP "(.Y.)" (2018-2020)
|
||||
"code_challenge": challenge_code,
|
||||
"code_challenge_method": "S512"
|
||||
})
|
||||
@ -168,7 +170,7 @@ class AuthorizationService:
|
||||
QDesktopServices.openUrl(QUrl("{}?{}".format(self._auth_url, query_string)))
|
||||
|
||||
# Start a local web server to receive the callback URL on.
|
||||
self._server.start(verification_code)
|
||||
self._server.start(verification_code, state)
|
||||
|
||||
## Callback method for the authentication flow.
|
||||
def _onAuthStateChanged(self, auth_response: AuthenticationResponse) -> None:
|
||||
|
@ -36,7 +36,8 @@ class LocalAuthorizationServer:
|
||||
|
||||
## Starts the local web server to handle the authorization callback.
|
||||
# \param verification_code The verification code part of the OAuth2 client identification.
|
||||
def start(self, verification_code: str) -> None:
|
||||
# \param state The unique state code (to ensure that the request we get back is really from the server.
|
||||
def start(self, verification_code: str, state: str) -> None:
|
||||
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.
|
||||
@ -53,6 +54,7 @@ class LocalAuthorizationServer:
|
||||
self._web_server.setAuthorizationHelpers(self._auth_helpers)
|
||||
self._web_server.setAuthorizationCallback(self._auth_state_changed_callback)
|
||||
self._web_server.setVerificationCode(verification_code)
|
||||
self._web_server.setState(state)
|
||||
|
||||
# Start the server on a new thread.
|
||||
self._web_server_thread = threading.Thread(None, self._web_server.serve_forever, daemon = self._daemon)
|
||||
|
@ -747,6 +747,11 @@ class MachineManager(QObject):
|
||||
result = [] # type: List[str]
|
||||
for setting_instance in container.findInstances():
|
||||
setting_key = setting_instance.definition.key
|
||||
if setting_key == "print_sequence":
|
||||
old_value = container.getProperty(setting_key, "value")
|
||||
Logger.log("d", "Reset setting [%s] in [%s] because its old value [%s] is no longer valid", setting_key, container, old_value)
|
||||
result.append(setting_key)
|
||||
continue
|
||||
if not self._global_container_stack.getProperty(setting_key, "type") in ("extruder", "optional_extruder"):
|
||||
continue
|
||||
|
||||
|
@ -29,9 +29,13 @@ parser.add_argument("--debug",
|
||||
known_args = vars(parser.parse_known_args()[0])
|
||||
|
||||
if with_sentry_sdk:
|
||||
sentry_env = "production"
|
||||
if ApplicationMetadata.CuraVersion == "master":
|
||||
sentry_env = "unknown" # Start off with a "IDK"
|
||||
if hasattr(sys, "frozen"):
|
||||
sentry_env = "production" # A frozen build is a "real" distribution.
|
||||
elif ApplicationMetadata.CuraVersion == "master":
|
||||
sentry_env = "development"
|
||||
elif "beta" in ApplicationMetadata.CuraVersion or "BETA" in ApplicationMetadata.CuraVersion:
|
||||
sentry_env = "beta"
|
||||
try:
|
||||
if ApplicationMetadata.CuraVersion.split(".")[2] == "99":
|
||||
sentry_env = "nightly"
|
||||
|
@ -13,6 +13,8 @@ export PKG_CONFIG_PATH="${CURA_BUILD_ENV_PATH}/lib/pkgconfig:${PKG_CONFIG_PATH}"
|
||||
|
||||
cd "${PROJECT_DIR}"
|
||||
|
||||
|
||||
|
||||
#
|
||||
# Clone Uranium and set PYTHONPATH first
|
||||
#
|
||||
|
@ -4,7 +4,8 @@
|
||||
from configparser import ConfigParser
|
||||
import zipfile
|
||||
import os
|
||||
from typing import cast, Dict, List, Optional, Tuple
|
||||
import json
|
||||
from typing import cast, Dict, List, Optional, Tuple, Any
|
||||
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
@ -732,7 +733,25 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
|
||||
|
||||
base_file_name = os.path.basename(file_name)
|
||||
self.setWorkspaceName(base_file_name)
|
||||
return nodes
|
||||
|
||||
return nodes, self._loadMetadata(file_name)
|
||||
|
||||
@staticmethod
|
||||
def _loadMetadata(file_name: str) -> Dict[str, Dict[str, Any]]:
|
||||
archive = zipfile.ZipFile(file_name, "r")
|
||||
|
||||
metadata_files = [name for name in archive.namelist() if name.endswith("plugin_metadata.json")]
|
||||
|
||||
result = dict()
|
||||
|
||||
for metadata_file in metadata_files:
|
||||
try:
|
||||
plugin_id = metadata_file.split("/")[0]
|
||||
result[plugin_id] = json.loads(archive.open("%s/plugin_metadata.json" % plugin_id).read().decode("utf-8"))
|
||||
except Exception:
|
||||
Logger.logException("w", "Unable to retrieve metadata for %s", metadata_file)
|
||||
|
||||
return result
|
||||
|
||||
def _processQualityChanges(self, global_stack):
|
||||
if self._machine_info.quality_changes_info is None:
|
||||
|
@ -73,11 +73,25 @@ class ThreeMFWorkspaceWriter(WorkspaceWriter):
|
||||
version_config_parser.write(version_file_string)
|
||||
archive.writestr(version_file, version_file_string.getvalue())
|
||||
|
||||
self._writePluginMetadataToArchive(archive)
|
||||
|
||||
# Close the archive & reset states.
|
||||
archive.close()
|
||||
mesh_writer.setStoreArchive(False)
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
def _writePluginMetadataToArchive(archive: zipfile.ZipFile) -> None:
|
||||
file_name_template = "%s/plugin_metadata.json"
|
||||
|
||||
for plugin_id, metadata in Application.getInstance().getWorkspaceMetadataStorage().getAllData().items():
|
||||
file_name = file_name_template % plugin_id
|
||||
file_in_archive = zipfile.ZipInfo(file_name)
|
||||
# We have to set the compress type of each file as well (it doesn't keep the type of the entire archive)
|
||||
file_in_archive.compress_type = zipfile.ZIP_DEFLATED
|
||||
import json
|
||||
archive.writestr(file_in_archive, json.dumps(metadata, separators = (", ", ": "), indent = 4, skipkeys = True))
|
||||
|
||||
## Helper function that writes ContainerStacks, InstanceContainers and DefinitionContainers to the archive.
|
||||
# \param container That follows the \type{ContainerInterface} to archive.
|
||||
# \param archive The archive to write to.
|
||||
|
@ -1,5 +1,6 @@
|
||||
# Copyright (c) 2015 Ultimaker B.V.
|
||||
# Uranium is released under the terms of the LGPLv3 or higher.
|
||||
from typing import Optional
|
||||
|
||||
from UM.Mesh.MeshWriter import MeshWriter
|
||||
from UM.Math.Vector import Vector
|
||||
@ -40,7 +41,7 @@ class ThreeMFWriter(MeshWriter):
|
||||
}
|
||||
|
||||
self._unit_matrix_string = self._convertMatrixToString(Matrix())
|
||||
self._archive = None
|
||||
self._archive = None # type: Optional[zipfile.ZipFile]
|
||||
self._store_archive = False
|
||||
|
||||
def _convertMatrixToString(self, matrix):
|
||||
|
@ -72,16 +72,23 @@ class DisplayFilenameAndLayerOnLCD(Script):
|
||||
lcd_text = "M117 Printing " + name + " - Layer "
|
||||
i = self.getSettingValueByKey("startNum")
|
||||
for layer in data:
|
||||
display_text = lcd_text + str(i) + " " + name
|
||||
display_text = lcd_text + str(i)
|
||||
layer_index = data.index(layer)
|
||||
lines = layer.split("\n")
|
||||
for line in lines:
|
||||
if line.startswith(";LAYER_COUNT:"):
|
||||
max_layer = line
|
||||
max_layer = max_layer.split(":")[1]
|
||||
if self.getSettingValueByKey("startNum") == 0:
|
||||
max_layer = str(int(max_layer) - 1)
|
||||
if line.startswith(";LAYER:"):
|
||||
if self.getSettingValueByKey("maxlayer"):
|
||||
display_text = display_text + " of " + max_layer
|
||||
if not self.getSettingValueByKey("scroll"):
|
||||
display_text = display_text + " " + name
|
||||
else:
|
||||
if not self.getSettingValueByKey("scroll"):
|
||||
display_text = display_text + " " + name + "!"
|
||||
else:
|
||||
display_text = display_text + "!"
|
||||
line_index = lines.index(line)
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
from ..Script import Script
|
||||
|
||||
|
||||
class TimeLapse(Script):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
@ -75,21 +76,29 @@ class TimeLapse(Script):
|
||||
trigger_command = self.getSettingValueByKey("trigger_command")
|
||||
pause_length = self.getSettingValueByKey("pause_length")
|
||||
gcode_to_append = ";TimeLapse Begin\n"
|
||||
last_x = 0
|
||||
last_y = 0
|
||||
|
||||
if park_print_head:
|
||||
gcode_to_append += self.putValue(G = 1, F = feed_rate, X = x_park, Y = y_park) + " ;Park print head\n"
|
||||
gcode_to_append += self.putValue(M = 400) + " ;Wait for moves to finish\n"
|
||||
gcode_to_append += self.putValue(G=1, F=feed_rate,
|
||||
X=x_park, Y=y_park) + " ;Park print head\n"
|
||||
gcode_to_append += self.putValue(M=400) + " ;Wait for moves to finish\n"
|
||||
gcode_to_append += trigger_command + " ;Snap Photo\n"
|
||||
gcode_to_append += self.putValue(G = 4, P = pause_length) + " ;Wait for camera\n"
|
||||
gcode_to_append += ";TimeLapse End\n"
|
||||
for layer in data:
|
||||
gcode_to_append += self.putValue(G=4, P=pause_length) + " ;Wait for camera\n"
|
||||
|
||||
for idx, layer in enumerate(data):
|
||||
for line in layer.split("\n"):
|
||||
if self.getValue(line, "G") in {0, 1}: # Track X,Y location.
|
||||
last_x = self.getValue(line, "X", last_x)
|
||||
last_y = self.getValue(line, "Y", last_y)
|
||||
# Check that a layer is being printed
|
||||
lines = layer.split("\n")
|
||||
for line in lines:
|
||||
if ";LAYER:" in line:
|
||||
index = data.index(layer)
|
||||
layer += gcode_to_append
|
||||
|
||||
data[index] = layer
|
||||
layer += "G0 X%s Y%s\n" % (last_x, last_y)
|
||||
|
||||
data[idx] = layer
|
||||
break
|
||||
return data
|
||||
|
@ -1,4 +1,4 @@
|
||||
# Copyright (c) 2018 Ultimaker B.V.
|
||||
# Copyright (c) 2020 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import sys
|
||||
@ -116,8 +116,9 @@ class SimulationView(CuraView):
|
||||
self._only_show_top_layers = bool(Application.getInstance().getPreferences().getValue("view/only_show_top_layers"))
|
||||
self._compatibility_mode = self._evaluateCompatibilityMode()
|
||||
|
||||
self._wireprint_warning_message = Message(catalog.i18nc("@info:status", "Cura does not accurately display layers when Wire Printing is enabled"),
|
||||
self._wireprint_warning_message = Message(catalog.i18nc("@info:status", "Cura does not accurately display layers when Wire Printing is enabled."),
|
||||
title = catalog.i18nc("@info:title", "Simulation View"))
|
||||
self._slice_first_warning_message = Message(catalog.i18nc("@info:status", "Nothing is shown because you need to slice first."), title = catalog.i18nc("@info:title", "No layer data"))
|
||||
|
||||
QtApplication.getInstance().engineCreatedSignal.connect(self._onEngineCreated)
|
||||
|
||||
@ -149,6 +150,7 @@ class SimulationView(CuraView):
|
||||
if self._activity == activity:
|
||||
return
|
||||
self._activity = activity
|
||||
self._updateSliceWarningVisibility()
|
||||
self.activityChanged.emit()
|
||||
|
||||
def getSimulationPass(self) -> SimulationPass:
|
||||
@ -543,11 +545,13 @@ class SimulationView(CuraView):
|
||||
self._composite_pass.getLayerBindings().append("simulationview")
|
||||
self._old_composite_shader = self._composite_pass.getCompositeShader()
|
||||
self._composite_pass.setCompositeShader(self._simulationview_composite_shader)
|
||||
self._updateSliceWarningVisibility()
|
||||
|
||||
elif event.type == Event.ViewDeactivateEvent:
|
||||
self._controller.getScene().getRoot().childrenChanged.disconnect(self._onSceneChanged)
|
||||
Application.getInstance().getPreferences().preferenceChanged.disconnect(self._onPreferencesChanged)
|
||||
self._wireprint_warning_message.hide()
|
||||
self._slice_first_warning_message.hide()
|
||||
Application.getInstance().globalContainerStackChanged.disconnect(self._onGlobalStackChanged)
|
||||
if self._global_container_stack:
|
||||
self._global_container_stack.propertyChanged.disconnect(self._onPropertyChanged)
|
||||
@ -661,6 +665,12 @@ class SimulationView(CuraView):
|
||||
|
||||
self._updateWithPreferences()
|
||||
|
||||
def _updateSliceWarningVisibility(self):
|
||||
if not self.getActivity():
|
||||
self._slice_first_warning_message.show()
|
||||
else:
|
||||
self._slice_first_warning_message.hide()
|
||||
|
||||
|
||||
class _CreateTopLayersJob(Job):
|
||||
def __init__(self, scene: "Scene", layer_number: int, solid_layers: int) -> None:
|
||||
|
@ -72,6 +72,7 @@ Window
|
||||
right: parent.right
|
||||
}
|
||||
text: catalog.i18nc("@text:window", "Ultimaker Cura collects anonymous data in order to improve the print quality and user experience. Below is an example of all the data that is shared:")
|
||||
color: UM.Theme.getColor("text")
|
||||
wrapMode: Text.WordWrap
|
||||
renderType: Text.NativeRendering
|
||||
}
|
||||
|
@ -20,6 +20,8 @@ UM.Dialog{
|
||||
maximumHeight: minimumHeight
|
||||
margin: 0
|
||||
|
||||
property string actionButtonText: subscribedPackagesModel.hasIncompatiblePackages && !subscribedPackagesModel.hasCompatiblePackages ? catalog.i18nc("@button", "Dismiss") : catalog.i18nc("@button", "Next")
|
||||
|
||||
Rectangle
|
||||
{
|
||||
id: root
|
||||
@ -90,7 +92,7 @@ UM.Dialog{
|
||||
Label
|
||||
{
|
||||
font: UM.Theme.getFont("default")
|
||||
text: catalog.i18nc("@label", "The following packages can not be installed because of incompatible Cura version:")
|
||||
text: catalog.i18nc("@label", "The following packages can not be installed because of an incompatible Cura version:")
|
||||
visible: subscribedPackagesModel.hasIncompatiblePackages
|
||||
color: UM.Theme.getColor("text")
|
||||
height: contentHeight + UM.Theme.getSize("default_margin").height
|
||||
@ -125,26 +127,6 @@ UM.Dialog{
|
||||
color: UM.Theme.getColor("text")
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
UM.TooltipArea
|
||||
{
|
||||
width: childrenRect.width;
|
||||
height: childrenRect.height;
|
||||
text: catalog.i18nc("@info:tooltip", "Dismisses the package and won't be shown in this dialog anymore")
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: packageIcon.verticalCenter
|
||||
Label
|
||||
{
|
||||
text: "(Dismiss)"
|
||||
font: UM.Theme.getFont("small")
|
||||
color: UM.Theme.getColor("text")
|
||||
MouseArea
|
||||
{
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
anchors.fill: parent
|
||||
onClicked: handler.dismissIncompatiblePackage(subscribedPackagesModel, model.package_id)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -152,14 +134,16 @@ UM.Dialog{
|
||||
|
||||
} // End of ScrollView
|
||||
|
||||
Cura.ActionButton
|
||||
Cura.PrimaryButton
|
||||
{
|
||||
id: nextButton
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.right: parent.right
|
||||
anchors.margins: UM.Theme.getSize("default_margin").height
|
||||
text: catalog.i18nc("@button", "Next")
|
||||
text: actionButtonText
|
||||
onClicked: accept()
|
||||
leftPadding: UM.Theme.getSize("dialog_primary_button_padding").width
|
||||
rightPadding: UM.Theme.getSize("dialog_primary_button_padding").width
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,12 +4,12 @@
|
||||
import QtQuick 2.10
|
||||
import QtQuick.Dialogs 1.1
|
||||
import QtQuick.Window 2.2
|
||||
import QtQuick.Controls 1.4
|
||||
import QtQuick.Controls 2.3
|
||||
import QtQuick.Layouts 1.3
|
||||
import QtQuick.Controls.Styles 1.4
|
||||
|
||||
// TODO: Switch to QtQuick.Controls 2.x and remove QtQuick.Controls.Styles
|
||||
|
||||
import UM 1.1 as UM
|
||||
import Cura 1.6 as Cura
|
||||
|
||||
UM.Dialog
|
||||
{
|
||||
@ -19,50 +19,90 @@ UM.Dialog
|
||||
minimumHeight: UM.Theme.getSize("license_window_minimum").height
|
||||
width: minimumWidth
|
||||
height: minimumHeight
|
||||
backgroundColor: UM.Theme.getColor("main_background")
|
||||
margin: screenScaleFactor * 10
|
||||
|
||||
Item
|
||||
ColumnLayout
|
||||
{
|
||||
anchors.fill: parent
|
||||
spacing: UM.Theme.getSize("thick_margin").height
|
||||
|
||||
UM.I18nCatalog{id: catalog; name: "cura"}
|
||||
|
||||
|
||||
Label
|
||||
{
|
||||
id: licenseHeader
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
text: licenseModel.headerText
|
||||
Layout.fillWidth: true
|
||||
text: catalog.i18nc("@label", "You need to accept the license to install the package")
|
||||
color: UM.Theme.getColor("text")
|
||||
wrapMode: Text.Wrap
|
||||
renderType: Text.NativeRendering
|
||||
}
|
||||
TextArea
|
||||
{
|
||||
id: licenseText
|
||||
anchors.top: licenseHeader.bottom
|
||||
anchors.bottom: parent.bottom
|
||||
|
||||
Row {
|
||||
id: packageRow
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.topMargin: UM.Theme.getSize("default_margin").height
|
||||
readOnly: true
|
||||
text: licenseModel.licenseText
|
||||
height: childrenRect.height
|
||||
spacing: UM.Theme.getSize("default_margin").width
|
||||
leftPadding: UM.Theme.getSize("narrow_margin").width
|
||||
|
||||
Image
|
||||
{
|
||||
id: icon
|
||||
width: 30 * screenScaleFactor
|
||||
height: width
|
||||
fillMode: Image.PreserveAspectFit
|
||||
source: licenseModel.iconUrl || "../../images/logobot.svg"
|
||||
mipmap: true
|
||||
}
|
||||
|
||||
Label
|
||||
{
|
||||
id: packageName
|
||||
text: licenseModel.packageName
|
||||
color: UM.Theme.getColor("text")
|
||||
font.bold: true
|
||||
anchors.verticalCenter: icon.verticalCenter
|
||||
height: contentHeight
|
||||
wrapMode: Text.Wrap
|
||||
renderType: Text.NativeRendering
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
Cura.ScrollableTextArea
|
||||
{
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
anchors.topMargin: UM.Theme.getSize("default_margin").height
|
||||
|
||||
textArea.text: licenseModel.licenseText
|
||||
textArea.readOnly: true
|
||||
}
|
||||
|
||||
}
|
||||
rightButtons:
|
||||
[
|
||||
Button
|
||||
Cura.PrimaryButton
|
||||
{
|
||||
id: acceptButton
|
||||
anchors.margins: UM.Theme.getSize("default_margin").width
|
||||
text: catalog.i18nc("@action:button", "Accept")
|
||||
leftPadding: UM.Theme.getSize("dialog_primary_button_padding").width
|
||||
rightPadding: UM.Theme.getSize("dialog_primary_button_padding").width
|
||||
|
||||
text: licenseModel.acceptButtonText
|
||||
onClicked: { handler.onLicenseAccepted() }
|
||||
},
|
||||
Button
|
||||
}
|
||||
]
|
||||
|
||||
leftButtons:
|
||||
[
|
||||
Cura.SecondaryButton
|
||||
{
|
||||
id: declineButton
|
||||
anchors.margins: UM.Theme.getSize("default_margin").width
|
||||
text: catalog.i18nc("@action:button", "Decline")
|
||||
text: licenseModel.declineButtonText
|
||||
onClicked: { handler.onLicenseDeclined() }
|
||||
}
|
||||
]
|
||||
|
@ -18,3 +18,11 @@ class CloudApiModel:
|
||||
cloud_api_root=cloud_api_root,
|
||||
cloud_api_version=cloud_api_version,
|
||||
)
|
||||
|
||||
## https://api.ultimaker.com/cura-packages/v1/user/packages/{package_id}
|
||||
@classmethod
|
||||
def userPackageUrl(cls, package_id: str) -> str:
|
||||
|
||||
return (CloudApiModel.api_url_user_packages + "/{package_id}").format(
|
||||
package_id=package_id
|
||||
)
|
||||
|
@ -8,11 +8,12 @@ from UM import i18nCatalog
|
||||
from UM.Logger import Logger
|
||||
from UM.Message import Message
|
||||
from UM.Signal import Signal
|
||||
from cura.CuraApplication import CuraApplication
|
||||
from cura.CuraApplication import CuraApplication, ApplicationMetadata
|
||||
from ..CloudApiModel import CloudApiModel
|
||||
from .SubscribedPackagesModel import SubscribedPackagesModel
|
||||
from ..UltimakerCloudScope import UltimakerCloudScope
|
||||
|
||||
from typing import List, Dict, Any
|
||||
|
||||
class CloudPackageChecker(QObject):
|
||||
def __init__(self, application: CuraApplication) -> None:
|
||||
@ -25,12 +26,12 @@ class CloudPackageChecker(QObject):
|
||||
|
||||
self._application.initializationFinished.connect(self._onAppInitialized)
|
||||
self._i18n_catalog = i18nCatalog("cura")
|
||||
self._sdk_version = ApplicationMetadata.CuraSDKVersion
|
||||
|
||||
# This is a plugin, so most of the components required are not ready when
|
||||
# this is initialized. Therefore, we wait until the application is ready.
|
||||
def _onAppInitialized(self) -> None:
|
||||
self._package_manager = self._application.getPackageManager()
|
||||
|
||||
# initial check
|
||||
self._fetchUserSubscribedPackages()
|
||||
# check again whenever the login state changes
|
||||
@ -38,25 +39,51 @@ class CloudPackageChecker(QObject):
|
||||
|
||||
def _fetchUserSubscribedPackages(self) -> None:
|
||||
if self._application.getCuraAPI().account.isLoggedIn:
|
||||
self._getUserPackages()
|
||||
self._getUserSubscribedPackages()
|
||||
|
||||
def _handleCompatibilityData(self, json_data) -> None:
|
||||
user_subscribed_packages = [plugin["package_id"] for plugin in json_data]
|
||||
def _getUserSubscribedPackages(self) -> None:
|
||||
Logger.debug("Requesting subscribed packages metadata from server.")
|
||||
url = CloudApiModel.api_url_user_packages
|
||||
self._application.getHttpRequestManager().get(url,
|
||||
callback = self._onUserPackagesRequestFinished,
|
||||
error_callback = self._onUserPackagesRequestFinished,
|
||||
scope = self._scope)
|
||||
|
||||
def _onUserPackagesRequestFinished(self, reply: "QNetworkReply", error: Optional["QNetworkReply.NetworkError"] = None) -> None:
|
||||
if error is not None or reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) != 200:
|
||||
Logger.log("w",
|
||||
"Requesting user packages failed, response code %s while trying to connect to %s",
|
||||
reply.attribute(QNetworkRequest.HttpStatusCodeAttribute), reply.url())
|
||||
return
|
||||
|
||||
try:
|
||||
json_data = json.loads(bytes(reply.readAll()).decode("utf-8"))
|
||||
# Check for errors:
|
||||
if "errors" in json_data:
|
||||
for error in json_data["errors"]:
|
||||
Logger.log("e", "%s", error["title"])
|
||||
return
|
||||
self._handleCompatibilityData(json_data["data"])
|
||||
except json.decoder.JSONDecodeError:
|
||||
Logger.log("w", "Received invalid JSON for user subscribed packages from the Web Marketplace")
|
||||
|
||||
def _handleCompatibilityData(self, subscribed_packages_payload: List[Dict[str, Any]]) -> None:
|
||||
user_subscribed_packages = [plugin["package_id"] for plugin in subscribed_packages_payload]
|
||||
user_installed_packages = self._package_manager.getUserInstalledPackages()
|
||||
|
||||
# We need to re-evaluate the dismissed packages
|
||||
# (i.e. some package might got updated to the correct SDK version in the meantime,
|
||||
# hence remove them from the Dismissed Incompatible list)
|
||||
self._package_manager.reEvaluateDismissedPackages(subscribed_packages_payload, self._sdk_version)
|
||||
user_dismissed_packages = self._package_manager.getDismissedPackages()
|
||||
if user_dismissed_packages:
|
||||
user_installed_packages += user_dismissed_packages
|
||||
# We check if there are packages installed in Cloud Marketplace but not in Cura marketplace
|
||||
|
||||
# We check if there are packages installed in Web Marketplace but not in Cura marketplace
|
||||
package_discrepancy = list(set(user_subscribed_packages).difference(user_installed_packages))
|
||||
|
||||
self._model.setMetadata(json_data)
|
||||
self._model.addDiscrepancies(package_discrepancy)
|
||||
self._model.initialize()
|
||||
|
||||
if not self._model.hasCompatiblePackages:
|
||||
return None
|
||||
|
||||
if package_discrepancy:
|
||||
self._model.addDiscrepancies(package_discrepancy)
|
||||
self._model.initialize(subscribed_packages_payload)
|
||||
self._handlePackageDiscrepancies()
|
||||
|
||||
def _handlePackageDiscrepancies(self) -> None:
|
||||
@ -64,7 +91,6 @@ class CloudPackageChecker(QObject):
|
||||
sync_message = Message(self._i18n_catalog.i18nc(
|
||||
"@info:generic",
|
||||
"\nDo you want to sync material and software packages with your account?"),
|
||||
lifetime=0,
|
||||
title=self._i18n_catalog.i18nc("@info:title", "Changes detected from your Ultimaker account", ))
|
||||
sync_message.addAction("sync",
|
||||
name=self._i18n_catalog.i18nc("@action:button", "Sync"),
|
||||
@ -77,34 +103,3 @@ class CloudPackageChecker(QObject):
|
||||
def _onSyncButtonClicked(self, sync_message: Message, sync_message_action: str) -> None:
|
||||
sync_message.hide()
|
||||
self.discrepancies.emit(self._model)
|
||||
|
||||
def _getUserPackages(self) -> None:
|
||||
Logger.log("d", "Requesting subscribed packages metadata from server.")
|
||||
url = CloudApiModel.api_url_user_packages
|
||||
|
||||
self._application.getHttpRequestManager().get(url,
|
||||
callback = self._onUserPackagesRequestFinished,
|
||||
error_callback = self._onUserPackagesRequestFinished,
|
||||
scope = self._scope)
|
||||
|
||||
def _onUserPackagesRequestFinished(self,
|
||||
reply: "QNetworkReply",
|
||||
error: Optional["QNetworkReply.NetworkError"] = None) -> None:
|
||||
if error is not None or reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) != 200:
|
||||
Logger.log("w",
|
||||
"Requesting user packages failed, response code %s while trying to connect to %s",
|
||||
reply.attribute(QNetworkRequest.HttpStatusCodeAttribute), reply.url())
|
||||
return
|
||||
|
||||
try:
|
||||
json_data = json.loads(bytes(reply.readAll()).decode("utf-8"))
|
||||
|
||||
# Check for errors:
|
||||
if "errors" in json_data:
|
||||
for error in json_data["errors"]:
|
||||
Logger.log("e", "%s", error["title"])
|
||||
return
|
||||
|
||||
self._handleCompatibilityData(json_data["data"])
|
||||
except json.decoder.JSONDecodeError:
|
||||
Logger.log("w", "Received invalid JSON for user packages")
|
||||
|
@ -1,18 +1,51 @@
|
||||
from UM.Logger import Logger
|
||||
from UM.TaskManagement.HttpRequestManager import HttpRequestManager
|
||||
from cura.CuraApplication import CuraApplication
|
||||
from ..CloudApiModel import CloudApiModel
|
||||
from ..UltimakerCloudScope import UltimakerCloudScope
|
||||
|
||||
|
||||
## Manages Cloud subscriptions. When a package is added to a user's account, the user is 'subscribed' to that package
|
||||
# Whenever the user logs in on another instance of Cura, these subscriptions can be used to sync the user's plugins
|
||||
class CloudPackageManager:
|
||||
def __init__(self, app: CuraApplication) -> None:
|
||||
self._request_manager = app.getHttpRequestManager()
|
||||
self._scope = UltimakerCloudScope(app)
|
||||
"""Manages Cloud subscriptions
|
||||
|
||||
def subscribe(self, package_id: str) -> None:
|
||||
When a package is added to a user's account, the user is 'subscribed' to that package.
|
||||
Whenever the user logs in on another instance of Cura, these subscriptions can be used to sync the user's plugins
|
||||
|
||||
Singleton: use CloudPackageManager.getInstance() instead of CloudPackageManager()
|
||||
"""
|
||||
|
||||
__instance = None
|
||||
|
||||
@classmethod
|
||||
def getInstance(cls, app: CuraApplication):
|
||||
if not cls.__instance:
|
||||
cls.__instance = CloudPackageManager(app)
|
||||
return cls.__instance
|
||||
|
||||
def __init__(self, app: CuraApplication) -> None:
|
||||
if self.__instance is not None:
|
||||
raise RuntimeError("This is a Singleton. use getInstance()")
|
||||
|
||||
self._scope = UltimakerCloudScope(app) # type: UltimakerCloudScope
|
||||
|
||||
app.getPackageManager().packageInstalled.connect(self._onPackageInstalled)
|
||||
|
||||
def unsubscribe(self, package_id: str) -> None:
|
||||
url = CloudApiModel.userPackageUrl(package_id)
|
||||
HttpRequestManager.getInstance().delete(url = url, scope = self._scope)
|
||||
|
||||
def _subscribe(self, package_id: str) -> None:
|
||||
"""You probably don't want to use this directly. All installed packages will be automatically subscribed."""
|
||||
|
||||
Logger.debug("Subscribing to {}", package_id)
|
||||
data = "{\"data\": {\"package_id\": \"%s\", \"sdk_version\": \"%s\"}}" % (package_id, CloudApiModel.sdk_version)
|
||||
self._request_manager.put(url=CloudApiModel.api_url_user_packages,
|
||||
data=data.encode(),
|
||||
scope=self._scope
|
||||
HttpRequestManager.getInstance().put(
|
||||
url = CloudApiModel.api_url_user_packages,
|
||||
data = data.encode(),
|
||||
scope = self._scope
|
||||
)
|
||||
|
||||
def _onPackageInstalled(self, package_id: str):
|
||||
if CuraApplication.getInstance().getCuraAPI().account.isLoggedIn:
|
||||
# We might already be subscribed, but checking would take one extra request. Instead, simply subscribe
|
||||
self._subscribe(package_id)
|
||||
|
@ -28,13 +28,12 @@ class DiscrepanciesPresenter(QObject):
|
||||
assert self._dialog
|
||||
self._dialog.accepted.connect(lambda: self._onConfirmClicked(model))
|
||||
|
||||
@pyqtSlot("QVariant", str)
|
||||
def dismissIncompatiblePackage(self, model: SubscribedPackagesModel, package_id: str) -> None:
|
||||
model.dismissPackage(package_id) # update the model to update the view
|
||||
self._package_manager.dismissPackage(package_id) # adds this package_id as dismissed in the user config file
|
||||
|
||||
def _onConfirmClicked(self, model: SubscribedPackagesModel) -> None:
|
||||
# If there are incompatible packages - automatically dismiss them
|
||||
if model.getIncompatiblePackages():
|
||||
self._package_manager.dismissAllIncompatiblePackages(model.getIncompatiblePackages())
|
||||
# For now, all compatible packages presented to the user should be installed.
|
||||
# Later, we might remove items for which the user unselected the package
|
||||
if model.getCompatiblePackages():
|
||||
model.setItems(model.getCompatiblePackages())
|
||||
self.packageMutations.emit(model)
|
||||
|
@ -62,7 +62,8 @@ class DownloadPresenter:
|
||||
"received": 0,
|
||||
"total": 1, # make sure this is not considered done yet. Also divByZero-safe
|
||||
"file_written": None,
|
||||
"request_data": request_data
|
||||
"request_data": request_data,
|
||||
"package_model": item
|
||||
}
|
||||
|
||||
self._started = True
|
||||
@ -128,7 +129,14 @@ class DownloadPresenter:
|
||||
if not item["file_written"]:
|
||||
return False
|
||||
|
||||
success_items = {package_id : value["file_written"] for package_id, value in self._progress.items()}
|
||||
success_items = {
|
||||
package_id:
|
||||
{
|
||||
"package_path": value["file_written"],
|
||||
"icon_url": value["package_model"]["icon_url"]
|
||||
}
|
||||
for package_id, value in self._progress.items()
|
||||
}
|
||||
error_items = [package_id for package_id in self._error]
|
||||
|
||||
self._progress_message.hide()
|
||||
|
@ -6,31 +6,52 @@ catalog = i18nCatalog("cura")
|
||||
|
||||
# Model for the ToolboxLicenseDialog
|
||||
class LicenseModel(QObject):
|
||||
dialogTitleChanged = pyqtSignal()
|
||||
headerChanged = pyqtSignal()
|
||||
licenseTextChanged = pyqtSignal()
|
||||
DEFAULT_DECLINE_BUTTON_TEXT = catalog.i18nc("@button", "Decline")
|
||||
ACCEPT_BUTTON_TEXT = catalog.i18nc("@button", "Agree")
|
||||
|
||||
def __init__(self) -> None:
|
||||
dialogTitleChanged = pyqtSignal()
|
||||
packageNameChanged = pyqtSignal()
|
||||
licenseTextChanged = pyqtSignal()
|
||||
iconChanged = pyqtSignal()
|
||||
|
||||
def __init__(self, decline_button_text: str = DEFAULT_DECLINE_BUTTON_TEXT) -> None:
|
||||
super().__init__()
|
||||
|
||||
self._current_page_idx = 0
|
||||
self._page_count = 1
|
||||
self._dialogTitle = ""
|
||||
self._header_text = ""
|
||||
self._license_text = ""
|
||||
self._package_name = ""
|
||||
self._icon_url = ""
|
||||
self._decline_button_text = decline_button_text
|
||||
|
||||
@pyqtProperty(str, constant = True)
|
||||
def acceptButtonText(self):
|
||||
return self.ACCEPT_BUTTON_TEXT
|
||||
|
||||
@pyqtProperty(str, constant = True)
|
||||
def declineButtonText(self):
|
||||
return self._decline_button_text
|
||||
|
||||
@pyqtProperty(str, notify=dialogTitleChanged)
|
||||
def dialogTitle(self) -> str:
|
||||
return self._dialogTitle
|
||||
|
||||
@pyqtProperty(str, notify=headerChanged)
|
||||
def headerText(self) -> str:
|
||||
return self._header_text
|
||||
@pyqtProperty(str, notify=packageNameChanged)
|
||||
def packageName(self) -> str:
|
||||
return self._package_name
|
||||
|
||||
def setPackageName(self, name: str) -> None:
|
||||
self._header_text = name + ": " + catalog.i18nc("@label", "This plugin contains a license.\nYou need to accept this license to install this plugin.\nDo you agree with the terms below?")
|
||||
self.headerChanged.emit()
|
||||
self._package_name = name
|
||||
self.packageNameChanged.emit()
|
||||
|
||||
@pyqtProperty(str, notify=iconChanged)
|
||||
def iconUrl(self) -> str:
|
||||
return self._icon_url
|
||||
|
||||
def setIconUrl(self, url: str):
|
||||
self._icon_url = url
|
||||
self.iconChanged.emit()
|
||||
|
||||
@pyqtProperty(str, notify=licenseTextChanged)
|
||||
def licenseText(self) -> str:
|
||||
@ -50,6 +71,7 @@ class LicenseModel(QObject):
|
||||
self._updateDialogTitle()
|
||||
|
||||
def _updateDialogTitle(self):
|
||||
self._dialogTitle = catalog.i18nc("@title:window", "Plugin License Agreement ({}/{})"
|
||||
.format(self._current_page_idx + 1, self._page_count))
|
||||
self._dialogTitle = catalog.i18nc("@title:window", "Plugin License Agreement")
|
||||
if self._page_count > 1:
|
||||
self._dialogTitle = self._dialogTitle + " ({}/{})".format(self._current_page_idx + 1, self._page_count)
|
||||
self.dialogTitleChanged.emit()
|
||||
|
@ -1,5 +1,6 @@
|
||||
import os
|
||||
from typing import Dict, Optional, List
|
||||
from collections import OrderedDict
|
||||
from typing import Dict, Optional, List, Any
|
||||
|
||||
from PyQt5.QtCore import QObject, pyqtSlot
|
||||
|
||||
@ -17,6 +18,7 @@ class LicensePresenter(QObject):
|
||||
|
||||
def __init__(self, app: CuraApplication) -> None:
|
||||
super().__init__()
|
||||
self._catalog = i18nCatalog("cura")
|
||||
self._dialog = None # type: Optional[QObject]
|
||||
self._package_manager = app.getPackageManager() # type: PackageManager
|
||||
# Emits List[Dict[str, [Any]] containing for example
|
||||
@ -25,7 +27,9 @@ class LicensePresenter(QObject):
|
||||
|
||||
self._current_package_idx = 0
|
||||
self._package_models = [] # type: List[Dict]
|
||||
self._license_model = LicenseModel() # type: LicenseModel
|
||||
decline_button_text = self._catalog.i18nc("@button", "Decline and remove from account")
|
||||
self._license_model = LicenseModel(decline_button_text=decline_button_text) # type: LicenseModel
|
||||
self._page_count = 0
|
||||
|
||||
self._app = app
|
||||
|
||||
@ -34,7 +38,7 @@ class LicensePresenter(QObject):
|
||||
## Show a license dialog for multiple packages where users can read a license and accept or decline them
|
||||
# \param plugin_path: Root directory of the Toolbox plugin
|
||||
# \param packages: Dict[package id, file path]
|
||||
def present(self, plugin_path: str, packages: Dict[str, str]) -> None:
|
||||
def present(self, plugin_path: str, packages: Dict[str, Dict[str, str]]) -> None:
|
||||
path = os.path.join(plugin_path, self._compatibility_dialog_path)
|
||||
|
||||
self._initState(packages)
|
||||
@ -42,12 +46,11 @@ class LicensePresenter(QObject):
|
||||
if self._dialog is None:
|
||||
|
||||
context_properties = {
|
||||
"catalog": i18nCatalog("cura"),
|
||||
"catalog": self._catalog,
|
||||
"licenseModel": self._license_model,
|
||||
"handler": self
|
||||
}
|
||||
self._dialog = self._app.createQmlComponent(path, context_properties)
|
||||
self._license_model.setPageCount(len(self._package_models))
|
||||
self._presentCurrentPackage()
|
||||
|
||||
@pyqtSlot()
|
||||
@ -60,32 +63,41 @@ class LicensePresenter(QObject):
|
||||
self._package_models[self._current_package_idx]["accepted"] = False
|
||||
self._checkNextPage()
|
||||
|
||||
def _initState(self, packages: Dict[str, str]) -> None:
|
||||
self._package_models = [
|
||||
{
|
||||
"package_id" : package_id,
|
||||
"package_path" : package_path,
|
||||
"accepted" : None #: None: no answer yet
|
||||
}
|
||||
for package_id, package_path in packages.items()
|
||||
]
|
||||
def _initState(self, packages: Dict[str, Dict[str, str]]) -> None:
|
||||
|
||||
implicitly_accepted_count = 0
|
||||
|
||||
for package_id, item in packages.items():
|
||||
item["package_id"] = package_id
|
||||
item["licence_content"] = self._package_manager.getPackageLicense(item["package_path"])
|
||||
if item["licence_content"] is None:
|
||||
# Implicitly accept when there is no license
|
||||
item["accepted"] = True
|
||||
implicitly_accepted_count = implicitly_accepted_count + 1
|
||||
self._package_models.append(item)
|
||||
else:
|
||||
item["accepted"] = None #: None: no answer yet
|
||||
# When presenting the packages, we want to show packages which have a license first.
|
||||
# In fact, we don't want to show the others at all because they are implicitly accepted
|
||||
self._package_models.insert(0, item)
|
||||
CuraApplication.getInstance().processEvents()
|
||||
self._page_count = len(self._package_models) - implicitly_accepted_count
|
||||
self._license_model.setPageCount(self._page_count)
|
||||
|
||||
|
||||
def _presentCurrentPackage(self) -> None:
|
||||
package_model = self._package_models[self._current_package_idx]
|
||||
license_content = self._package_manager.getPackageLicense(package_model["package_path"])
|
||||
if license_content is None:
|
||||
# Implicitly accept when there is no license
|
||||
self.onLicenseAccepted()
|
||||
return
|
||||
package_info = self._package_manager.getPackageInfo(package_model["package_path"])
|
||||
|
||||
self._license_model.setCurrentPageIdx(self._current_package_idx)
|
||||
self._license_model.setPackageName(package_model["package_id"])
|
||||
self._license_model.setLicenseText(license_content)
|
||||
self._license_model.setPackageName(package_info["display_name"])
|
||||
self._license_model.setIconUrl(package_model["icon_url"])
|
||||
self._license_model.setLicenseText(package_model["licence_content"])
|
||||
if self._dialog:
|
||||
self._dialog.open() # Does nothing if already open
|
||||
|
||||
def _checkNextPage(self) -> None:
|
||||
if self._current_package_idx + 1 < len(self._package_models):
|
||||
if self._current_package_idx + 1 < self._page_count:
|
||||
self._current_package_idx += 1
|
||||
self._presentCurrentPackage()
|
||||
else:
|
||||
|
@ -37,27 +37,18 @@ class SubscribedPackagesModel(ListModel):
|
||||
return True
|
||||
return False
|
||||
|
||||
# Sets the "is_compatible" to True for the given package, in memory
|
||||
|
||||
@pyqtSlot()
|
||||
def dismissPackage(self, package_id: str) -> None:
|
||||
package = self.find(key="package_id", value=package_id)
|
||||
if package != -1:
|
||||
self.setProperty(package, property="is_dismissed", value=True)
|
||||
Logger.debug("Package {} has been dismissed".format(package_id))
|
||||
|
||||
def setMetadata(self, data: List[Dict[str, List[Any]]]) -> None:
|
||||
self._metadata = data
|
||||
|
||||
def addDiscrepancies(self, discrepancy: List[str]) -> None:
|
||||
self._discrepancies = discrepancy
|
||||
|
||||
def getCompatiblePackages(self):
|
||||
return [x for x in self._items if x["is_compatible"]]
|
||||
def getCompatiblePackages(self) -> List[Dict[str, Any]]:
|
||||
return [package for package in self._items if package["is_compatible"]]
|
||||
|
||||
def initialize(self) -> None:
|
||||
def getIncompatiblePackages(self) -> List[str]:
|
||||
return [package["package_id"] for package in self._items if not package["is_compatible"]]
|
||||
|
||||
def initialize(self, subscribed_packages_payload: List[Dict[str, Any]]) -> None:
|
||||
self._items.clear()
|
||||
for item in self._metadata:
|
||||
for item in subscribed_packages_payload:
|
||||
if item["package_id"] not in self._discrepancies:
|
||||
continue
|
||||
package = {
|
||||
|
@ -1,8 +1,10 @@
|
||||
import os
|
||||
from typing import List, Dict, Any, cast
|
||||
|
||||
from UM import i18n_catalog
|
||||
from UM.Extension import Extension
|
||||
from UM.Logger import Logger
|
||||
from UM.Message import Message
|
||||
from UM.PluginRegistry import PluginRegistry
|
||||
from cura.CuraApplication import CuraApplication
|
||||
from .CloudPackageChecker import CloudPackageChecker
|
||||
@ -36,7 +38,8 @@ class SyncOrchestrator(Extension):
|
||||
self._name = "SyncOrchestrator"
|
||||
|
||||
self._package_manager = app.getPackageManager()
|
||||
self._cloud_package_manager = CloudPackageManager(app)
|
||||
# Keep a reference to the CloudPackageManager. it watches for installed packages and subscribes to them
|
||||
self._cloud_package_manager = CloudPackageManager.getInstance(app) # type: CloudPackageManager
|
||||
|
||||
self._checker = CloudPackageChecker(app) # type: CloudPackageChecker
|
||||
self._checker.discrepancies.connect(self._onDiscrepancies)
|
||||
@ -61,32 +64,37 @@ class SyncOrchestrator(Extension):
|
||||
self._download_presenter.download(mutations)
|
||||
|
||||
## Called when a set of packages have finished downloading
|
||||
# \param success_items: Dict[package_id, file_path]
|
||||
# \param success_items: Dict[package_id, Dict[str, str]]
|
||||
# \param error_items: List[package_id]
|
||||
def _onDownloadFinished(self, success_items: Dict[str, str], error_items: List[str]) -> None:
|
||||
# todo handle error items
|
||||
def _onDownloadFinished(self, success_items: Dict[str, Dict[str, str]], error_items: List[str]) -> None:
|
||||
if error_items:
|
||||
message = i18n_catalog.i18nc("@info:generic", "{} plugins failed to download".format(len(error_items)))
|
||||
self._showErrorMessage(message)
|
||||
|
||||
plugin_path = cast(str, PluginRegistry.getInstance().getPluginPath(self.getPluginId()))
|
||||
self._license_presenter.present(plugin_path, success_items)
|
||||
|
||||
# Called when user has accepted / declined all licenses for the downloaded packages
|
||||
def _onLicenseAnswers(self, answers: List[Dict[str, Any]]) -> None:
|
||||
Logger.debug("Got license answers: {}", answers)
|
||||
|
||||
has_changes = False # True when at least one package is installed
|
||||
|
||||
for item in answers:
|
||||
if item["accepted"]:
|
||||
# install and subscribe packages
|
||||
if not self._package_manager.installPackage(item["package_path"]):
|
||||
Logger.error("could not install {}".format(item["package_id"]))
|
||||
message = "Could not install {}".format(item["package_id"])
|
||||
self._showErrorMessage(message)
|
||||
continue
|
||||
self._cloud_package_manager.subscribe(item["package_id"])
|
||||
has_changes = True
|
||||
else:
|
||||
# todo unsubscribe declined packages
|
||||
pass
|
||||
self._cloud_package_manager.unsubscribe(item["package_id"])
|
||||
# delete temp file
|
||||
os.remove(item["package_path"])
|
||||
|
||||
if has_changes:
|
||||
self._restart_presenter.present()
|
||||
|
||||
## Logs an error and shows it to the user
|
||||
def _showErrorMessage(self, text: str):
|
||||
Logger.error(text)
|
||||
Message(text, lifetime=0).show()
|
||||
|
@ -21,7 +21,6 @@ from cura.Machines.ContainerTree import ContainerTree
|
||||
|
||||
from .CloudApiModel import CloudApiModel
|
||||
from .AuthorsModel import AuthorsModel
|
||||
from .CloudSync.CloudPackageManager import CloudPackageManager
|
||||
from .CloudSync.LicenseModel import LicenseModel
|
||||
from .PackagesModel import PackagesModel
|
||||
from .UltimakerCloudScope import UltimakerCloudScope
|
||||
@ -44,7 +43,6 @@ class Toolbox(QObject, Extension):
|
||||
self._sdk_version = ApplicationMetadata.CuraSDKVersion # type: Union[str, int]
|
||||
|
||||
# Network:
|
||||
self._cloud_package_manager = CloudPackageManager(application) # type: CloudPackageManager
|
||||
self._download_request_data = None # type: Optional[HttpRequestData]
|
||||
self._download_progress = 0 # type: float
|
||||
self._is_downloading = False # type: bool
|
||||
@ -147,17 +145,14 @@ class Toolbox(QObject, Extension):
|
||||
|
||||
self._application.getHttpRequestManager().put(url, data = data.encode(), scope = self._scope)
|
||||
|
||||
@pyqtSlot(str)
|
||||
def subscribe(self, package_id: str) -> None:
|
||||
self._cloud_package_manager.subscribe(package_id)
|
||||
|
||||
def getLicenseDialogPluginFileLocation(self) -> str:
|
||||
return self._license_dialog_plugin_file_location
|
||||
|
||||
def openLicenseDialog(self, plugin_name: str, license_content: str, plugin_file_location: str) -> None:
|
||||
def openLicenseDialog(self, plugin_name: str, license_content: str, plugin_file_location: str, icon_url: str) -> None:
|
||||
# Set page 1/1 when opening the dialog for a single package
|
||||
self._license_model.setCurrentPageIdx(0)
|
||||
self._license_model.setPageCount(1)
|
||||
self._license_model.setIconUrl(icon_url)
|
||||
|
||||
self._license_model.setPackageName(plugin_name)
|
||||
self._license_model.setLicenseText(license_content)
|
||||
@ -376,7 +371,6 @@ class Toolbox(QObject, Extension):
|
||||
def onLicenseAccepted(self):
|
||||
self.closeLicenseDialog.emit()
|
||||
package_id = self.install(self.getLicenseDialogPluginFileLocation())
|
||||
self.subscribe(package_id)
|
||||
|
||||
|
||||
@pyqtSlot()
|
||||
@ -670,14 +664,16 @@ class Toolbox(QObject, Extension):
|
||||
return
|
||||
|
||||
license_content = self._package_manager.getPackageLicense(file_path)
|
||||
package_id = package_info["package_id"]
|
||||
if license_content is not None:
|
||||
self.openLicenseDialog(package_info["package_id"], license_content, file_path)
|
||||
# get the icon url for package_id, make sure the result is a string, never None
|
||||
icon_url = next((x["icon_url"] for x in self.packagesModel.items if x["id"] == package_id), None) or ""
|
||||
self.openLicenseDialog(package_info["display_name"], license_content, file_path, icon_url)
|
||||
return
|
||||
|
||||
package_id = self.install(file_path)
|
||||
if package_id != package_info["package_id"]:
|
||||
Logger.error("Installed package {} does not match {}".format(package_id, package_info["package_id"]))
|
||||
self.subscribe(package_id)
|
||||
installed_id = self.install(file_path)
|
||||
if installed_id != package_id:
|
||||
Logger.error("Installed package {} does not match {}".format(installed_id, package_id))
|
||||
|
||||
# Getter & Setters for Properties:
|
||||
# --------------------------------------------------------------------------
|
||||
@ -699,14 +695,14 @@ class Toolbox(QObject, Extension):
|
||||
def isDownloading(self) -> bool:
|
||||
return self._is_downloading
|
||||
|
||||
def setActivePackage(self, package: Dict[str, Any]) -> None:
|
||||
def setActivePackage(self, package: QObject) -> None:
|
||||
if self._active_package != package:
|
||||
self._active_package = package
|
||||
self.activePackageChanged.emit()
|
||||
|
||||
## The active package is the package that is currently being downloaded
|
||||
@pyqtProperty(QObject, fset = setActivePackage, notify = activePackageChanged)
|
||||
def activePackage(self) -> Optional[Dict[str, Any]]:
|
||||
def activePackage(self) -> Optional[QObject]:
|
||||
return self._active_package
|
||||
|
||||
def setViewCategory(self, category: str = "plugin") -> None:
|
||||
|
@ -6,6 +6,9 @@ from cura.API import Account
|
||||
from cura.CuraApplication import CuraApplication
|
||||
|
||||
|
||||
## Add a Authorization header to the request for Ultimaker Cloud Api requests.
|
||||
# When the user is not logged in or a token is not available, a warning will be logged
|
||||
# Also add the user agent headers (see DefaultUserAgentScope)
|
||||
class UltimakerCloudScope(DefaultUserAgentScope):
|
||||
def __init__(self, application: CuraApplication):
|
||||
super().__init__(application)
|
||||
|
@ -848,6 +848,23 @@
|
||||
"website": "https://ultimaker.com"
|
||||
}
|
||||
}
|
||||
},
|
||||
"VersionUpgrade44to45": {
|
||||
"package_info": {
|
||||
"package_id": "VersionUpgrade44to45",
|
||||
"package_type": "plugin",
|
||||
"display_name": "Version Upgrade 4.4 to 4.5",
|
||||
"description": "Upgrades configurations from Cura 4.4 to Cura 4.5.",
|
||||
"package_version": "1.0.0",
|
||||
"sdk_version": "7.0.0",
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "UltimakerPackages",
|
||||
"display_name": "Ultimaker B.V.",
|
||||
"email": "plugins@ultimaker.com",
|
||||
"website": "https://ultimaker.com"
|
||||
}
|
||||
}
|
||||
},
|
||||
"X3DReader": {
|
||||
"package_info": {
|
||||
|
@ -2122,6 +2122,7 @@
|
||||
"default_value": 210,
|
||||
"minimum_value_warning": "0",
|
||||
"maximum_value_warning": "285",
|
||||
"maximum_value": "365",
|
||||
"enabled": false,
|
||||
"settable_per_extruder": true,
|
||||
"settable_per_mesh": false,
|
||||
@ -2153,6 +2154,7 @@
|
||||
"minimum_value": "-273.15",
|
||||
"minimum_value_warning": "0",
|
||||
"maximum_value_warning": "285",
|
||||
"maximum_value": "365",
|
||||
"enabled": "machine_nozzle_temp_enabled and not (material_flow_dependent_temperature)",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true
|
||||
@ -2168,6 +2170,7 @@
|
||||
"minimum_value": "-273.15",
|
||||
"minimum_value_warning": "0",
|
||||
"maximum_value_warning": "285",
|
||||
"maximum_value": "365",
|
||||
"enabled": "machine_nozzle_temp_enabled",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true
|
||||
@ -2183,6 +2186,7 @@
|
||||
"minimum_value": "-273.15",
|
||||
"minimum_value_warning": "material_standby_temperature",
|
||||
"maximum_value_warning": "material_print_temperature",
|
||||
"maximum_value": "365",
|
||||
"enabled": "machine_nozzle_temp_enabled and not machine_extruders_share_heater",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true
|
||||
@ -2198,6 +2202,7 @@
|
||||
"minimum_value": "-273.15",
|
||||
"minimum_value_warning": "material_standby_temperature",
|
||||
"maximum_value_warning": "material_print_temperature",
|
||||
"maximum_value": "365",
|
||||
"enabled": "machine_nozzle_temp_enabled and not machine_extruders_share_heater",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true
|
||||
@ -2227,6 +2232,7 @@
|
||||
"minimum_value": "-273.15",
|
||||
"minimum_value_warning": "build_volume_temperature",
|
||||
"maximum_value_warning": "130",
|
||||
"maximum_value": "200",
|
||||
"enabled": false,
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": false,
|
||||
@ -2244,6 +2250,7 @@
|
||||
"minimum_value": "-273.15",
|
||||
"minimum_value_warning": "build_volume_temperature",
|
||||
"maximum_value_warning": "130",
|
||||
"maximum_value": "200",
|
||||
"enabled": "machine_heated_bed and machine_gcode_flavor != \"UltiGCode\"",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": false,
|
||||
@ -2261,6 +2268,7 @@
|
||||
"minimum_value": "-273.15",
|
||||
"minimum_value_warning": "max(build_volume_temperature, max(extruderValues('material_bed_temperature')))",
|
||||
"maximum_value_warning": "130",
|
||||
"maximum_value": "200",
|
||||
"enabled": "machine_heated_bed and machine_gcode_flavor != \"UltiGCode\"",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": false,
|
||||
@ -2377,6 +2385,7 @@
|
||||
"enabled": false,
|
||||
"minimum_value": "-273.15",
|
||||
"maximum_value_warning": "300",
|
||||
"maximum_value": "365",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true
|
||||
},
|
||||
@ -2415,6 +2424,7 @@
|
||||
"default_value": 50,
|
||||
"enabled": false,
|
||||
"minimum_value": "-273.15",
|
||||
"maximum_value": "365",
|
||||
"maximum_value_warning": "300",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true
|
||||
@ -2691,6 +2701,7 @@
|
||||
"minimum_value": "-273.15",
|
||||
"minimum_value_warning": "0",
|
||||
"maximum_value_warning": "260",
|
||||
"maximum_value": "365",
|
||||
"enabled": "extruders_enabled_count > 1 and machine_nozzle_temp_enabled",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true
|
||||
@ -5688,7 +5699,7 @@
|
||||
"unit": "mm",
|
||||
"enabled": "resolveOrValue('prime_tower_enable')",
|
||||
"default_value": 200,
|
||||
"value": "machine_width - max(extruderValue(adhesion_extruder_nr, 'brim_width') * extruderValue(adhesion_extruder_nr, 'initial_layer_line_width_factor') / 100 if adhesion_type == 'brim' or (prime_tower_brim_enable and adhesion_type != 'raft') else (extruderValue(adhesion_extruder_nr, 'raft_margin') if adhesion_type == 'raft' else (extruderValue(adhesion_extruder_nr, 'skirt_gap') if adhesion_type == 'skirt' else 0)), max(extruderValues('travel_avoid_distance'))) - max(extruderValues('support_offset')) - sum(extruderValues('skirt_brim_line_width')) * extruderValue(adhesion_extruder_nr, 'initial_layer_line_width_factor') / 100 - (resolveOrValue('draft_shield_dist') if resolveOrValue('draft_shield_enabled') else 0) - 1",
|
||||
"value": "machine_width - max(extruderValue(adhesion_extruder_nr, 'brim_width') * extruderValue(adhesion_extruder_nr, 'initial_layer_line_width_factor') / 100 if adhesion_type == 'brim' or (prime_tower_brim_enable and adhesion_type != 'raft') else (extruderValue(adhesion_extruder_nr, 'raft_margin') if adhesion_type == 'raft' else (extruderValue(adhesion_extruder_nr, 'skirt_gap') if adhesion_type == 'skirt' else 0)), max(extruderValues('travel_avoid_distance'))) - max(extruderValues('support_offset')) - sum(extruderValues('skirt_brim_line_width')) * extruderValue(adhesion_extruder_nr, 'initial_layer_line_width_factor') / 100 - (resolveOrValue('draft_shield_dist') if resolveOrValue('draft_shield_enabled') else 0) - max(map(abs, extruderValues('machine_nozzle_offset_x'))) - 1",
|
||||
"maximum_value": "machine_width / 2 if machine_center_is_zero else machine_width",
|
||||
"minimum_value": "resolveOrValue('prime_tower_size') - machine_width / 2 if machine_center_is_zero else resolveOrValue('prime_tower_size')",
|
||||
"settable_per_mesh": false,
|
||||
@ -5702,7 +5713,7 @@
|
||||
"unit": "mm",
|
||||
"enabled": "resolveOrValue('prime_tower_enable')",
|
||||
"default_value": 200,
|
||||
"value": "machine_depth - prime_tower_size - max(extruderValue(adhesion_extruder_nr, 'brim_width') * extruderValue(adhesion_extruder_nr, 'initial_layer_line_width_factor') / 100 if adhesion_type == 'brim' or (prime_tower_brim_enable and adhesion_type != 'raft') else (extruderValue(adhesion_extruder_nr, 'raft_margin') if adhesion_type == 'raft' else (extruderValue(adhesion_extruder_nr, 'skirt_gap') if adhesion_type == 'skirt' else 0)), max(extruderValues('travel_avoid_distance'))) - max(extruderValues('support_offset')) - sum(extruderValues('skirt_brim_line_width')) * extruderValue(adhesion_extruder_nr, 'initial_layer_line_width_factor') / 100 - (resolveOrValue('draft_shield_dist') if resolveOrValue('draft_shield_enabled') else 0) - 1",
|
||||
"value": "machine_depth - prime_tower_size - max(extruderValue(adhesion_extruder_nr, 'brim_width') * extruderValue(adhesion_extruder_nr, 'initial_layer_line_width_factor') / 100 if adhesion_type == 'brim' or (prime_tower_brim_enable and adhesion_type != 'raft') else (extruderValue(adhesion_extruder_nr, 'raft_margin') if adhesion_type == 'raft' else (extruderValue(adhesion_extruder_nr, 'skirt_gap') if adhesion_type == 'skirt' else 0)), max(extruderValues('travel_avoid_distance'))) - max(extruderValues('support_offset')) - sum(extruderValues('skirt_brim_line_width')) * extruderValue(adhesion_extruder_nr, 'initial_layer_line_width_factor') / 100 - (resolveOrValue('draft_shield_dist') if resolveOrValue('draft_shield_enabled') else 0) - max(map(abs, extruderValues('machine_nozzle_offset_y'))) - 1",
|
||||
"maximum_value": "machine_depth / 2 - resolveOrValue('prime_tower_size') if machine_center_is_zero else machine_depth - resolveOrValue('prime_tower_size')",
|
||||
"minimum_value": "machine_depth / -2 if machine_center_is_zero else 0",
|
||||
"settable_per_mesh": false,
|
||||
@ -5984,7 +5995,7 @@
|
||||
"print_sequence":
|
||||
{
|
||||
"label": "Print Sequence",
|
||||
"description": "Whether to print all models one layer at a time or to wait for one model to finish, before moving on to the next. One at a time mode is only possible if all models are separated in such a way that the whole print head can move in between and all models are lower than the distance between the nozzle and the X/Y axes.",
|
||||
"description": "Whether to print all models one layer at a time or to wait for one model to finish, before moving on to the next. One at a time mode is possible if a) only one extruder is enabled and b) all models are separated in such a way that the whole print head can move in between and all models are lower than the distance between the nozzle and the X/Y axes. ",
|
||||
"type": "enum",
|
||||
"options":
|
||||
{
|
||||
@ -6666,6 +6677,16 @@
|
||||
"limit_to_extruder": "wall_0_extruder_nr",
|
||||
"settable_per_mesh": true
|
||||
},
|
||||
"magic_fuzzy_skin_outside_only":
|
||||
{
|
||||
"label": "Fuzzy Skin Outside Only",
|
||||
"description": "Jitter only the parts' outlines and not the parts' holes.",
|
||||
"type": "bool",
|
||||
"default_value": false,
|
||||
"enabled": "magic_fuzzy_skin_enabled",
|
||||
"limit_to_extruder": "wall_0_extruder_nr",
|
||||
"settable_per_mesh": true
|
||||
},
|
||||
"magic_fuzzy_skin_thickness":
|
||||
{
|
||||
"label": "Fuzzy Skin Thickness",
|
||||
|
57
resources/definitions/geeetech_A10.def.json
Normal file
57
resources/definitions/geeetech_A10.def.json
Normal file
@ -0,0 +1,57 @@
|
||||
{
|
||||
"version": 2,
|
||||
"name": "Geeetech A10",
|
||||
"inherits": "fdmprinter",
|
||||
"metadata": {
|
||||
"visible": true,
|
||||
"author": "Amit L",
|
||||
"manufacturer": "Geeetech",
|
||||
"file_formats": "text/x-gcode",
|
||||
"has_materials": true,
|
||||
"machine_extruder_trains":
|
||||
{
|
||||
"0": "geeetech_A10_1"
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
"overrides": {
|
||||
"machine_name": { "default_value": "Geeetech A10" },
|
||||
"machine_width": {
|
||||
"default_value": 220
|
||||
},
|
||||
"machine_height": {
|
||||
"default_value": 220
|
||||
},
|
||||
"machine_depth": {
|
||||
"default_value": 260
|
||||
}, "machine_center_is_zero": {
|
||||
"default_value": false
|
||||
},
|
||||
"layer_height": { "default_value": 0.1 },
|
||||
"layer_height_0": { "default_value": 0.15 },
|
||||
"retraction_amount": { "default_value": 0.8 },
|
||||
"retraction_speed": { "default_value": 35 },
|
||||
"adhesion_type": { "default_value": "skirt" },
|
||||
"machine_head_with_fans_polygon": { "default_value": [[-31,31],[34,31],[34,-40],[-31,-40]] },
|
||||
"gantry_height": { "value": "28" },
|
||||
"machine_max_feedrate_z": { "default_value": 12 },
|
||||
"machine_max_feedrate_e": { "default_value": 120 },
|
||||
"machine_max_acceleration_z": { "default_value": 500 },
|
||||
"machine_acceleration": { "default_value": 1000 },
|
||||
"machine_max_jerk_xy": { "default_value": 10 },
|
||||
"machine_max_jerk_z": { "default_value": 0.2 },
|
||||
"machine_max_jerk_e": { "default_value": 2.5 },
|
||||
"machine_gcode_flavor": { "default_value": "RepRap (Marlin/Sprinter)" },
|
||||
"machine_start_gcode": {
|
||||
"default_value": "G28 \nG1 Z15 F300\nM107\nG90\nM82\nM104 S215\nM140 S55\nG92 E0\nM109 S215\nM107\nG0 X10 Y20 F6000\nG1 Z0.8\nG1 F300 X180 E40\nG1 F1200 Z2\nG92 E0\nG28"
|
||||
},
|
||||
"machine_end_gcode": {
|
||||
"default_value": "G91\nG1 E-1\nG0 X0 Y200\nM104 S0\nG90\nG92 E0\nM140 S0\nM84\nM104 S0\nM140 S0\nM84"
|
||||
},
|
||||
"machine_extruder_count": {
|
||||
"default_value": 1
|
||||
}
|
||||
|
||||
}
|
||||
}
|
58
resources/definitions/geeetech_A10M.def.json
Normal file
58
resources/definitions/geeetech_A10M.def.json
Normal file
@ -0,0 +1,58 @@
|
||||
{
|
||||
"version": 2,
|
||||
"name": "Geeetech A10M",
|
||||
"inherits": "fdmprinter",
|
||||
"metadata": {
|
||||
"visible": true,
|
||||
"author": "Amit L",
|
||||
"manufacturer": "Geeetech",
|
||||
"file_formats": "text/x-gcode",
|
||||
"has_materials": true,
|
||||
"machine_extruder_trains":
|
||||
{
|
||||
"0": "geeetech_A10M_1",
|
||||
"1": "geeetech_A10M_2"
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
"overrides": {
|
||||
"machine_name": { "default_value": "Geeetech A10M" },
|
||||
"machine_width": {
|
||||
"default_value": 220
|
||||
},
|
||||
"machine_height": {
|
||||
"default_value": 220
|
||||
},
|
||||
"machine_depth": {
|
||||
"default_value": 260
|
||||
}, "machine_center_is_zero": {
|
||||
"default_value": false
|
||||
},
|
||||
"layer_height": { "default_value": 0.1 },
|
||||
"layer_height_0": { "default_value": 0.15 },
|
||||
"retraction_amount": { "default_value": 0.8 },
|
||||
"retraction_speed": { "default_value": 35 },
|
||||
"adhesion_type": { "default_value": "skirt" },
|
||||
"machine_head_with_fans_polygon": { "default_value": [[-31,31],[34,31],[34,-40],[-31,-40]] },
|
||||
"gantry_height": { "value": "28" },
|
||||
"machine_max_feedrate_z": { "default_value": 12 },
|
||||
"machine_max_feedrate_e": { "default_value": 120 },
|
||||
"machine_max_acceleration_z": { "default_value": 500 },
|
||||
"machine_acceleration": { "default_value": 1000 },
|
||||
"machine_max_jerk_xy": { "default_value": 10 },
|
||||
"machine_max_jerk_z": { "default_value": 0.2 },
|
||||
"machine_max_jerk_e": { "default_value": 2.5 },
|
||||
"machine_gcode_flavor": { "default_value": "RepRap (Marlin/Sprinter)" },
|
||||
"machine_start_gcode": {
|
||||
"default_value": "G28 \nG1 Z15 F300\nM107\nG90\nM82\nM104 S215\nM140 S55\nG92 E0\nM109 S215\nM107\nM163 S0 P0.50\nM163 S1 P0.50\nM164 S4\nG0 X10 Y20 F6000\nG1 Z0.8\nG1 F300 X180 E40\nG1 F1200 Z2\nG92 E0\nG28"
|
||||
},
|
||||
"machine_end_gcode": {
|
||||
"default_value": "G91\nG1 E-1\nG0 X0 Y200\nM104 S0\nG90\nG92 E0\nM140 S0\nM84\nM104 S0\nM140 S0\nM84"
|
||||
},
|
||||
"machine_extruder_count": {
|
||||
"default_value": 2
|
||||
}
|
||||
|
||||
}
|
||||
}
|
59
resources/definitions/geeetech_A10T.def.json
Normal file
59
resources/definitions/geeetech_A10T.def.json
Normal file
@ -0,0 +1,59 @@
|
||||
{
|
||||
"version": 2,
|
||||
"name": "Geeetech A10T",
|
||||
"inherits": "fdmprinter",
|
||||
"metadata": {
|
||||
"visible": true,
|
||||
"author": "Amit L",
|
||||
"manufacturer": "Geeetech",
|
||||
"file_formats": "text/x-gcode",
|
||||
"has_materials": true,
|
||||
"machine_extruder_trains":
|
||||
{
|
||||
"0": "geeetech_A10T_1",
|
||||
"1": "geeetech_A10T_2",
|
||||
"2": "geeetech_A10T_3"
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
"overrides": {
|
||||
"machine_name": { "default_value": "Geeetech A10T" },
|
||||
"machine_width": {
|
||||
"default_value": 220
|
||||
},
|
||||
"machine_height": {
|
||||
"default_value": 220
|
||||
},
|
||||
"machine_depth": {
|
||||
"default_value": 260
|
||||
}, "machine_center_is_zero": {
|
||||
"default_value": false
|
||||
},
|
||||
"layer_height": { "default_value": 0.1 },
|
||||
"layer_height_0": { "default_value": 0.15 },
|
||||
"retraction_amount": { "default_value": 0.8 },
|
||||
"retraction_speed": { "default_value": 35 },
|
||||
"adhesion_type": { "default_value": "skirt" },
|
||||
"machine_head_with_fans_polygon": { "default_value": [[-31,31],[34,31],[34,-40],[-31,-40]] },
|
||||
"gantry_height": { "value": "28" },
|
||||
"machine_max_feedrate_z": { "default_value": 12 },
|
||||
"machine_max_feedrate_e": { "default_value": 120 },
|
||||
"machine_max_acceleration_z": { "default_value": 500 },
|
||||
"machine_acceleration": { "default_value": 1000 },
|
||||
"machine_max_jerk_xy": { "default_value": 10 },
|
||||
"machine_max_jerk_z": { "default_value": 0.2 },
|
||||
"machine_max_jerk_e": { "default_value": 2.5 },
|
||||
"machine_gcode_flavor": { "default_value": "RepRap (Marlin/Sprinter)" },
|
||||
"machine_start_gcode": {
|
||||
"default_value": "G28 \nG1 Z15 F300\nM107\nG90\nM82\nM104 S215\nM140 S55\nG92 E0\nM109 S215\nM107\nM163 S0 P0.33\nM163 S1 P0.33\nM163 S2 P0.33\nM164 S4\nG0 X10 Y20 F6000\nG1 Z0.8\nG1 F300 X180 E40\nG1 F1200 Z2\nG92 E0\nG28"
|
||||
},
|
||||
"machine_end_gcode": {
|
||||
"default_value": "G91\nG1 E-1\nG0 X0 Y200\nM104 S0\nG90\nG92 E0\nM140 S0\nM84\nM104 S0\nM140 S0\nM84"
|
||||
},
|
||||
"machine_extruder_count": {
|
||||
"default_value": 3
|
||||
}
|
||||
|
||||
}
|
||||
}
|
57
resources/definitions/geeetech_A20.def.json
Normal file
57
resources/definitions/geeetech_A20.def.json
Normal file
@ -0,0 +1,57 @@
|
||||
{
|
||||
"version": 2,
|
||||
"name": "Geeetech A20",
|
||||
"inherits": "fdmprinter",
|
||||
"metadata": {
|
||||
"visible": true,
|
||||
"author": "Amit L",
|
||||
"manufacturer": "Geeetech",
|
||||
"file_formats": "text/x-gcode",
|
||||
"has_materials": true,
|
||||
"machine_extruder_trains":
|
||||
{
|
||||
"0": "geeetech_A20_1"
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
"overrides": {
|
||||
"machine_name": { "default_value": "Geeetech A20" },
|
||||
"machine_width": {
|
||||
"default_value": 250
|
||||
},
|
||||
"machine_height": {
|
||||
"default_value": 250
|
||||
},
|
||||
"machine_depth": {
|
||||
"default_value": 250
|
||||
}, "machine_center_is_zero": {
|
||||
"default_value": false
|
||||
},
|
||||
"layer_height": { "default_value": 0.1 },
|
||||
"layer_height_0": { "default_value": 0.15 },
|
||||
"retraction_amount": { "default_value": 0.8 },
|
||||
"retraction_speed": { "default_value": 35 },
|
||||
"adhesion_type": { "default_value": "skirt" },
|
||||
"machine_head_with_fans_polygon": { "default_value": [[-31,31],[34,31],[34,-40],[-31,-40]] },
|
||||
"gantry_height": { "value": "28" },
|
||||
"machine_max_feedrate_z": { "default_value": 12 },
|
||||
"machine_max_feedrate_e": { "default_value": 120 },
|
||||
"machine_max_acceleration_z": { "default_value": 500 },
|
||||
"machine_acceleration": { "default_value": 1000 },
|
||||
"machine_max_jerk_xy": { "default_value": 10 },
|
||||
"machine_max_jerk_z": { "default_value": 0.2 },
|
||||
"machine_max_jerk_e": { "default_value": 2.5 },
|
||||
"machine_gcode_flavor": { "default_value": "RepRap (Marlin/Sprinter)" },
|
||||
"machine_start_gcode": {
|
||||
"default_value": "G28 \nG1 Z15 F300\nM107\nG90\nM82\nM104 S215\nM140 S55\nG92 E0\nM109 S215\nM107\nG0 X10 Y20 F6000\nG1 Z0.8\nG1 F300 X200 E40\nG1 F1200 Z2\nG92 E0\nG28"
|
||||
},
|
||||
"machine_end_gcode": {
|
||||
"default_value": "G91\nG1 E-1\nG0 X0 Y200\nM104 S0\nG90\nG92 E0\nM140 S0\nM84\nM104 S0\nM140 S0\nM84"
|
||||
},
|
||||
"machine_extruder_count": {
|
||||
"default_value": 1
|
||||
}
|
||||
|
||||
}
|
||||
}
|
58
resources/definitions/geeetech_A20M.def.json
Normal file
58
resources/definitions/geeetech_A20M.def.json
Normal file
@ -0,0 +1,58 @@
|
||||
{
|
||||
"version": 2,
|
||||
"name": "Geeetech A20M",
|
||||
"inherits": "fdmprinter",
|
||||
"metadata": {
|
||||
"visible": true,
|
||||
"author": "Amit L",
|
||||
"manufacturer": "Geeetech",
|
||||
"file_formats": "text/x-gcode",
|
||||
"has_materials": true,
|
||||
"machine_extruder_trains":
|
||||
{
|
||||
"0": "geeetech_A20M_1",
|
||||
"1": "geeetech_A20M_2"
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
"overrides": {
|
||||
"machine_name": { "default_value": "Geeetech A20M" },
|
||||
"machine_width": {
|
||||
"default_value": 250
|
||||
},
|
||||
"machine_height": {
|
||||
"default_value": 250
|
||||
},
|
||||
"machine_depth": {
|
||||
"default_value": 250
|
||||
}, "machine_center_is_zero": {
|
||||
"default_value": false
|
||||
},
|
||||
"layer_height": { "default_value": 0.1 },
|
||||
"layer_height_0": { "default_value": 0.15 },
|
||||
"retraction_amount": { "default_value": 0.8 },
|
||||
"retraction_speed": { "default_value": 35 },
|
||||
"adhesion_type": { "default_value": "skirt" },
|
||||
"machine_head_with_fans_polygon": { "default_value": [[-31,31],[34,31],[34,-40],[-31,-40]] },
|
||||
"gantry_height": { "value": "28" },
|
||||
"machine_max_feedrate_z": { "default_value": 12 },
|
||||
"machine_max_feedrate_e": { "default_value": 120 },
|
||||
"machine_max_acceleration_z": { "default_value": 500 },
|
||||
"machine_acceleration": { "default_value": 1000 },
|
||||
"machine_max_jerk_xy": { "default_value": 10 },
|
||||
"machine_max_jerk_z": { "default_value": 0.2 },
|
||||
"machine_max_jerk_e": { "default_value": 2.5 },
|
||||
"machine_gcode_flavor": { "default_value": "RepRap (Marlin/Sprinter)" },
|
||||
"machine_start_gcode": {
|
||||
"default_value": "G28 \nG1 Z15 F300\nM107\nG90\nM82\nM104 S215\nM140 S55\nG92 E0\nM109 S215\nM107\nM163 S0 P0.50\nM163 S1 P0.50\nM164 S4\nG0 X10 Y20 F6000\nG1 Z0.8\nG1 F300 X200 E40\nG1 F1200 Z2\nG92 E0\nG28"
|
||||
},
|
||||
"machine_end_gcode": {
|
||||
"default_value": "G91\nG1 E-1\nG0 X0 Y200\nM104 S0\nG90\nG92 E0\nM140 S0\nM84\nM104 S0\nM140 S0\nM84"
|
||||
},
|
||||
"machine_extruder_count": {
|
||||
"default_value": 2
|
||||
}
|
||||
|
||||
}
|
||||
}
|
59
resources/definitions/geeetech_A20T.def.json
Normal file
59
resources/definitions/geeetech_A20T.def.json
Normal file
@ -0,0 +1,59 @@
|
||||
{
|
||||
"version": 2,
|
||||
"name": "Geeetech A20T",
|
||||
"inherits": "fdmprinter",
|
||||
"metadata": {
|
||||
"visible": true,
|
||||
"author": "Amit L",
|
||||
"manufacturer": "Geeetech",
|
||||
"file_formats": "text/x-gcode",
|
||||
"has_materials": true,
|
||||
"machine_extruder_trains":
|
||||
{
|
||||
"0": "geeetech_A20T_1",
|
||||
"1": "geeetech_A20T_2",
|
||||
"2": "geeetech_A20T_3"
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
"overrides": {
|
||||
"machine_name": { "default_value": "Geeetech A20T" },
|
||||
"machine_width": {
|
||||
"default_value": 250
|
||||
},
|
||||
"machine_height": {
|
||||
"default_value": 250
|
||||
},
|
||||
"machine_depth": {
|
||||
"default_value": 250
|
||||
}, "machine_center_is_zero": {
|
||||
"default_value": false
|
||||
},
|
||||
"layer_height": { "default_value": 0.1 },
|
||||
"layer_height_0": { "default_value": 0.15 },
|
||||
"retraction_amount": { "default_value": 0.8 },
|
||||
"retraction_speed": { "default_value": 35 },
|
||||
"adhesion_type": { "default_value": "skirt" },
|
||||
"machine_head_with_fans_polygon": { "default_value": [[-31,31],[34,31],[34,-40],[-31,-40]] },
|
||||
"gantry_height": { "value": "28" },
|
||||
"machine_max_feedrate_z": { "default_value": 12 },
|
||||
"machine_max_feedrate_e": { "default_value": 120 },
|
||||
"machine_max_acceleration_z": { "default_value": 500 },
|
||||
"machine_acceleration": { "default_value": 1000 },
|
||||
"machine_max_jerk_xy": { "default_value": 10 },
|
||||
"machine_max_jerk_z": { "default_value": 0.2 },
|
||||
"machine_max_jerk_e": { "default_value": 2.5 },
|
||||
"machine_gcode_flavor": { "default_value": "RepRap (Marlin/Sprinter)" },
|
||||
"machine_start_gcode": {
|
||||
"default_value": "G28 \nG1 Z15 F300\nM107\nG90\nM82\nM104 S215\nM140 S55\nG92 E0\nM109 S215\nM107\nM163 S0 P0.33\nM163 S1 P0.33\nM163 S2 P0.33\nM164 S4\nG0 X10 Y20 F6000\nG1 Z0.8\nG1 F300 X200 E40\nG1 F1200 Z2\nG92 E0\nG28"
|
||||
},
|
||||
"machine_end_gcode": {
|
||||
"default_value": "G91\nG1 E-1\nG0 X0 Y200\nM104 S0\nG90\nG92 E0\nM140 S0\nM84\nM104 S0\nM140 S0\nM84"
|
||||
},
|
||||
"machine_extruder_count": {
|
||||
"default_value": 3
|
||||
}
|
||||
|
||||
}
|
||||
}
|
96
resources/definitions/makeit_pro_mx.def.json
Normal file
96
resources/definitions/makeit_pro_mx.def.json
Normal file
@ -0,0 +1,96 @@
|
||||
{
|
||||
"version": 2,
|
||||
"name": "MAKEiT Pro-MX",
|
||||
"inherits": "fdmprinter",
|
||||
"metadata": {
|
||||
"visible": true,
|
||||
"author": "unknown",
|
||||
"manufacturer": "MAKEiT 3D",
|
||||
"file_formats": "text/x-gcode",
|
||||
"has_materials": false,
|
||||
"machine_extruder_trains":
|
||||
{
|
||||
"0": "makeit_mx_dual_1st",
|
||||
"1": "makeit_mx_dual_2nd"
|
||||
}
|
||||
},
|
||||
|
||||
"overrides": {
|
||||
"machine_name": { "default_value": "MAKEiT Pro-MX" },
|
||||
"machine_width": {
|
||||
"default_value": 200
|
||||
},
|
||||
"machine_height": {
|
||||
"default_value": 330
|
||||
},
|
||||
"machine_depth": {
|
||||
"default_value": 240
|
||||
},
|
||||
"machine_center_is_zero": {
|
||||
"default_value": false
|
||||
},
|
||||
"machine_head_with_fans_polygon":
|
||||
{
|
||||
"default_value": [
|
||||
[ -200, 240 ],
|
||||
[ -200, -32 ],
|
||||
[ 200, 240 ],
|
||||
[ 200, -32 ]
|
||||
]
|
||||
},
|
||||
"gantry_height": {
|
||||
"value": "200"
|
||||
},
|
||||
"machine_use_extruder_offset_to_offset_coords": {
|
||||
"default_value": true
|
||||
},
|
||||
"machine_gcode_flavor": {
|
||||
"default_value": "RepRap (Marlin/Sprinter)"
|
||||
},
|
||||
"machine_start_gcode": {
|
||||
"default_value": "G21 ;metric values\nG90 ;absolute positioning\nM82 ;set extruder to absolute mode\nG92 E0 ;zero the extruded length\nG28 ;home\nG1 F200 E30 ;extrude 30 mm of feed stock\nG92 E0 ;zero the extruded length\nG1 E-5 ;retract 5 mm\nG28 SC ;Do homeing, clean nozzles and let printer to know that printing started\nG92 X-6 ;Sets Curas checker board to match printers heated bed coordinates\nG1 F{speed_travel}\nM117 Printing..."
|
||||
},
|
||||
"machine_end_gcode": {
|
||||
"default_value": "M104 T0 S0 ;1st extruder heater off\nM104 T1 S0 ;2nd extruder heater off\nM140 S0 ;heated bed heater off (if you have it)\nG91 ;relative positioning\nG1 E-5 F9000 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\nG1 Z+5 X+20 Y+20 F9000 ;move Z up a bit\nM117 MAKEiT Pro@Done\nG28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\nM84 ;steppers off\nG90 ;absolute positioning\nM81"
|
||||
},
|
||||
"machine_extruder_count": {
|
||||
"default_value": 2
|
||||
},
|
||||
"print_sequence": {
|
||||
"enabled": false
|
||||
},
|
||||
"prime_tower_position_x": {
|
||||
"value": "185"
|
||||
},
|
||||
"prime_tower_position_y": {
|
||||
"value": "160"
|
||||
},
|
||||
"layer_height": {
|
||||
"default_value": 0.2
|
||||
},
|
||||
"retraction_speed": {
|
||||
"default_value": 180
|
||||
},
|
||||
"infill_sparse_density": {
|
||||
"default_value": 20
|
||||
},
|
||||
"retraction_amount": {
|
||||
"default_value": 6
|
||||
},
|
||||
"speed_print": {
|
||||
"default_value": 60
|
||||
},
|
||||
"wall_thickness": {
|
||||
"default_value": 1.2
|
||||
},
|
||||
"cool_min_layer_time_fan_speed_max": {
|
||||
"default_value": 5
|
||||
},
|
||||
"adhesion_type": {
|
||||
"default_value": "skirt"
|
||||
},
|
||||
"machine_heated_bed": {
|
||||
"default_value": true
|
||||
}
|
||||
}
|
||||
}
|
76
resources/definitions/mp_mini_delta.def.json
Normal file
76
resources/definitions/mp_mini_delta.def.json
Normal file
@ -0,0 +1,76 @@
|
||||
{
|
||||
"version": 2,
|
||||
"name": "MP Mini Delta",
|
||||
"inherits": "fdmprinter",
|
||||
"metadata": {
|
||||
"author": "MPMD Facebook Group",
|
||||
"manufacturer": "Monoprice",
|
||||
"category": "Other",
|
||||
"file_formats": "text/x-gcode",
|
||||
"platform": "mp_mini_delta_platform.stl",
|
||||
"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_extruder_0"
|
||||
}
|
||||
},
|
||||
|
||||
"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 "
|
||||
},
|
||||
"machine_end_gcode":
|
||||
{
|
||||
"default_value": "M107; \nM104 S0; turn off hotend heater \nM140 S0; turn off bed heater \nG91; Switch to use Relative Coordinates \nG1 E-2 F300; retract the filament a bit before lifting the nozzle to release some of the pressure \nG1 Z5 E-5 F4800; move nozzle up a bit and retract filament even more \nG28 X0; return to home positions so the nozzle is out of the way \nM84; turn off stepper motors \nG90; switch to absolute positioning \nM82; absolute extrusion mode"
|
||||
},
|
||||
"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,
|
||||
"minimum_value": 0.10,
|
||||
"maximum_value": 0.80
|
||||
},
|
||||
"layer_height": {
|
||||
"default_value": 0.14,
|
||||
"minimum_value": 0.04
|
||||
},
|
||||
"layer_height_0": {
|
||||
"default_value": 0.21,
|
||||
"minimum_value": 0.07
|
||||
},
|
||||
"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" },
|
||||
"machine_gcode_flavor": { "default_value": "RepRap (Marlin/Sprinter)" },
|
||||
"machine_max_feedrate_x": { "default_value": 150 },
|
||||
"machine_max_feedrate_y": { "default_value": 150 },
|
||||
"machine_max_feedrate_z": { "default_value": 150 },
|
||||
"machine_max_feedrate_e": { "default_value": 50 },
|
||||
"machine_max_acceleration_x": { "default_value": 800 },
|
||||
"machine_max_acceleration_y": { "default_value": 800 },
|
||||
"machine_max_acceleration_z": { "default_value": 800 },
|
||||
"machine_max_acceleration_e": { "default_value": 10000 },
|
||||
"machine_acceleration": { "default_value": 3000 },
|
||||
"machine_max_jerk_xy": { "default_value": 20 },
|
||||
"machine_max_jerk_z": { "default_value": 20 },
|
||||
"machine_max_jerk_e": { "default_value": 5},
|
||||
"retraction_amount": { "default_value": 4 },
|
||||
"retraction_speed": { "default_value": 50 },
|
||||
"retraction_hop_enabled": { "default_value": false },
|
||||
"retract_at_layer_change": { "default_value": true },
|
||||
"coasting_enable": { "default_value": true }
|
||||
}
|
||||
}
|
@ -18,10 +18,10 @@
|
||||
"default_value": "skirt"
|
||||
},
|
||||
"bottom_thickness": {
|
||||
"value": "0.5"
|
||||
"value": "0.6"
|
||||
},
|
||||
"brim_width": {
|
||||
"value": "2.0"
|
||||
"value": "3.0"
|
||||
},
|
||||
"cool_fan_enabled": {
|
||||
"value": "True"
|
||||
@ -39,19 +39,28 @@
|
||||
"value": "True"
|
||||
},
|
||||
"cool_min_layer_time": {
|
||||
"value": "5.0"
|
||||
"value": "1.0"
|
||||
},
|
||||
"cool_min_speed": {
|
||||
"value": "10.0"
|
||||
"value": "5.0"
|
||||
},
|
||||
"infill_before_walls": {
|
||||
"value": "True"
|
||||
},
|
||||
"infill_line_width": {
|
||||
"value": "0.6"
|
||||
},
|
||||
"infill_overlap": {
|
||||
"value": "15.0"
|
||||
},
|
||||
"infill_sparse_density": {
|
||||
"value": "26.0"
|
||||
},
|
||||
"ironing_enabled": {
|
||||
"value": "True"
|
||||
},
|
||||
"layer_0_z_overlap": {
|
||||
"value": "0.22"
|
||||
"value": "0.11"
|
||||
},
|
||||
"layer_height_0": {
|
||||
"value": "0.3"
|
||||
@ -60,11 +69,23 @@
|
||||
"value": "100"
|
||||
},
|
||||
"machine_end_gcode": {
|
||||
"default_value": ";End GCode\nG91 ;relative positioning\nG1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\nG1 E-5 X-20 Y-20 ;retract filament even more\nG28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\nG0 Z{machine_height} ;move the platform all the way down\nM104 S0 ;extruder heater off\nM140 S0 ;heated bed heater off (if you have it)\nM84 ;steppers off\nG90 ;absolute positioning\nM117 Done"
|
||||
"default_value": ";End GCode\nG91 ;relative positioning\nG1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\nG1 Z+0.5 E-4 F300 ;move Z up a bit and retract filament even more\nM104 S0 ;extruder heater off\nM140 S0 ;heated bed heater off (if you have it)\nG0 Z{machine_height} F1800 ;move the platform all the way down\nG28 X0 Y0 F1800 ;move X/Y to min endstops, so the head is out of the way\nM84 ;steppers off\nG90 ;absolute positioning\nM117 Done"
|
||||
},
|
||||
"machine_gcode_flavor": {
|
||||
"default_value": "RepRap (Marlin/Sprinter)"
|
||||
},
|
||||
"machine_head_with_fans_polygon":
|
||||
{
|
||||
"default_value": [
|
||||
[-26, -27],
|
||||
[38, -27],
|
||||
[38, 55],
|
||||
[-26, 55]
|
||||
]
|
||||
},
|
||||
"gantry_height": {
|
||||
"value": "8"
|
||||
},
|
||||
"machine_height": {
|
||||
"value": "100"
|
||||
},
|
||||
@ -72,7 +93,7 @@
|
||||
"default_value": "Renkforce RF100"
|
||||
},
|
||||
"machine_start_gcode": {
|
||||
"default_value": ";Start GCode\nG21 ;metric values\nG90 ;absolute positioning\nM82 ;set extruder to absolute mode\nM107 ;start with the fan off\nG28 X0 Y0 ;move X/Y to min endstops\nG28 Z0 ;move Z to min endstops\nG1 Z15.0 ;move the platform down 15mm\nG92 E0 ;zero the extruded length\nG1 F200 E3 ;extrude 3mm of feed stock\nG92 E0 ;zero the extruded length again\n;Put printing message on LCD screen\nM117 Printing..."
|
||||
"default_value": ";Sliced at: {day} {date} {time}\nG21 ;metric values\nG90 ;absolute positioning\nM82 ;set extruder to absolute mode\nM107 ;start with the fan off\nG1 Z5.0 F1800 ;move Z to 5mm\nG28 X0 Y0 F1800 ;move X/Y to min endstops\nG28 Z0 ;move Z to min endstop\nG92 E0 ;zero the extruded length\nG1 F200 E6.0 ;extrude 6.0mm of feed stock to build pressure\nG1 Z5.0 F300 ;move the platform down 5mm\nG92 E0 ;zero the extruded length again\nG1 F1800\n;Put printing message on LCD screen\nM117 Printing..."
|
||||
},
|
||||
"machine_width": {
|
||||
"value": "100"
|
||||
@ -90,7 +111,7 @@
|
||||
"value": "True"
|
||||
},
|
||||
"raft_airgap": {
|
||||
"value": "0.22"
|
||||
"value": "0.33"
|
||||
},
|
||||
"raft_base_line_spacing": {
|
||||
"value": "3.0"
|
||||
@ -111,22 +132,25 @@
|
||||
"value": "0.27"
|
||||
},
|
||||
"raft_margin": {
|
||||
"value": "5.0"
|
||||
"value": "6.0"
|
||||
},
|
||||
"raft_speed": {
|
||||
"value": "20.0"
|
||||
},
|
||||
"raft_surface_layers": {
|
||||
"value": "2.0"
|
||||
"value": "2"
|
||||
},
|
||||
"raft_surface_line_spacing": {
|
||||
"value": "3.0"
|
||||
"value": "0.4"
|
||||
},
|
||||
"raft_surface_line_width": {
|
||||
"value": "0.4"
|
||||
},
|
||||
"raft_surface_thickness": {
|
||||
"value": "0.27"
|
||||
"value": "0.1"
|
||||
},
|
||||
"retraction_amount": {
|
||||
"value": "2.0"
|
||||
"value": "5.0"
|
||||
},
|
||||
"retraction_combing": {
|
||||
"default_value": "all"
|
||||
@ -134,15 +158,9 @@
|
||||
"retraction_enable": {
|
||||
"value": "True"
|
||||
},
|
||||
"retraction_hop_enabled": {
|
||||
"value": "1.0"
|
||||
},
|
||||
"retraction_min_travel": {
|
||||
"value": "1.5"
|
||||
},
|
||||
"retraction_speed": {
|
||||
"value": "40.0"
|
||||
},
|
||||
"skin_overlap": {
|
||||
"value": "15.0"
|
||||
},
|
||||
@ -185,6 +203,9 @@
|
||||
"support_infill_rate": {
|
||||
"value": "15 if support_enable else 0 if support_tree_enable else 15"
|
||||
},
|
||||
"support_line_width": {
|
||||
"value": "0.6"
|
||||
},
|
||||
"support_pattern": {
|
||||
"default_value": "lines"
|
||||
},
|
||||
@ -192,13 +213,13 @@
|
||||
"default_value": "everywhere"
|
||||
},
|
||||
"support_xy_distance": {
|
||||
"value": "0.5"
|
||||
"value": "0.7"
|
||||
},
|
||||
"support_z_distance": {
|
||||
"value": "0.1"
|
||||
"value": "0.35"
|
||||
},
|
||||
"top_thickness": {
|
||||
"value": "0.5"
|
||||
"top_bottom_thickness": {
|
||||
"value": "0.8"
|
||||
},
|
||||
"wall_thickness": {
|
||||
"value": "0.8"
|
||||
|
228
resources/definitions/renkforce_rf100_v2.def.json
Normal file
228
resources/definitions/renkforce_rf100_v2.def.json
Normal file
@ -0,0 +1,228 @@
|
||||
{
|
||||
"version": 2,
|
||||
"name": "Renkforce RF100 V2",
|
||||
"inherits": "fdmprinter",
|
||||
"metadata": {
|
||||
"author": "Simon Peter (based on RF100.ini by Conrad Electronic SE)",
|
||||
"file_formats": "text/x-gcode",
|
||||
"manufacturer": "Renkforce",
|
||||
"visible": true,
|
||||
"machine_extruder_trains":
|
||||
{
|
||||
"0": "renkforce_rf100_extruder_0"
|
||||
}
|
||||
},
|
||||
|
||||
"overrides": {
|
||||
"adhesion_type": {
|
||||
"default_value": "skirt"
|
||||
},
|
||||
"bottom_thickness": {
|
||||
"value": "0.6"
|
||||
},
|
||||
"brim_width": {
|
||||
"value": "3.0"
|
||||
},
|
||||
"cool_fan_enabled": {
|
||||
"value": "True"
|
||||
},
|
||||
"cool_fan_full_at_height": {
|
||||
"value": "0.5"
|
||||
},
|
||||
"cool_fan_speed_max": {
|
||||
"value": "100.0"
|
||||
},
|
||||
"cool_fan_speed_min": {
|
||||
"value": "100.0"
|
||||
},
|
||||
"cool_lift_head": {
|
||||
"value": "True"
|
||||
},
|
||||
"cool_min_layer_time": {
|
||||
"value": "1.0"
|
||||
},
|
||||
"cool_min_speed": {
|
||||
"value": "5.0"
|
||||
},
|
||||
"infill_before_walls": {
|
||||
"value": "True"
|
||||
},
|
||||
"infill_line_width": {
|
||||
"value": "0.6"
|
||||
},
|
||||
"infill_overlap": {
|
||||
"value": "15.0"
|
||||
},
|
||||
"infill_sparse_density": {
|
||||
"value": "26.0"
|
||||
},
|
||||
"ironing_enabled": {
|
||||
"value": "True"
|
||||
},
|
||||
"layer_0_z_overlap": {
|
||||
"value": "0.11"
|
||||
},
|
||||
"layer_height_0": {
|
||||
"value": "0.3"
|
||||
},
|
||||
"machine_depth": {
|
||||
"value": "120"
|
||||
},
|
||||
"machine_end_gcode": {
|
||||
"default_value": ";End GCode\nG91 ;relative positioning\nG1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\nG1 Z+0.5 E-4 F300 ;move Z up a bit and retract filament even more\nM104 S0 ;extruder heater off\nM140 S0 ;heated bed heater off (if you have it)\nG0 Z{machine_height} F1800 ;move the platform all the way down\nG28 X0 Y0 F1800 ;move X/Y to min endstops, so the head is out of the way\nM84 ;steppers off\nG90 ;absolute positioning\nM117 Done"
|
||||
},
|
||||
"machine_gcode_flavor": {
|
||||
"default_value": "RepRap (Marlin/Sprinter)"
|
||||
},
|
||||
"machine_head_with_fans_polygon":
|
||||
{
|
||||
"default_value": [
|
||||
[-26, -27],
|
||||
[38, -27],
|
||||
[38, 55],
|
||||
[-26, 55]
|
||||
]
|
||||
},
|
||||
"gantry_height": {
|
||||
"value": "8"
|
||||
},
|
||||
"machine_height": {
|
||||
"value": "120"
|
||||
},
|
||||
"machine_name": {
|
||||
"default_value": "Renkforce RF100 V2"
|
||||
},
|
||||
"machine_start_gcode": {
|
||||
"default_value": ";Sliced at: {day} {date} {time}\nG21 ;metric values\nG90 ;absolute positioning\nM82 ;set extruder to absolute mode\nM107 ;start with the fan off\nG1 Z5.0 F1800 ;move Z to 5mm\nG28 X0 Y0 F1800 ;move X/Y to min endstops\nG28 Z0 ;move Z to min endstop\nG92 E0 ;zero the extruded length\nG1 F200 E6.0 ;extrude 6.0mm of feed stock to build pressure\nG1 Z5.0 F300 ;move the platform down 5mm\nG92 E0 ;zero the extruded length again\nG1 F1800\n;Put printing message on LCD screen\nM117 Printing..."
|
||||
},
|
||||
"machine_width": {
|
||||
"value": "120"
|
||||
},
|
||||
"material_bed_temperature": {
|
||||
"enabled": false
|
||||
},
|
||||
"material_flow": {
|
||||
"value": "110"
|
||||
},
|
||||
"material_print_temperature": {
|
||||
"value": "210.0"
|
||||
},
|
||||
"ooze_shield_enabled": {
|
||||
"value": "True"
|
||||
},
|
||||
"raft_airgap": {
|
||||
"value": "0.33"
|
||||
},
|
||||
"raft_base_line_spacing": {
|
||||
"value": "3.0"
|
||||
},
|
||||
"raft_base_line_width": {
|
||||
"value": "1.0"
|
||||
},
|
||||
"raft_base_thickness": {
|
||||
"value": "0.3"
|
||||
},
|
||||
"raft_interface_line_spacing": {
|
||||
"value": "3.0"
|
||||
},
|
||||
"raft_interface_line_width": {
|
||||
"value": "0.4"
|
||||
},
|
||||
"raft_interface_thickness": {
|
||||
"value": "0.27"
|
||||
},
|
||||
"raft_margin": {
|
||||
"value": "6.0"
|
||||
},
|
||||
"raft_speed": {
|
||||
"value": "20.0"
|
||||
},
|
||||
"raft_surface_layers": {
|
||||
"value": "2"
|
||||
},
|
||||
"raft_surface_line_spacing": {
|
||||
"value": "0.4"
|
||||
},
|
||||
"raft_surface_line_width": {
|
||||
"value": "0.4"
|
||||
},
|
||||
"raft_surface_thickness": {
|
||||
"value": "0.1"
|
||||
},
|
||||
"retraction_amount": {
|
||||
"value": "5.0"
|
||||
},
|
||||
"retraction_combing": {
|
||||
"default_value": "all"
|
||||
},
|
||||
"retraction_enable": {
|
||||
"value": "True"
|
||||
},
|
||||
"retraction_min_travel": {
|
||||
"value": "1.5"
|
||||
},
|
||||
"skin_overlap": {
|
||||
"value": "15.0"
|
||||
},
|
||||
"skirt_brim_minimal_length": {
|
||||
"value": "150.0"
|
||||
},
|
||||
"skirt_gap": {
|
||||
"value": "3.0"
|
||||
},
|
||||
"skirt_line_count": {
|
||||
"value": "3"
|
||||
},
|
||||
"speed_infill": {
|
||||
"value": "50.0"
|
||||
},
|
||||
"speed_layer_0": {
|
||||
"value": "15.0"
|
||||
},
|
||||
"speed_print": {
|
||||
"value": "50.0"
|
||||
},
|
||||
"speed_topbottom": {
|
||||
"value": "30.0"
|
||||
},
|
||||
"speed_travel": {
|
||||
"value": "50.0"
|
||||
},
|
||||
"speed_wall_0": {
|
||||
"value": "25.0"
|
||||
},
|
||||
"speed_wall_x": {
|
||||
"value": "35.0"
|
||||
},
|
||||
"support_angle": {
|
||||
"value": "60.0"
|
||||
},
|
||||
"support_enable": {
|
||||
"value": "False"
|
||||
},
|
||||
"support_infill_rate": {
|
||||
"value": "15 if support_enable else 0 if support_tree_enable else 15"
|
||||
},
|
||||
"support_line_width": {
|
||||
"value": "0.6"
|
||||
},
|
||||
"support_pattern": {
|
||||
"default_value": "lines"
|
||||
},
|
||||
"support_type": {
|
||||
"default_value": "everywhere"
|
||||
},
|
||||
"support_xy_distance": {
|
||||
"value": "0.7"
|
||||
},
|
||||
"support_z_distance": {
|
||||
"value": "0.35"
|
||||
},
|
||||
"top_bottom_thickness": {
|
||||
"value": "0.8"
|
||||
},
|
||||
"wall_thickness": {
|
||||
"value": "0.8"
|
||||
}
|
||||
}
|
||||
}
|
216
resources/definitions/renkforce_rf100_xl.def.json
Normal file
216
resources/definitions/renkforce_rf100_xl.def.json
Normal file
@ -0,0 +1,216 @@
|
||||
{
|
||||
"version": 2,
|
||||
"name": "Renkforce RF100 XL",
|
||||
"inherits": "fdmprinter",
|
||||
"metadata": {
|
||||
"author": "Simon Peter (based on RF100.ini by Conrad Electronic SE)",
|
||||
"file_formats": "text/x-gcode",
|
||||
"manufacturer": "Renkforce",
|
||||
"visible": true,
|
||||
"machine_extruder_trains":
|
||||
{
|
||||
"0": "renkforce_rf100_xl_extruder_0"
|
||||
}
|
||||
},
|
||||
|
||||
"overrides": {
|
||||
"adhesion_type": {
|
||||
"default_value": "skirt"
|
||||
},
|
||||
"bottom_thickness": {
|
||||
"value": "0.6"
|
||||
},
|
||||
"brim_width": {
|
||||
"value": "3.0"
|
||||
},
|
||||
"cool_fan_enabled": {
|
||||
"value": "True"
|
||||
},
|
||||
"cool_fan_full_at_height": {
|
||||
"value": "0.5"
|
||||
},
|
||||
"cool_fan_speed_max": {
|
||||
"value": "100.0"
|
||||
},
|
||||
"cool_fan_speed_min": {
|
||||
"value": "100.0"
|
||||
},
|
||||
"cool_lift_head": {
|
||||
"value": "True"
|
||||
},
|
||||
"cool_min_layer_time": {
|
||||
"value": "1.0"
|
||||
},
|
||||
"cool_min_speed": {
|
||||
"value": "5.0"
|
||||
},
|
||||
"infill_before_walls": {
|
||||
"value": "True"
|
||||
},
|
||||
"infill_line_width": {
|
||||
"value": "0.6"
|
||||
},
|
||||
"infill_overlap": {
|
||||
"value": "15.0"
|
||||
},
|
||||
"infill_sparse_density": {
|
||||
"value": "26.0"
|
||||
},
|
||||
"ironing_enabled": {
|
||||
"value": "True"
|
||||
},
|
||||
"layer_0_z_overlap": {
|
||||
"value": "0.11"
|
||||
},
|
||||
"layer_height_0": {
|
||||
"value": "0.3"
|
||||
},
|
||||
"machine_depth": {
|
||||
"value": "200"
|
||||
},
|
||||
"machine_end_gcode": {
|
||||
"default_value": ";End GCode\nG91 ;relative positioning\nG1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\nG1 Z+0.5 E-4 F300 ;move Z up a bit and retract filament even more\nM104 S0 ;extruder heater off\nM140 S0 ;heated bed heater off (if you have it)\nG0 Z{machine_height} F1800 ;move the platform all the way down\nG28 X0 Y0 F1800 ;move X/Y to min endstops, so the head is out of the way\nM84 ;steppers off\nG90 ;absolute positioning\nM117 Done"
|
||||
},
|
||||
"machine_gcode_flavor": {
|
||||
"default_value": "RepRap (Marlin/Sprinter)"
|
||||
},
|
||||
"machine_heated_bed": {
|
||||
"default_value": "true"
|
||||
},
|
||||
"machine_height": {
|
||||
"value": "200"
|
||||
},
|
||||
"machine_name": {
|
||||
"default_value": "Renkforce RF100 XL"
|
||||
},
|
||||
"machine_start_gcode": {
|
||||
"default_value": ";Sliced at: {day} {date} {time}\nG21 ;metric values\nG90 ;absolute positioning\nM82 ;set extruder to absolute mode\nM107 ;start with the fan off\nG1 Z5.0 F1800 ;move Z to 5mm\nG28 X0 Y0 F1800 ;move X/Y to min endstops\nG28 Z0 ;move Z to min endstop\nG92 E0 ;zero the extruded length\nG1 F200 E6.0 ;extrude 6.0mm of feed stock to build pressure\nG1 Z5.0 F300 ;move the platform down 5mm\nG92 E0 ;zero the extruded length again\nG1 F1800\n;Put printing message on LCD screen\nM117 Printing..."
|
||||
},
|
||||
"machine_width": {
|
||||
"value": "200"
|
||||
},
|
||||
"material_bed_temperature": {
|
||||
"value": "70"
|
||||
},
|
||||
"material_print_temperature": {
|
||||
"value": "210.0"
|
||||
},
|
||||
"ooze_shield_enabled": {
|
||||
"value": "True"
|
||||
},
|
||||
"raft_airgap": {
|
||||
"value": "0.33"
|
||||
},
|
||||
"raft_base_line_spacing": {
|
||||
"value": "3.0"
|
||||
},
|
||||
"raft_base_line_width": {
|
||||
"value": "1.0"
|
||||
},
|
||||
"raft_base_thickness": {
|
||||
"value": "0.3"
|
||||
},
|
||||
"raft_interface_line_spacing": {
|
||||
"value": "3.0"
|
||||
},
|
||||
"raft_interface_line_width": {
|
||||
"value": "0.4"
|
||||
},
|
||||
"raft_interface_thickness": {
|
||||
"value": "0.27"
|
||||
},
|
||||
"raft_margin": {
|
||||
"value": "6.0"
|
||||
},
|
||||
"raft_speed": {
|
||||
"value": "20.0"
|
||||
},
|
||||
"raft_surface_layers": {
|
||||
"value": "2"
|
||||
},
|
||||
"raft_surface_line_spacing": {
|
||||
"value": "0.4"
|
||||
},
|
||||
"raft_surface_line_width": {
|
||||
"value": "0.4"
|
||||
},
|
||||
"raft_surface_thickness": {
|
||||
"value": "0.1"
|
||||
},
|
||||
"retraction_amount": {
|
||||
"value": "5.0"
|
||||
},
|
||||
"retraction_combing": {
|
||||
"default_value": "all"
|
||||
},
|
||||
"retraction_enable": {
|
||||
"value": "True"
|
||||
},
|
||||
"retraction_min_travel": {
|
||||
"value": "1.5"
|
||||
},
|
||||
"skin_overlap": {
|
||||
"value": "15.0"
|
||||
},
|
||||
"skirt_brim_minimal_length": {
|
||||
"value": "150.0"
|
||||
},
|
||||
"skirt_gap": {
|
||||
"value": "3.0"
|
||||
},
|
||||
"skirt_line_count": {
|
||||
"value": "3"
|
||||
},
|
||||
"speed_infill": {
|
||||
"value": "50.0"
|
||||
},
|
||||
"speed_layer_0": {
|
||||
"value": "15.0"
|
||||
},
|
||||
"speed_print": {
|
||||
"value": "50.0"
|
||||
},
|
||||
"speed_topbottom": {
|
||||
"value": "30.0"
|
||||
},
|
||||
"speed_travel": {
|
||||
"value": "50.0"
|
||||
},
|
||||
"speed_wall_0": {
|
||||
"value": "25.0"
|
||||
},
|
||||
"speed_wall_x": {
|
||||
"value": "35.0"
|
||||
},
|
||||
"support_angle": {
|
||||
"value": "60.0"
|
||||
},
|
||||
"support_enable": {
|
||||
"value": "False"
|
||||
},
|
||||
"support_infill_rate": {
|
||||
"value": "15 if support_enable else 0 if support_tree_enable else 15"
|
||||
},
|
||||
"support_line_width": {
|
||||
"value": "0.6"
|
||||
},
|
||||
"support_pattern": {
|
||||
"default_value": "lines"
|
||||
},
|
||||
"support_type": {
|
||||
"default_value": "everywhere"
|
||||
},
|
||||
"support_xy_distance": {
|
||||
"value": "0.7"
|
||||
},
|
||||
"support_z_distance": {
|
||||
"value": "0.35"
|
||||
},
|
||||
"top_bottom_thickness": {
|
||||
"value": "0.8"
|
||||
},
|
||||
"wall_thickness": {
|
||||
"value": "0.8"
|
||||
}
|
||||
}
|
||||
}
|
19
resources/extruders/geeetech_A10M_1.def.json
Normal file
19
resources/extruders/geeetech_A10M_1.def.json
Normal file
@ -0,0 +1,19 @@
|
||||
{
|
||||
"version": 2,
|
||||
"name": "Extruder 1",
|
||||
"inherits": "fdmextruder",
|
||||
"metadata": {
|
||||
"machine": "geeetech_A10M",
|
||||
"position": "0"
|
||||
},
|
||||
|
||||
"overrides": {
|
||||
"extruder_nr": {
|
||||
"default_value": 0,
|
||||
"maximum_value": "1"
|
||||
},
|
||||
"machine_nozzle_size": { "default_value": 0.4 },
|
||||
"material_diameter": { "default_value": 1.75 }
|
||||
|
||||
}
|
||||
}
|
19
resources/extruders/geeetech_A10M_2.def.json
Normal file
19
resources/extruders/geeetech_A10M_2.def.json
Normal file
@ -0,0 +1,19 @@
|
||||
{
|
||||
"version": 2,
|
||||
"name": "Extruder 2",
|
||||
"inherits": "fdmextruder",
|
||||
"metadata": {
|
||||
"machine": "geeetech_A10M",
|
||||
"position": "1"
|
||||
},
|
||||
|
||||
"overrides": {
|
||||
"extruder_nr": {
|
||||
"default_value": 1,
|
||||
"maximum_value": "1"
|
||||
},
|
||||
"machine_nozzle_size": { "default_value": 0.4 },
|
||||
"material_diameter": { "default_value": 1.75 }
|
||||
|
||||
}
|
||||
}
|
19
resources/extruders/geeetech_A10T_1.def.json
Normal file
19
resources/extruders/geeetech_A10T_1.def.json
Normal file
@ -0,0 +1,19 @@
|
||||
{
|
||||
"version": 2,
|
||||
"name": "Extruder 1",
|
||||
"inherits": "fdmextruder",
|
||||
"metadata": {
|
||||
"machine": "geeetech_A10T",
|
||||
"position": "0"
|
||||
},
|
||||
|
||||
"overrides": {
|
||||
"extruder_nr": {
|
||||
"default_value": 0,
|
||||
"maximum_value": "2"
|
||||
},
|
||||
"machine_nozzle_size": { "default_value": 0.4 },
|
||||
"material_diameter": { "default_value": 1.75 }
|
||||
|
||||
}
|
||||
}
|
19
resources/extruders/geeetech_A10T_2.def.json
Normal file
19
resources/extruders/geeetech_A10T_2.def.json
Normal file
@ -0,0 +1,19 @@
|
||||
{
|
||||
"version": 2,
|
||||
"name": "Extruder 2",
|
||||
"inherits": "fdmextruder",
|
||||
"metadata": {
|
||||
"machine": "geeetech_A10T",
|
||||
"position": "1"
|
||||
},
|
||||
|
||||
"overrides": {
|
||||
"extruder_nr": {
|
||||
"default_value": 1,
|
||||
"maximum_value": "2"
|
||||
},
|
||||
"machine_nozzle_size": { "default_value": 0.4 },
|
||||
"material_diameter": { "default_value": 1.75 }
|
||||
|
||||
}
|
||||
}
|
19
resources/extruders/geeetech_A10T_3.def.json
Normal file
19
resources/extruders/geeetech_A10T_3.def.json
Normal file
@ -0,0 +1,19 @@
|
||||
{
|
||||
"version": 2,
|
||||
"name": "Extruder 3",
|
||||
"inherits": "fdmextruder",
|
||||
"metadata": {
|
||||
"machine": "geeetech_A10T",
|
||||
"position": "2"
|
||||
},
|
||||
|
||||
"overrides": {
|
||||
"extruder_nr": {
|
||||
"default_value": 2,
|
||||
"maximum_value": "2"
|
||||
},
|
||||
"machine_nozzle_size": { "default_value": 0.4 },
|
||||
"material_diameter": { "default_value": 1.75 }
|
||||
|
||||
}
|
||||
}
|
17
resources/extruders/geeetech_A10_1.def.json
Normal file
17
resources/extruders/geeetech_A10_1.def.json
Normal file
@ -0,0 +1,17 @@
|
||||
{
|
||||
|
||||
"version": 2,
|
||||
"name": "Extruder 1",
|
||||
"inherits": "fdmextruder",
|
||||
"metadata": {
|
||||
"machine": "geeetech_A10",
|
||||
"position": "0"
|
||||
},
|
||||
|
||||
"overrides": {
|
||||
"extruder_nr": { "default_value": 0 },
|
||||
"machine_nozzle_size": { "default_value": 0.4 },
|
||||
"material_diameter": { "default_value": 1.75 }
|
||||
|
||||
}
|
||||
}
|
19
resources/extruders/geeetech_A20M_1.def.json
Normal file
19
resources/extruders/geeetech_A20M_1.def.json
Normal file
@ -0,0 +1,19 @@
|
||||
{
|
||||
"version": 2,
|
||||
"name": "Extruder 1",
|
||||
"inherits": "fdmextruder",
|
||||
"metadata": {
|
||||
"machine": "geeetech_A20M",
|
||||
"position": "0"
|
||||
},
|
||||
|
||||
"overrides": {
|
||||
"extruder_nr": {
|
||||
"default_value": 0,
|
||||
"maximum_value": "1"
|
||||
},
|
||||
"machine_nozzle_size": { "default_value": 0.4 },
|
||||
"material_diameter": { "default_value": 1.75 }
|
||||
|
||||
}
|
||||
}
|
19
resources/extruders/geeetech_A20M_2.def.json
Normal file
19
resources/extruders/geeetech_A20M_2.def.json
Normal file
@ -0,0 +1,19 @@
|
||||
{
|
||||
"version": 2,
|
||||
"name": "Extruder 2",
|
||||
"inherits": "fdmextruder",
|
||||
"metadata": {
|
||||
"machine": "geeetech_A20M",
|
||||
"position": "1"
|
||||
},
|
||||
|
||||
"overrides": {
|
||||
"extruder_nr": {
|
||||
"default_value": 1,
|
||||
"maximum_value": "1"
|
||||
},
|
||||
"machine_nozzle_size": { "default_value": 0.4 },
|
||||
"material_diameter": { "default_value": 1.75 }
|
||||
|
||||
}
|
||||
}
|
19
resources/extruders/geeetech_A20T_1.def.json
Normal file
19
resources/extruders/geeetech_A20T_1.def.json
Normal file
@ -0,0 +1,19 @@
|
||||
{
|
||||
"version": 2,
|
||||
"name": "Extruder 1",
|
||||
"inherits": "fdmextruder",
|
||||
"metadata": {
|
||||
"machine": "geeetech_A20T",
|
||||
"position": "0"
|
||||
},
|
||||
|
||||
"overrides": {
|
||||
"extruder_nr": {
|
||||
"default_value": 0,
|
||||
"maximum_value": "2"
|
||||
},
|
||||
"machine_nozzle_size": { "default_value": 0.4 },
|
||||
"material_diameter": { "default_value": 1.75 }
|
||||
|
||||
}
|
||||
}
|
19
resources/extruders/geeetech_A20T_2.def.json
Normal file
19
resources/extruders/geeetech_A20T_2.def.json
Normal file
@ -0,0 +1,19 @@
|
||||
{
|
||||
"version": 2,
|
||||
"name": "Extruder 2",
|
||||
"inherits": "fdmextruder",
|
||||
"metadata": {
|
||||
"machine": "geeetech_A20T",
|
||||
"position": "1"
|
||||
},
|
||||
|
||||
"overrides": {
|
||||
"extruder_nr": {
|
||||
"default_value": 1,
|
||||
"maximum_value": "2"
|
||||
},
|
||||
"machine_nozzle_size": { "default_value": 0.4 },
|
||||
"material_diameter": { "default_value": 1.75 }
|
||||
|
||||
}
|
||||
}
|
19
resources/extruders/geeetech_A20T_3.def.json
Normal file
19
resources/extruders/geeetech_A20T_3.def.json
Normal file
@ -0,0 +1,19 @@
|
||||
{
|
||||
"version": 2,
|
||||
"name": "Extruder 3",
|
||||
"inherits": "fdmextruder",
|
||||
"metadata": {
|
||||
"machine": "geeetech_A20T",
|
||||
"position": "2"
|
||||
},
|
||||
|
||||
"overrides": {
|
||||
"extruder_nr": {
|
||||
"default_value": 2,
|
||||
"maximum_value": "2"
|
||||
},
|
||||
"machine_nozzle_size": { "default_value": 0.4 },
|
||||
"material_diameter": { "default_value": 1.75 }
|
||||
|
||||
}
|
||||
}
|
16
resources/extruders/geeetech_A20_1.def.json
Normal file
16
resources/extruders/geeetech_A20_1.def.json
Normal file
@ -0,0 +1,16 @@
|
||||
{
|
||||
"version": 2,
|
||||
"name": "Extruder 1",
|
||||
"inherits": "fdmextruder",
|
||||
"metadata": {
|
||||
"machine": "geeetech_A20",
|
||||
"position": "0"
|
||||
},
|
||||
|
||||
"overrides": {
|
||||
"extruder_nr": { "default_value": 0 },
|
||||
"machine_nozzle_size": { "default_value": 0.4 },
|
||||
"material_diameter": { "default_value": 1.75 }
|
||||
|
||||
}
|
||||
}
|
27
resources/extruders/makeit_mx_dual_1st.def.json
Normal file
27
resources/extruders/makeit_mx_dual_1st.def.json
Normal file
@ -0,0 +1,27 @@
|
||||
{
|
||||
"version": 2,
|
||||
"name": "1st Extruder",
|
||||
"inherits": "fdmextruder",
|
||||
"metadata": {
|
||||
"machine": "makeit_pro_mx",
|
||||
"position": "0"
|
||||
},
|
||||
|
||||
"overrides": {
|
||||
"extruder_nr": {
|
||||
"default_value": 0,
|
||||
"maximum_value": "1"
|
||||
},
|
||||
"machine_nozzle_offset_x": { "default_value": 0.0 },
|
||||
"machine_nozzle_offset_y": { "default_value": 0.0 },
|
||||
"machine_nozzle_size": { "default_value": 0.4 },
|
||||
"material_diameter": { "default_value": 1.75 },
|
||||
|
||||
"machine_extruder_start_pos_abs": { "default_value": true },
|
||||
"machine_extruder_start_pos_x": { "value": "prime_tower_position_x" },
|
||||
"machine_extruder_start_pos_y": { "value": "prime_tower_position_y" },
|
||||
"machine_extruder_end_pos_abs": { "default_value": true },
|
||||
"machine_extruder_end_pos_x": { "value": "prime_tower_position_x" },
|
||||
"machine_extruder_end_pos_y": { "value": "prime_tower_position_y" }
|
||||
}
|
||||
}
|
27
resources/extruders/makeit_mx_dual_2nd.def.json
Normal file
27
resources/extruders/makeit_mx_dual_2nd.def.json
Normal file
@ -0,0 +1,27 @@
|
||||
{
|
||||
"version": 2,
|
||||
"name": "2nd Extruder",
|
||||
"inherits": "fdmextruder",
|
||||
"metadata": {
|
||||
"machine": "makeit_pro_mx",
|
||||
"position": "1"
|
||||
},
|
||||
|
||||
"overrides": {
|
||||
"extruder_nr": {
|
||||
"default_value": 1,
|
||||
"maximum_value": "1"
|
||||
},
|
||||
"machine_nozzle_offset_x": { "default_value": 0.0 },
|
||||
"machine_nozzle_offset_y": { "default_value": 0.0 },
|
||||
"machine_nozzle_size": { "default_value": 0.4 },
|
||||
"material_diameter": { "default_value": 1.75 },
|
||||
|
||||
"machine_extruder_start_pos_abs": { "default_value": true },
|
||||
"machine_extruder_start_pos_x": { "value": "prime_tower_position_x" },
|
||||
"machine_extruder_start_pos_y": { "value": "prime_tower_position_y" },
|
||||
"machine_extruder_end_pos_abs": { "default_value": true },
|
||||
"machine_extruder_end_pos_x": { "value": "prime_tower_position_x" },
|
||||
"machine_extruder_end_pos_y": { "value": "prime_tower_position_y" }
|
||||
}
|
||||
}
|
15
resources/extruders/mp_mini_delta_extruder_0.def.json
Normal file
15
resources/extruders/mp_mini_delta_extruder_0.def.json
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"version": 2,
|
||||
"name": "Extruder 0",
|
||||
"inherits": "fdmextruder",
|
||||
"metadata": {
|
||||
"machine": "mp_mini_delta",
|
||||
"position": "0"
|
||||
},
|
||||
|
||||
"overrides": {
|
||||
"extruder_nr": { "default_value": 0 },
|
||||
"machine_nozzle_size": { "default_value": 0.4 },
|
||||
"material_diameter": { "default_value": 1.75 }
|
||||
}
|
||||
}
|
15
resources/extruders/renkforce_rf100_xl_extruder_0.def.json
Normal file
15
resources/extruders/renkforce_rf100_xl_extruder_0.def.json
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"version": 2,
|
||||
"name": "Extruder 1",
|
||||
"inherits": "fdmextruder",
|
||||
"metadata": {
|
||||
"machine": "renkforce_rf100_xl",
|
||||
"position": "0"
|
||||
},
|
||||
|
||||
"overrides": {
|
||||
"extruder_nr": { "default_value": 0 },
|
||||
"machine_nozzle_size": { "default_value": 0.4 },
|
||||
"material_diameter": { "default_value": 1.75 }
|
||||
}
|
||||
}
|
BIN
resources/meshes/mp_mini_delta_platform.stl
Normal file
BIN
resources/meshes/mp_mini_delta_platform.stl
Normal file
Binary file not shown.
@ -238,7 +238,7 @@ UM.MainWindow
|
||||
if (filename.toLowerCase().endsWith(".curapackage"))
|
||||
{
|
||||
// Try to install plugin & close.
|
||||
CuraApplication.getPackageManager().installPackageViaDragAndDrop(filename);
|
||||
CuraApplication.installPackageViaDragAndDrop(filename);
|
||||
packageInstallDialog.text = catalog.i18nc("@label", "This package will be installed after restarting.");
|
||||
packageInstallDialog.icon = StandardIcon.Information;
|
||||
packageInstallDialog.open();
|
||||
|
@ -127,8 +127,8 @@ Item
|
||||
icon: StandardIcon.Question
|
||||
onYes:
|
||||
{
|
||||
CuraApplication.deleteAll();
|
||||
Cura.Actions.resetProfile.trigger();
|
||||
CuraApplication.resetWorkspace()
|
||||
Cura.Actions.resetProfile.trigger()
|
||||
UM.Controller.setActiveStage("PrepareStage")
|
||||
}
|
||||
}
|
||||
|
@ -125,6 +125,7 @@ Item
|
||||
id: createMenuButton
|
||||
text: catalog.i18nc("@action:button", "Create")
|
||||
iconName: "list-add"
|
||||
enabled: Cura.MachineManager.activeMachine.hasMaterials
|
||||
onClicked:
|
||||
{
|
||||
forceActiveFocus();
|
||||
@ -174,7 +175,7 @@ Item
|
||||
forceActiveFocus();
|
||||
importMaterialDialog.open();
|
||||
}
|
||||
visible: true
|
||||
enabled: Cura.MachineManager.activeMachine.hasMaterials
|
||||
}
|
||||
|
||||
// Export button
|
||||
|
@ -202,8 +202,9 @@ Item
|
||||
// dragging a tool handle.
|
||||
Rectangle
|
||||
{
|
||||
x: -base.x + base.mouseX + UM.Theme.getSize("default_margin").width
|
||||
y: -base.y + base.mouseY + UM.Theme.getSize("default_margin").height
|
||||
id: toolInfo
|
||||
x: visible ? -base.x + base.mouseX + UM.Theme.getSize("default_margin").width: 0
|
||||
y: visible ? -base.y + base.mouseY + UM.Theme.getSize("default_margin").height: 0
|
||||
|
||||
width: toolHint.width + UM.Theme.getSize("default_margin").width
|
||||
height: toolHint.height;
|
||||
|
@ -20,7 +20,7 @@ ScrollView
|
||||
background: Rectangle // Border
|
||||
{
|
||||
color: UM.Theme.getColor("main_background")
|
||||
border.color: UM.Theme.getColor("lining")
|
||||
border.color: UM.Theme.getColor("thick_lining")
|
||||
border.width: UM.Theme.getSize("default_lining").width
|
||||
}
|
||||
|
||||
|
@ -388,6 +388,7 @@ support_conical_enabled
|
||||
support_conical_angle
|
||||
support_conical_min_width
|
||||
magic_fuzzy_skin_enabled
|
||||
magic_fuzzy_skin_outside_only
|
||||
magic_fuzzy_skin_thickness
|
||||
magic_fuzzy_skin_point_density
|
||||
magic_fuzzy_skin_point_dist
|
||||
|
@ -8,7 +8,7 @@
|
||||
"main_background": [39, 44, 48, 255],
|
||||
"message_background": [39, 44, 48, 255],
|
||||
"wide_lining": [31, 36, 39, 255],
|
||||
"thick_lining": [255, 255, 255, 30],
|
||||
"thick_lining": [255, 255, 255, 60],
|
||||
"lining": [64, 69, 72, 255],
|
||||
"viewport_overlay": [30, 36, 39, 255],
|
||||
|
||||
|
@ -149,7 +149,7 @@
|
||||
|
||||
"main_background": [255, 255, 255, 255],
|
||||
"wide_lining": [245, 245, 245, 255],
|
||||
"thick_lining": [127, 127, 127, 255],
|
||||
"thick_lining": [180, 180, 180, 255],
|
||||
"lining": [192, 193, 194, 255],
|
||||
"viewport_overlay": [246, 246, 246, 255],
|
||||
|
||||
@ -520,6 +520,7 @@
|
||||
"action_button": [15.0, 2.5],
|
||||
"action_button_icon": [1.0, 1.0],
|
||||
"action_button_radius": [0.15, 0.15],
|
||||
"dialog_primary_button_padding": [3.0, 0],
|
||||
|
||||
"radio_button": [1.3, 1.3],
|
||||
|
||||
|
65
scripts/check_invalid_imports.py
Normal file
65
scripts/check_invalid_imports.py
Normal file
@ -0,0 +1,65 @@
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
"""
|
||||
Run this file with the Cura project root as the working directory
|
||||
Checks for invalid imports. When importing from plugins, there will be no problems when running from source,
|
||||
but for some build types the plugins dir is not on the path, so relative imports should be used instead. eg:
|
||||
from ..UltimakerCloudScope import UltimakerCloudScope <-- OK
|
||||
import plugins.Toolbox.src ... <-- NOT OK
|
||||
"""
|
||||
|
||||
|
||||
class InvalidImportsChecker:
|
||||
# compile regex
|
||||
REGEX = re.compile(r"^\s*(from plugins|import plugins)")
|
||||
|
||||
def check(self):
|
||||
""" Checks for invalid imports
|
||||
|
||||
:return: True if checks passed, False when the test fails
|
||||
"""
|
||||
cwd = os.getcwd()
|
||||
cura_result = checker.check_dir(os.path.join(cwd, "cura"))
|
||||
plugins_result = checker.check_dir(os.path.join(cwd, "plugins"))
|
||||
result = cura_result and plugins_result
|
||||
if not result:
|
||||
print("error: sources contain invalid imports. Use relative imports when referencing plugin source files")
|
||||
|
||||
return result
|
||||
|
||||
def check_dir(self, root_dir: str) -> bool:
|
||||
""" Checks a directory for invalid imports
|
||||
|
||||
:return: True if checks passed, False when the test fails
|
||||
"""
|
||||
passed = True
|
||||
for path_like in Path(root_dir).rglob('*.py'):
|
||||
if not self.check_file(str(path_like)):
|
||||
passed = False
|
||||
|
||||
return passed
|
||||
|
||||
def check_file(self, file_path):
|
||||
""" Checks a file for invalid imports
|
||||
|
||||
:return: True if checks passed, False when the test fails
|
||||
"""
|
||||
passed = True
|
||||
with open(file_path, 'r', encoding = "utf-8") as inputFile:
|
||||
# loop through each line in file
|
||||
for line_i, line in enumerate(inputFile, 1):
|
||||
# check if we have a regex match
|
||||
match = self.REGEX.search(line)
|
||||
if match:
|
||||
path = os.path.relpath(file_path)
|
||||
print("{path}:{line_i}:{match}".format(path=path, line_i=line_i, match=match.group(1)))
|
||||
passed = False
|
||||
return passed
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
checker = InvalidImportsChecker()
|
||||
sys.exit(0 if checker.check() else 1)
|
Loading…
x
Reference in New Issue
Block a user