mirror of
https://github.com/Ultimaker/Cura.git
synced 2025-07-06 22:47:29 -06:00
Merge remote-tracking branch 'origin/master' into shader_optimization
This commit is contained in:
commit
dab0a43214
304 changed files with 53275 additions and 18828 deletions
|
@ -29,6 +29,7 @@ i18n_catalog = i18nCatalog("cura")
|
||||||
class Account(QObject):
|
class Account(QObject):
|
||||||
# Signal emitted when user logged in or out.
|
# Signal emitted when user logged in or out.
|
||||||
loginStateChanged = pyqtSignal(bool)
|
loginStateChanged = pyqtSignal(bool)
|
||||||
|
accessTokenChanged = pyqtSignal()
|
||||||
|
|
||||||
def __init__(self, application: "CuraApplication", parent = None) -> None:
|
def __init__(self, application: "CuraApplication", parent = None) -> None:
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
|
@ -59,8 +60,12 @@ class Account(QObject):
|
||||||
self._authorization_service.initialize(self._application.getPreferences())
|
self._authorization_service.initialize(self._application.getPreferences())
|
||||||
self._authorization_service.onAuthStateChanged.connect(self._onLoginStateChanged)
|
self._authorization_service.onAuthStateChanged.connect(self._onLoginStateChanged)
|
||||||
self._authorization_service.onAuthenticationError.connect(self._onLoginStateChanged)
|
self._authorization_service.onAuthenticationError.connect(self._onLoginStateChanged)
|
||||||
|
self._authorization_service.accessTokenChanged.connect(self._onAccessTokenChanged)
|
||||||
self._authorization_service.loadAuthDataFromPreferences()
|
self._authorization_service.loadAuthDataFromPreferences()
|
||||||
|
|
||||||
|
def _onAccessTokenChanged(self):
|
||||||
|
self.accessTokenChanged.emit()
|
||||||
|
|
||||||
## Returns a boolean indicating whether the given authentication is applied against staging or not.
|
## Returns a boolean indicating whether the given authentication is applied against staging or not.
|
||||||
@property
|
@property
|
||||||
def is_staging(self) -> bool:
|
def is_staging(self) -> bool:
|
||||||
|
@ -105,7 +110,7 @@ class Account(QObject):
|
||||||
return None
|
return None
|
||||||
return user_profile.profile_image_url
|
return user_profile.profile_image_url
|
||||||
|
|
||||||
@pyqtProperty(str, notify=loginStateChanged)
|
@pyqtProperty(str, notify=accessTokenChanged)
|
||||||
def accessToken(self) -> Optional[str]:
|
def accessToken(self) -> Optional[str]:
|
||||||
return self._authorization_service.getAccessToken()
|
return self._authorization_service.getAccessToken()
|
||||||
|
|
||||||
|
|
139
cura/API/Machines.py
Normal file
139
cura/API/Machines.py
Normal file
|
@ -0,0 +1,139 @@
|
||||||
|
# Copyright (c) 2019 Ultimaker B.V.
|
||||||
|
# Cura is released under the terms of the LGPLv3 or higher.
|
||||||
|
|
||||||
|
from typing import Optional, Dict, List, TYPE_CHECKING, Any
|
||||||
|
from PyQt5.QtCore import QObject, pyqtSignal, pyqtSlot, pyqtProperty
|
||||||
|
from UM.i18n import i18nCatalog
|
||||||
|
from UM.Logger import Logger
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from cura.CuraApplication import CuraApplication
|
||||||
|
from cura.PrinterOutput.PrinterOutputDevice import PrinterOutputDevice
|
||||||
|
|
||||||
|
i18n_catalog = i18nCatalog("cura")
|
||||||
|
|
||||||
|
## The account API provides a version-proof bridge to use Ultimaker Accounts
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# ```
|
||||||
|
# from cura.API import CuraAPI
|
||||||
|
# api = CuraAPI()
|
||||||
|
# api.machines.addOutputDeviceToCurrentMachine()
|
||||||
|
# ```
|
||||||
|
|
||||||
|
## Since Cura doesn't have a machine class, we're going to make a fake one to make our lives a
|
||||||
|
# little bit easier.
|
||||||
|
class Machine():
|
||||||
|
def __init__(self) -> None:
|
||||||
|
self.hostname = "" # type: str
|
||||||
|
self.group_id = "" # type: str
|
||||||
|
self.group_name = "" # type: str
|
||||||
|
self.um_network_key = "" # type: str
|
||||||
|
self.configuration = {} # type: Dict[str, Any]
|
||||||
|
self.connection_types = [] # type: List[int]
|
||||||
|
|
||||||
|
class Machines(QObject):
|
||||||
|
|
||||||
|
def __init__(self, application: "CuraApplication", parent = None) -> None:
|
||||||
|
super().__init__(parent)
|
||||||
|
self._application = application
|
||||||
|
|
||||||
|
@pyqtSlot(result="QVariantMap")
|
||||||
|
def getCurrentMachine(self) -> Machine:
|
||||||
|
fake_machine = Machine() # type: Machine
|
||||||
|
global_stack = self._application.getGlobalContainerStack()
|
||||||
|
if global_stack:
|
||||||
|
metadata = global_stack.getMetaData()
|
||||||
|
if "group_id" in metadata:
|
||||||
|
fake_machine.group_id = global_stack.getMetaDataEntry("group_id")
|
||||||
|
if "group_name" in metadata:
|
||||||
|
fake_machine.group_name = global_stack.getMetaDataEntry("group_name")
|
||||||
|
if "um_network_key" in metadata:
|
||||||
|
fake_machine.um_network_key = global_stack.getMetaDataEntry("um_network_key")
|
||||||
|
|
||||||
|
fake_machine.connection_types = global_stack.configuredConnectionTypes
|
||||||
|
|
||||||
|
return fake_machine
|
||||||
|
|
||||||
|
## Set the current machine's friendy name.
|
||||||
|
# This is the same as "group name" since we use "group" and "current machine" interchangeably.
|
||||||
|
# TODO: Maybe make this "friendly name" to distinguish from "hostname"?
|
||||||
|
@pyqtSlot(str)
|
||||||
|
def setCurrentMachineGroupName(self, group_name: str) -> None:
|
||||||
|
Logger.log("d", "Attempting to set the group name of the active machine to %s", group_name)
|
||||||
|
global_stack = self._application.getGlobalContainerStack()
|
||||||
|
if global_stack:
|
||||||
|
# Update a GlobalStacks in the same group with the new group name.
|
||||||
|
group_id = global_stack.getMetaDataEntry("group_id")
|
||||||
|
machine_manager = self._application.getMachineManager()
|
||||||
|
for machine in machine_manager.getMachinesInGroup(group_id):
|
||||||
|
machine.setMetaDataEntry("group_name", group_name)
|
||||||
|
|
||||||
|
# Set the default value for "hidden", which is used when you have a group with multiple types of printers
|
||||||
|
global_stack.setMetaDataEntry("hidden", False)
|
||||||
|
|
||||||
|
## Set the current machine's configuration from an (optional) output device.
|
||||||
|
# If no output device is given, the first one available on the machine will be used.
|
||||||
|
# NOTE: Group and machine are used interchangeably.
|
||||||
|
# NOTE: This doesn't seem to be used anywhere. Maybe delete?
|
||||||
|
@pyqtSlot(QObject)
|
||||||
|
def updateCurrentMachineConfiguration(self, output_device: Optional["PrinterOutputDevice"]) -> None:
|
||||||
|
|
||||||
|
if output_device is None:
|
||||||
|
machine_manager = self._application.getMachineManager()
|
||||||
|
output_device = machine_manager.printerOutputDevices[0]
|
||||||
|
|
||||||
|
hotend_ids = output_device.hotendIds
|
||||||
|
for index in range(len(hotend_ids)):
|
||||||
|
output_device.hotendIdChanged.emit(index, hotend_ids[index])
|
||||||
|
|
||||||
|
material_ids = output_device.materialIds
|
||||||
|
for index in range(len(material_ids)):
|
||||||
|
output_device.materialIdChanged.emit(index, material_ids[index])
|
||||||
|
|
||||||
|
## Add an output device to the current machine.
|
||||||
|
# In practice, this means:
|
||||||
|
# - Setting the output device's network key in the current machine's metadata
|
||||||
|
# - Adding the output device's connection type to the current machine's configured connection
|
||||||
|
# types.
|
||||||
|
# TODO: CHANGE TO HOSTNAME
|
||||||
|
@pyqtSlot(QObject)
|
||||||
|
def addOutputDeviceToCurrentMachine(self, output_device: "PrinterOutputDevice") -> None:
|
||||||
|
if not output_device:
|
||||||
|
return
|
||||||
|
Logger.log("d",
|
||||||
|
"Attempting to set the network key of the active machine to %s",
|
||||||
|
output_device.key)
|
||||||
|
global_stack = self._application.getGlobalContainerStack()
|
||||||
|
if not global_stack:
|
||||||
|
return
|
||||||
|
metadata = global_stack.getMetaData()
|
||||||
|
|
||||||
|
# Global stack already had a connection, but it's changed.
|
||||||
|
if "um_network_key" in metadata:
|
||||||
|
old_network_key = metadata["um_network_key"]
|
||||||
|
|
||||||
|
# Since we might have a bunch of hidden stacks, we also need to change it there.
|
||||||
|
metadata_filter = {"um_network_key": old_network_key}
|
||||||
|
containers = self._application.getContainerRegistry().findContainerStacks(
|
||||||
|
type = "machine", **metadata_filter)
|
||||||
|
for container in containers:
|
||||||
|
container.setMetaDataEntry("um_network_key", output_device.key)
|
||||||
|
|
||||||
|
# Delete old authentication data.
|
||||||
|
Logger.log("d", "Removing old authentication id %s for device %s",
|
||||||
|
global_stack.getMetaDataEntry("network_authentication_id", None),
|
||||||
|
output_device.key)
|
||||||
|
|
||||||
|
container.removeMetaDataEntry("network_authentication_id")
|
||||||
|
container.removeMetaDataEntry("network_authentication_key")
|
||||||
|
|
||||||
|
# Ensure that these containers do know that they are configured for the given
|
||||||
|
# connection type (can be more than one type; e.g. LAN & Cloud)
|
||||||
|
container.addConfiguredConnectionType(output_device.connectionType.value)
|
||||||
|
|
||||||
|
else: # Global stack didn't have a connection yet, configure it.
|
||||||
|
global_stack.setMetaDataEntry("um_network_key", output_device.key)
|
||||||
|
global_stack.addConfiguredConnectionType(output_device.connectionType.value)
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
|
@ -6,6 +6,7 @@ from PyQt5.QtCore import QObject, pyqtProperty
|
||||||
|
|
||||||
from cura.API.Backups import Backups
|
from cura.API.Backups import Backups
|
||||||
from cura.API.Interface import Interface
|
from cura.API.Interface import Interface
|
||||||
|
from cura.API.Machines import Machines
|
||||||
from cura.API.Account import Account
|
from cura.API.Account import Account
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
|
@ -44,6 +45,9 @@ class CuraAPI(QObject):
|
||||||
# Backups API
|
# Backups API
|
||||||
self._backups = Backups(self._application)
|
self._backups = Backups(self._application)
|
||||||
|
|
||||||
|
# Machines API
|
||||||
|
self._machines = Machines(self._application)
|
||||||
|
|
||||||
# Interface API
|
# Interface API
|
||||||
self._interface = Interface(self._application)
|
self._interface = Interface(self._application)
|
||||||
|
|
||||||
|
@ -58,6 +62,10 @@ class CuraAPI(QObject):
|
||||||
def backups(self) -> "Backups":
|
def backups(self) -> "Backups":
|
||||||
return self._backups
|
return self._backups
|
||||||
|
|
||||||
|
@pyqtProperty(QObject)
|
||||||
|
def machines(self) -> "Machines":
|
||||||
|
return self._machines
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def interface(self) -> "Interface":
|
def interface(self) -> "Interface":
|
||||||
return self._interface
|
return self._interface
|
||||||
|
|
|
@ -19,6 +19,7 @@ class AutoSave:
|
||||||
self._change_timer.setInterval(self._application.getPreferences().getValue("cura/autosave_delay"))
|
self._change_timer.setInterval(self._application.getPreferences().getValue("cura/autosave_delay"))
|
||||||
self._change_timer.setSingleShot(True)
|
self._change_timer.setSingleShot(True)
|
||||||
|
|
||||||
|
self._enabled = True
|
||||||
self._saving = False
|
self._saving = False
|
||||||
|
|
||||||
def initialize(self):
|
def initialize(self):
|
||||||
|
@ -32,6 +33,13 @@ class AutoSave:
|
||||||
if not self._saving:
|
if not self._saving:
|
||||||
self._change_timer.start()
|
self._change_timer.start()
|
||||||
|
|
||||||
|
def setEnabled(self, enabled: bool) -> None:
|
||||||
|
self._enabled = enabled
|
||||||
|
if self._enabled:
|
||||||
|
self._change_timer.start()
|
||||||
|
else:
|
||||||
|
self._change_timer.stop()
|
||||||
|
|
||||||
def _onGlobalStackChanged(self):
|
def _onGlobalStackChanged(self):
|
||||||
if self._global_stack:
|
if self._global_stack:
|
||||||
self._global_stack.propertyChanged.disconnect(self._triggerTimer)
|
self._global_stack.propertyChanged.disconnect(self._triggerTimer)
|
||||||
|
|
|
@ -51,8 +51,8 @@ class BackupsManager:
|
||||||
## Here we try to disable the auto-save plug-in as it might interfere with
|
## Here we try to disable the auto-save plug-in as it might interfere with
|
||||||
# restoring a back-up.
|
# restoring a back-up.
|
||||||
def _disableAutoSave(self) -> None:
|
def _disableAutoSave(self) -> None:
|
||||||
self._application.setSaveDataEnabled(False)
|
self._application.getAutoSave().setEnabled(False)
|
||||||
|
|
||||||
## Re-enable auto-save after we're done.
|
## Re-enable auto-save after we're done.
|
||||||
def _enableAutoSave(self) -> None:
|
def _enableAutoSave(self) -> None:
|
||||||
self._application.setSaveDataEnabled(True)
|
self._application.getAutoSave().setEnabled(True)
|
||||||
|
|
|
@ -23,6 +23,7 @@ from UM.Platform import Platform
|
||||||
from UM.PluginError import PluginNotFoundError
|
from UM.PluginError import PluginNotFoundError
|
||||||
from UM.Resources import Resources
|
from UM.Resources import Resources
|
||||||
from UM.Preferences import Preferences
|
from UM.Preferences import Preferences
|
||||||
|
from UM.Qt.Bindings import MainWindow
|
||||||
from UM.Qt.QtApplication import QtApplication # The class we're inheriting from.
|
from UM.Qt.QtApplication import QtApplication # The class we're inheriting from.
|
||||||
import UM.Util
|
import UM.Util
|
||||||
from UM.View.SelectionPass import SelectionPass # For typing.
|
from UM.View.SelectionPass import SelectionPass # For typing.
|
||||||
|
@ -116,6 +117,8 @@ from cura.UI.AddPrinterPagesModel import AddPrinterPagesModel
|
||||||
from cura.UI.WelcomePagesModel import WelcomePagesModel
|
from cura.UI.WelcomePagesModel import WelcomePagesModel
|
||||||
from cura.UI.WhatsNewPagesModel import WhatsNewPagesModel
|
from cura.UI.WhatsNewPagesModel import WhatsNewPagesModel
|
||||||
|
|
||||||
|
from cura.Utils.NetworkingUtil import NetworkingUtil
|
||||||
|
|
||||||
from .SingleInstance import SingleInstance
|
from .SingleInstance import SingleInstance
|
||||||
from .AutoSave import AutoSave
|
from .AutoSave import AutoSave
|
||||||
from . import PlatformPhysics
|
from . import PlatformPhysics
|
||||||
|
@ -216,7 +219,7 @@ class CuraApplication(QtApplication):
|
||||||
|
|
||||||
self._machine_settings_manager = MachineSettingsManager(self, parent = self)
|
self._machine_settings_manager = MachineSettingsManager(self, parent = self)
|
||||||
|
|
||||||
self._discovered_printer_model = DiscoveredPrintersModel(parent = self)
|
self._discovered_printer_model = DiscoveredPrintersModel(self, parent = self)
|
||||||
self._first_start_machine_actions_model = FirstStartMachineActionsModel(self, parent = self)
|
self._first_start_machine_actions_model = FirstStartMachineActionsModel(self, parent = self)
|
||||||
self._welcome_pages_model = WelcomePagesModel(self, parent = self)
|
self._welcome_pages_model = WelcomePagesModel(self, parent = self)
|
||||||
self._add_printer_pages_model = AddPrinterPagesModel(self, parent = self)
|
self._add_printer_pages_model = AddPrinterPagesModel(self, parent = self)
|
||||||
|
@ -258,7 +261,6 @@ class CuraApplication(QtApplication):
|
||||||
|
|
||||||
# Backups
|
# Backups
|
||||||
self._auto_save = None
|
self._auto_save = None
|
||||||
self._save_data_enabled = True
|
|
||||||
|
|
||||||
from cura.Settings.CuraContainerRegistry import CuraContainerRegistry
|
from cura.Settings.CuraContainerRegistry import CuraContainerRegistry
|
||||||
self._container_registry_class = CuraContainerRegistry
|
self._container_registry_class = CuraContainerRegistry
|
||||||
|
@ -523,6 +525,10 @@ class CuraApplication(QtApplication):
|
||||||
preferences.addPreference("cura/use_multi_build_plate", False)
|
preferences.addPreference("cura/use_multi_build_plate", False)
|
||||||
preferences.addPreference("view/settings_list_height", 400)
|
preferences.addPreference("view/settings_list_height", 400)
|
||||||
preferences.addPreference("view/settings_visible", False)
|
preferences.addPreference("view/settings_visible", False)
|
||||||
|
preferences.addPreference("view/settings_xpos", 0)
|
||||||
|
preferences.addPreference("view/settings_ypos", 56)
|
||||||
|
preferences.addPreference("view/colorscheme_xpos", 0)
|
||||||
|
preferences.addPreference("view/colorscheme_ypos", 56)
|
||||||
preferences.addPreference("cura/currency", "€")
|
preferences.addPreference("cura/currency", "€")
|
||||||
preferences.addPreference("cura/material_settings", "{}")
|
preferences.addPreference("cura/material_settings", "{}")
|
||||||
|
|
||||||
|
@ -668,13 +674,10 @@ class CuraApplication(QtApplication):
|
||||||
self._message_box_callback(button, *self._message_box_callback_arguments)
|
self._message_box_callback(button, *self._message_box_callback_arguments)
|
||||||
self._message_box_callback = None
|
self._message_box_callback = None
|
||||||
self._message_box_callback_arguments = []
|
self._message_box_callback_arguments = []
|
||||||
|
|
||||||
def setSaveDataEnabled(self, enabled: bool) -> None:
|
|
||||||
self._save_data_enabled = enabled
|
|
||||||
|
|
||||||
# Cura has multiple locations where instance containers need to be saved, so we need to handle this differently.
|
# Cura has multiple locations where instance containers need to be saved, so we need to handle this differently.
|
||||||
def saveSettings(self):
|
def saveSettings(self):
|
||||||
if not self.started or not self._save_data_enabled:
|
if not self.started:
|
||||||
# Do not do saving during application start or when data should not be saved on quit.
|
# Do not do saving during application start or when data should not be saved on quit.
|
||||||
return
|
return
|
||||||
ContainerRegistry.getInstance().saveDirtyContainers()
|
ContainerRegistry.getInstance().saveDirtyContainers()
|
||||||
|
@ -1027,6 +1030,8 @@ class CuraApplication(QtApplication):
|
||||||
qmlRegisterSingletonType(SimpleModeSettingsManager, "Cura", 1, 0, "SimpleModeSettingsManager", self.getSimpleModeSettingsManager)
|
qmlRegisterSingletonType(SimpleModeSettingsManager, "Cura", 1, 0, "SimpleModeSettingsManager", self.getSimpleModeSettingsManager)
|
||||||
qmlRegisterSingletonType(MachineActionManager.MachineActionManager, "Cura", 1, 0, "MachineActionManager", self.getMachineActionManager)
|
qmlRegisterSingletonType(MachineActionManager.MachineActionManager, "Cura", 1, 0, "MachineActionManager", self.getMachineActionManager)
|
||||||
|
|
||||||
|
qmlRegisterType(NetworkingUtil, "Cura", 1, 5, "NetworkingUtil")
|
||||||
|
|
||||||
qmlRegisterType(WelcomePagesModel, "Cura", 1, 0, "WelcomePagesModel")
|
qmlRegisterType(WelcomePagesModel, "Cura", 1, 0, "WelcomePagesModel")
|
||||||
qmlRegisterType(WhatsNewPagesModel, "Cura", 1, 0, "WhatsNewPagesModel")
|
qmlRegisterType(WhatsNewPagesModel, "Cura", 1, 0, "WhatsNewPagesModel")
|
||||||
qmlRegisterType(AddPrinterPagesModel, "Cura", 1, 0, "AddPrinterPagesModel")
|
qmlRegisterType(AddPrinterPagesModel, "Cura", 1, 0, "AddPrinterPagesModel")
|
||||||
|
@ -1786,3 +1791,19 @@ class CuraApplication(QtApplication):
|
||||||
# Only show the what's new dialog if there's no machine and we have just upgraded
|
# Only show the what's new dialog if there's no machine and we have just upgraded
|
||||||
show_whatsnew_only = has_active_machine and has_app_just_upgraded
|
show_whatsnew_only = has_active_machine and has_app_just_upgraded
|
||||||
return show_whatsnew_only
|
return show_whatsnew_only
|
||||||
|
|
||||||
|
@pyqtSlot(result = int)
|
||||||
|
def appWidth(self) -> int:
|
||||||
|
main_window = QtApplication.getInstance().getMainWindow()
|
||||||
|
if main_window:
|
||||||
|
return main_window.width()
|
||||||
|
else:
|
||||||
|
return 0
|
||||||
|
|
||||||
|
@pyqtSlot(result = int)
|
||||||
|
def appHeight(self) -> int:
|
||||||
|
main_window = QtApplication.getInstance().getMainWindow()
|
||||||
|
if main_window:
|
||||||
|
return main_window.height()
|
||||||
|
else:
|
||||||
|
return 0
|
||||||
|
|
|
@ -3,8 +3,11 @@
|
||||||
|
|
||||||
from PyQt5.QtCore import pyqtProperty, QUrl
|
from PyQt5.QtCore import pyqtProperty, QUrl
|
||||||
|
|
||||||
|
from UM.Resources import Resources
|
||||||
from UM.View.View import View
|
from UM.View.View import View
|
||||||
|
|
||||||
|
from cura.CuraApplication import CuraApplication
|
||||||
|
|
||||||
|
|
||||||
# Since Cura has a few pre-defined "space claims" for the locations of certain components, we've provided some structure
|
# Since Cura has a few pre-defined "space claims" for the locations of certain components, we've provided some structure
|
||||||
# to indicate this.
|
# to indicate this.
|
||||||
|
@ -12,13 +15,20 @@ from UM.View.View import View
|
||||||
# the stageMenuComponent returns an item that should be used somehwere in the stage menu. It's up to the active stage
|
# the stageMenuComponent returns an item that should be used somehwere in the stage menu. It's up to the active stage
|
||||||
# to actually do something with this.
|
# to actually do something with this.
|
||||||
class CuraView(View):
|
class CuraView(View):
|
||||||
def __init__(self, parent = None) -> None:
|
def __init__(self, parent = None, use_empty_menu_placeholder: bool = False) -> None:
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
|
|
||||||
|
self._empty_menu_placeholder_url = QUrl(Resources.getPath(CuraApplication.ResourceTypes.QmlFiles,
|
||||||
|
"EmptyViewMenuComponent.qml"))
|
||||||
|
self._use_empty_menu_placeholder = use_empty_menu_placeholder
|
||||||
|
|
||||||
@pyqtProperty(QUrl, constant = True)
|
@pyqtProperty(QUrl, constant = True)
|
||||||
def mainComponent(self) -> QUrl:
|
def mainComponent(self) -> QUrl:
|
||||||
return self.getDisplayComponent("main")
|
return self.getDisplayComponent("main")
|
||||||
|
|
||||||
@pyqtProperty(QUrl, constant = True)
|
@pyqtProperty(QUrl, constant = True)
|
||||||
def stageMenuComponent(self) -> QUrl:
|
def stageMenuComponent(self) -> QUrl:
|
||||||
return self.getDisplayComponent("menu")
|
url = self.getDisplayComponent("menu")
|
||||||
|
if not url.toString() and self._use_empty_menu_placeholder:
|
||||||
|
url = self._empty_menu_placeholder_url
|
||||||
|
return url
|
||||||
|
|
|
@ -20,7 +20,7 @@ class LayerPolygon:
|
||||||
MoveCombingType = 8
|
MoveCombingType = 8
|
||||||
MoveRetractionType = 9
|
MoveRetractionType = 9
|
||||||
SupportInterfaceType = 10
|
SupportInterfaceType = 10
|
||||||
PrimeTower = 11
|
PrimeTowerType = 11
|
||||||
__number_of_types = 12
|
__number_of_types = 12
|
||||||
|
|
||||||
__jump_map = numpy.logical_or(numpy.logical_or(numpy.arange(__number_of_types) == NoneType, numpy.arange(__number_of_types) == MoveCombingType), numpy.arange(__number_of_types) == MoveRetractionType)
|
__jump_map = numpy.logical_or(numpy.logical_or(numpy.arange(__number_of_types) == NoneType, numpy.arange(__number_of_types) == MoveCombingType), numpy.arange(__number_of_types) == MoveRetractionType)
|
||||||
|
@ -245,7 +245,7 @@ class LayerPolygon:
|
||||||
theme.getColor("layerview_move_combing").getRgbF(), # MoveCombingType
|
theme.getColor("layerview_move_combing").getRgbF(), # MoveCombingType
|
||||||
theme.getColor("layerview_move_retraction").getRgbF(), # MoveRetractionType
|
theme.getColor("layerview_move_retraction").getRgbF(), # MoveRetractionType
|
||||||
theme.getColor("layerview_support_interface").getRgbF(), # SupportInterfaceType
|
theme.getColor("layerview_support_interface").getRgbF(), # SupportInterfaceType
|
||||||
theme.getColor("layerview_prime_tower").getRgbF()
|
theme.getColor("layerview_prime_tower").getRgbF() # PrimeTowerType
|
||||||
])
|
])
|
||||||
|
|
||||||
return cls.__color_map
|
return cls.__color_map
|
||||||
|
|
|
@ -2,8 +2,9 @@
|
||||||
# Cura is released under the terms of the LGPLv3 or higher.
|
# Cura is released under the terms of the LGPLv3 or higher.
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
from PyQt5.QtCore import QObject, pyqtSlot, pyqtProperty, pyqtSignal
|
from PyQt5.QtCore import QObject, QUrl, pyqtSlot, pyqtProperty, pyqtSignal
|
||||||
|
|
||||||
from UM.Logger import Logger
|
from UM.Logger import Logger
|
||||||
from UM.PluginObject import PluginObject
|
from UM.PluginObject import PluginObject
|
||||||
|
@ -72,18 +73,26 @@ class MachineAction(QObject, PluginObject):
|
||||||
return self._finished
|
return self._finished
|
||||||
|
|
||||||
## Protected helper to create a view object based on provided QML.
|
## Protected helper to create a view object based on provided QML.
|
||||||
def _createViewFromQML(self) -> None:
|
def _createViewFromQML(self) -> Optional["QObject"]:
|
||||||
plugin_path = PluginRegistry.getInstance().getPluginPath(self.getPluginId())
|
plugin_path = PluginRegistry.getInstance().getPluginPath(self.getPluginId())
|
||||||
if plugin_path is None:
|
if plugin_path is None:
|
||||||
Logger.log("e", "Cannot create QML view: cannot find plugin path for plugin [%s]", self.getPluginId())
|
Logger.log("e", "Cannot create QML view: cannot find plugin path for plugin [%s]", self.getPluginId())
|
||||||
return
|
return None
|
||||||
path = os.path.join(plugin_path, self._qml_url)
|
path = os.path.join(plugin_path, self._qml_url)
|
||||||
|
|
||||||
from cura.CuraApplication import CuraApplication
|
from cura.CuraApplication import CuraApplication
|
||||||
self._view = CuraApplication.getInstance().createQmlComponent(path, {"manager": self})
|
view = CuraApplication.getInstance().createQmlComponent(path, {"manager": self})
|
||||||
|
return view
|
||||||
|
|
||||||
@pyqtProperty(QObject, constant = True)
|
@pyqtProperty(QUrl, constant = True)
|
||||||
def displayItem(self):
|
def qmlPath(self) -> "QUrl":
|
||||||
if not self._view:
|
plugin_path = PluginRegistry.getInstance().getPluginPath(self.getPluginId())
|
||||||
self._createViewFromQML()
|
if plugin_path is None:
|
||||||
return self._view
|
Logger.log("e", "Cannot create QML view: cannot find plugin path for plugin [%s]", self.getPluginId())
|
||||||
|
return QUrl("")
|
||||||
|
path = os.path.join(plugin_path, self._qml_url)
|
||||||
|
return QUrl.fromLocalFile(path)
|
||||||
|
|
||||||
|
@pyqtSlot(result = QObject)
|
||||||
|
def getDisplayItem(self) -> Optional["QObject"]:
|
||||||
|
return self._createViewFromQML()
|
||||||
|
|
|
@ -103,6 +103,8 @@ class MaterialManager(QObject):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
root_material_id = material_metadata.get("base_file", "")
|
root_material_id = material_metadata.get("base_file", "")
|
||||||
|
if root_material_id not in material_metadatas: #Not a registered material profile. Don't store this in the look-up tables.
|
||||||
|
continue
|
||||||
if root_material_id not in self._material_group_map:
|
if root_material_id not in self._material_group_map:
|
||||||
self._material_group_map[root_material_id] = MaterialGroup(root_material_id, MaterialNode(material_metadatas[root_material_id]))
|
self._material_group_map[root_material_id] = MaterialGroup(root_material_id, MaterialNode(material_metadatas[root_material_id]))
|
||||||
self._material_group_map[root_material_id].is_read_only = self._container_registry.isReadOnly(root_material_id)
|
self._material_group_map[root_material_id].is_read_only = self._container_registry.isReadOnly(root_material_id)
|
||||||
|
|
|
@ -3,15 +3,17 @@
|
||||||
|
|
||||||
from typing import Callable, Dict, List, Optional, TYPE_CHECKING
|
from typing import Callable, Dict, List, Optional, TYPE_CHECKING
|
||||||
|
|
||||||
from PyQt5.QtCore import pyqtSlot, pyqtProperty, pyqtSignal, QObject
|
from PyQt5.QtCore import pyqtSlot, pyqtProperty, pyqtSignal, QObject, QTimer
|
||||||
|
|
||||||
from UM.i18n import i18nCatalog
|
from UM.i18n import i18nCatalog
|
||||||
from UM.Logger import Logger
|
from UM.Logger import Logger
|
||||||
from UM.Util import parseBool
|
from UM.Util import parseBool
|
||||||
|
from UM.OutputDevice.OutputDeviceManager import ManualDeviceAdditionAttempt
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from PyQt5.QtCore import QObject
|
from PyQt5.QtCore import QObject
|
||||||
|
from UM.OutputDevice.OutputDevicePlugin import OutputDevicePlugin
|
||||||
|
from cura.CuraApplication import CuraApplication
|
||||||
from cura.PrinterOutput.NetworkedPrinterOutputDevice import NetworkedPrinterOutputDevice
|
from cura.PrinterOutput.NetworkedPrinterOutputDevice import NetworkedPrinterOutputDevice
|
||||||
|
|
||||||
|
|
||||||
|
@ -45,6 +47,10 @@ class DiscoveredPrinter(QObject):
|
||||||
self._name = name
|
self._name = name
|
||||||
self.nameChanged.emit()
|
self.nameChanged.emit()
|
||||||
|
|
||||||
|
@pyqtProperty(str, constant = True)
|
||||||
|
def address(self) -> str:
|
||||||
|
return self._ip_address
|
||||||
|
|
||||||
machineTypeChanged = pyqtSignal()
|
machineTypeChanged = pyqtSignal()
|
||||||
|
|
||||||
@pyqtProperty(str, notify = machineTypeChanged)
|
@pyqtProperty(str, notify = machineTypeChanged)
|
||||||
|
@ -60,15 +66,26 @@ class DiscoveredPrinter(QObject):
|
||||||
@pyqtProperty(str, notify = machineTypeChanged)
|
@pyqtProperty(str, notify = machineTypeChanged)
|
||||||
def readableMachineType(self) -> str:
|
def readableMachineType(self) -> str:
|
||||||
from cura.CuraApplication import CuraApplication
|
from cura.CuraApplication import CuraApplication
|
||||||
readable_type = CuraApplication.getInstance().getMachineManager().getMachineTypeNameFromId(self._machine_type)
|
machine_manager = CuraApplication.getInstance().getMachineManager()
|
||||||
if not readable_type:
|
# In ClusterUM3OutputDevice, when it updates a printer information, it updates the machine type using the field
|
||||||
readable_type = catalog.i18nc("@label", "Unknown")
|
# "machine_variant", and for some reason, it's not the machine type ID/codename/... but a human-readable string
|
||||||
|
# like "Ultimaker 3". The code below handles this case.
|
||||||
|
if machine_manager.hasHumanReadableMachineTypeName(self._machine_type):
|
||||||
|
readable_type = self._machine_type
|
||||||
|
else:
|
||||||
|
readable_type = machine_manager.getMachineTypeNameFromId(self._machine_type)
|
||||||
|
if not readable_type:
|
||||||
|
readable_type = catalog.i18nc("@label", "Unknown")
|
||||||
return readable_type
|
return readable_type
|
||||||
|
|
||||||
@pyqtProperty(bool, notify = machineTypeChanged)
|
@pyqtProperty(bool, notify = machineTypeChanged)
|
||||||
def isUnknownMachineType(self) -> bool:
|
def isUnknownMachineType(self) -> bool:
|
||||||
from cura.CuraApplication import CuraApplication
|
from cura.CuraApplication import CuraApplication
|
||||||
readable_type = CuraApplication.getInstance().getMachineManager().getMachineTypeNameFromId(self._machine_type)
|
machine_manager = CuraApplication.getInstance().getMachineManager()
|
||||||
|
if machine_manager.hasHumanReadableMachineTypeName(self._machine_type):
|
||||||
|
readable_type = self._machine_type
|
||||||
|
else:
|
||||||
|
readable_type = machine_manager.getMachineTypeNameFromId(self._machine_type)
|
||||||
return not readable_type
|
return not readable_type
|
||||||
|
|
||||||
@pyqtProperty(QObject, constant = True)
|
@pyqtProperty(QObject, constant = True)
|
||||||
|
@ -94,14 +111,88 @@ class DiscoveredPrinter(QObject):
|
||||||
#
|
#
|
||||||
class DiscoveredPrintersModel(QObject):
|
class DiscoveredPrintersModel(QObject):
|
||||||
|
|
||||||
def __init__(self, parent: Optional["QObject"] = None) -> None:
|
def __init__(self, application: "CuraApplication", parent: Optional["QObject"] = None) -> None:
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
|
|
||||||
|
self._application = application
|
||||||
self._discovered_printer_by_ip_dict = dict() # type: Dict[str, DiscoveredPrinter]
|
self._discovered_printer_by_ip_dict = dict() # type: Dict[str, DiscoveredPrinter]
|
||||||
|
|
||||||
|
self._plugin_for_manual_device = None # type: Optional[OutputDevicePlugin]
|
||||||
|
self._manual_device_address = ""
|
||||||
|
|
||||||
|
self._manual_device_request_timeout_in_seconds = 5 # timeout for adding a manual device in seconds
|
||||||
|
self._manual_device_request_timer = QTimer()
|
||||||
|
self._manual_device_request_timer.setInterval(self._manual_device_request_timeout_in_seconds * 1000)
|
||||||
|
self._manual_device_request_timer.setSingleShot(True)
|
||||||
|
self._manual_device_request_timer.timeout.connect(self._onManualRequestTimeout)
|
||||||
|
|
||||||
discoveredPrintersChanged = pyqtSignal()
|
discoveredPrintersChanged = pyqtSignal()
|
||||||
|
|
||||||
@pyqtProperty(list, notify = discoveredPrintersChanged)
|
@pyqtSlot(str)
|
||||||
|
def checkManualDevice(self, address: str) -> None:
|
||||||
|
if self.hasManualDeviceRequestInProgress:
|
||||||
|
Logger.log("i", "A manual device request for address [%s] is still in progress, do nothing",
|
||||||
|
self._manual_device_address)
|
||||||
|
return
|
||||||
|
|
||||||
|
priority_order = [
|
||||||
|
ManualDeviceAdditionAttempt.PRIORITY,
|
||||||
|
ManualDeviceAdditionAttempt.POSSIBLE,
|
||||||
|
] # type: List[ManualDeviceAdditionAttempt]
|
||||||
|
|
||||||
|
all_plugins_dict = self._application.getOutputDeviceManager().getAllOutputDevicePlugins()
|
||||||
|
|
||||||
|
can_add_manual_plugins = [item for item in filter(
|
||||||
|
lambda plugin_item: plugin_item.canAddManualDevice(address) in priority_order,
|
||||||
|
all_plugins_dict.values())]
|
||||||
|
|
||||||
|
if not can_add_manual_plugins:
|
||||||
|
Logger.log("d", "Could not find a plugin to accept adding %s manually via address.", address)
|
||||||
|
return
|
||||||
|
|
||||||
|
plugin = max(can_add_manual_plugins, key = lambda p: priority_order.index(p.canAddManualDevice(address)))
|
||||||
|
self._plugin_for_manual_device = plugin
|
||||||
|
self._plugin_for_manual_device.addManualDevice(address, callback = self._onManualDeviceRequestFinished)
|
||||||
|
self._manual_device_address = address
|
||||||
|
self._manual_device_request_timer.start()
|
||||||
|
self.hasManualDeviceRequestInProgressChanged.emit()
|
||||||
|
|
||||||
|
@pyqtSlot()
|
||||||
|
def cancelCurrentManualDeviceRequest(self) -> None:
|
||||||
|
self._manual_device_request_timer.stop()
|
||||||
|
|
||||||
|
if self._manual_device_address:
|
||||||
|
if self._plugin_for_manual_device is not None:
|
||||||
|
self._plugin_for_manual_device.removeManualDevice(self._manual_device_address, address = self._manual_device_address)
|
||||||
|
self._manual_device_address = ""
|
||||||
|
self._plugin_for_manual_device = None
|
||||||
|
self.hasManualDeviceRequestInProgressChanged.emit()
|
||||||
|
self.manualDeviceRequestFinished.emit(False)
|
||||||
|
|
||||||
|
def _onManualRequestTimeout(self) -> None:
|
||||||
|
Logger.log("w", "Manual printer [%s] request timed out. Cancel the current request.", self._manual_device_address)
|
||||||
|
self.cancelCurrentManualDeviceRequest()
|
||||||
|
|
||||||
|
hasManualDeviceRequestInProgressChanged = pyqtSignal()
|
||||||
|
|
||||||
|
@pyqtProperty(bool, notify = hasManualDeviceRequestInProgressChanged)
|
||||||
|
def hasManualDeviceRequestInProgress(self) -> bool:
|
||||||
|
return self._manual_device_address != ""
|
||||||
|
|
||||||
|
manualDeviceRequestFinished = pyqtSignal(bool, arguments = ["success"])
|
||||||
|
|
||||||
|
def _onManualDeviceRequestFinished(self, success: bool, address: str) -> None:
|
||||||
|
self._manual_device_request_timer.stop()
|
||||||
|
if address == self._manual_device_address:
|
||||||
|
self._manual_device_address = ""
|
||||||
|
self.hasManualDeviceRequestInProgressChanged.emit()
|
||||||
|
self.manualDeviceRequestFinished.emit(success)
|
||||||
|
|
||||||
|
@pyqtProperty("QVariantMap", notify = discoveredPrintersChanged)
|
||||||
|
def discoveredPrintersByAddress(self) -> Dict[str, DiscoveredPrinter]:
|
||||||
|
return self._discovered_printer_by_ip_dict
|
||||||
|
|
||||||
|
@pyqtProperty("QVariantList", notify = discoveredPrintersChanged)
|
||||||
def discoveredPrinters(self) -> List["DiscoveredPrinter"]:
|
def discoveredPrinters(self) -> List["DiscoveredPrinter"]:
|
||||||
item_list = list(
|
item_list = list(
|
||||||
x for x in self._discovered_printer_by_ip_dict.values() if not parseBool(x.device.getProperty("temporary")))
|
x for x in self._discovered_printer_by_ip_dict.values() if not parseBool(x.device.getProperty("temporary")))
|
||||||
|
@ -157,11 +248,3 @@ class DiscoveredPrintersModel(QObject):
|
||||||
@pyqtSlot("QVariant")
|
@pyqtSlot("QVariant")
|
||||||
def createMachineFromDiscoveredPrinter(self, discovered_printer: "DiscoveredPrinter") -> None:
|
def createMachineFromDiscoveredPrinter(self, discovered_printer: "DiscoveredPrinter") -> None:
|
||||||
discovered_printer.create_callback(discovered_printer.getKey())
|
discovered_printer.create_callback(discovered_printer.getKey())
|
||||||
|
|
||||||
@pyqtSlot(str)
|
|
||||||
def createMachineFromDiscoveredPrinterAddress(self, ip_address: str) -> None:
|
|
||||||
if ip_address not in self._discovered_printer_by_ip_dict:
|
|
||||||
Logger.log("i", "Key [%s] does not exist in the discovered printers list.", ip_address)
|
|
||||||
return
|
|
||||||
|
|
||||||
self.createMachineFromDiscoveredPrinter(self._discovered_printer_by_ip_dict[ip_address])
|
|
||||||
|
|
|
@ -35,6 +35,8 @@ class FirstStartMachineActionsModel(ListModel):
|
||||||
self._application = application
|
self._application = application
|
||||||
self._application.initializationFinished.connect(self._initialize)
|
self._application.initializationFinished.connect(self._initialize)
|
||||||
|
|
||||||
|
self._previous_global_stack = None
|
||||||
|
|
||||||
def _initialize(self) -> None:
|
def _initialize(self) -> None:
|
||||||
self._application.getMachineManager().globalContainerChanged.connect(self._update)
|
self._application.getMachineManager().globalContainerChanged.connect(self._update)
|
||||||
self._update()
|
self._update()
|
||||||
|
@ -86,13 +88,19 @@ class FirstStartMachineActionsModel(ListModel):
|
||||||
self.setItems([])
|
self.setItems([])
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# Do not update if the machine has not been switched. This can cause the SettingProviders on the Machine
|
||||||
|
# Setting page to do a force update, but they can use potential outdated cached values.
|
||||||
|
if self._previous_global_stack is not None and global_stack.getId() == self._previous_global_stack.getId():
|
||||||
|
return
|
||||||
|
self._previous_global_stack = global_stack
|
||||||
|
|
||||||
definition_id = global_stack.definition.getId()
|
definition_id = global_stack.definition.getId()
|
||||||
first_start_actions = self._application.getMachineActionManager().getFirstStartActions(definition_id)
|
first_start_actions = self._application.getMachineActionManager().getFirstStartActions(definition_id)
|
||||||
|
|
||||||
item_list = []
|
item_list = []
|
||||||
for item in first_start_actions:
|
for item in first_start_actions:
|
||||||
item_list.append({"title": item.label,
|
item_list.append({"title": item.label,
|
||||||
"content": item.displayItem,
|
"content": item.getDisplayItem(),
|
||||||
"action": item,
|
"action": item,
|
||||||
})
|
})
|
||||||
item.reset()
|
item.reset()
|
||||||
|
|
|
@ -5,6 +5,7 @@ from PyQt5.QtCore import Qt, QTimer
|
||||||
|
|
||||||
from UM.Qt.ListModel import ListModel
|
from UM.Qt.ListModel import ListModel
|
||||||
from UM.i18n import i18nCatalog
|
from UM.i18n import i18nCatalog
|
||||||
|
from UM.Util import parseBool
|
||||||
|
|
||||||
from cura.PrinterOutput.PrinterOutputDevice import ConnectionType
|
from cura.PrinterOutput.PrinterOutputDevice import ConnectionType
|
||||||
from cura.Settings.CuraContainerRegistry import CuraContainerRegistry
|
from cura.Settings.CuraContainerRegistry import CuraContainerRegistry
|
||||||
|
@ -54,7 +55,6 @@ class GlobalStacksModel(ListModel):
|
||||||
items = []
|
items = []
|
||||||
|
|
||||||
container_stacks = CuraContainerRegistry.getInstance().findContainerStacks(type = "machine")
|
container_stacks = CuraContainerRegistry.getInstance().findContainerStacks(type = "machine")
|
||||||
|
|
||||||
for container_stack in container_stacks:
|
for container_stack in container_stacks:
|
||||||
has_remote_connection = False
|
has_remote_connection = False
|
||||||
|
|
||||||
|
@ -62,7 +62,7 @@ class GlobalStacksModel(ListModel):
|
||||||
has_remote_connection |= connection_type in [ConnectionType.NetworkConnection.value,
|
has_remote_connection |= connection_type in [ConnectionType.NetworkConnection.value,
|
||||||
ConnectionType.CloudConnection.value]
|
ConnectionType.CloudConnection.value]
|
||||||
|
|
||||||
if container_stack.getMetaDataEntry("hidden", False) in ["True", True]:
|
if parseBool(container_stack.getMetaDataEntry("hidden", False)):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
section_name = "Network enabled printers" if has_remote_connection else "Local printers"
|
section_name = "Network enabled printers" if has_remote_connection else "Local printers"
|
||||||
|
@ -73,5 +73,5 @@ class GlobalStacksModel(ListModel):
|
||||||
"hasRemoteConnection": has_remote_connection,
|
"hasRemoteConnection": has_remote_connection,
|
||||||
"metadata": container_stack.getMetaData().copy(),
|
"metadata": container_stack.getMetaData().copy(),
|
||||||
"discoverySource": section_name})
|
"discoverySource": section_name})
|
||||||
items.sort(key = lambda i: not i["hasRemoteConnection"])
|
items.sort(key = lambda i: (not i["hasRemoteConnection"], i["name"]))
|
||||||
self.setItems(items)
|
self.setItems(items)
|
||||||
|
|
|
@ -34,6 +34,8 @@ class AuthorizationService:
|
||||||
# Emit signal when authentication failed.
|
# Emit signal when authentication failed.
|
||||||
onAuthenticationError = Signal()
|
onAuthenticationError = Signal()
|
||||||
|
|
||||||
|
accessTokenChanged = Signal()
|
||||||
|
|
||||||
def __init__(self, settings: "OAuth2Settings", preferences: Optional["Preferences"] = None) -> None:
|
def __init__(self, settings: "OAuth2Settings", preferences: Optional["Preferences"] = None) -> None:
|
||||||
self._settings = settings
|
self._settings = settings
|
||||||
self._auth_helpers = AuthorizationHelpers(settings)
|
self._auth_helpers = AuthorizationHelpers(settings)
|
||||||
|
@ -99,7 +101,9 @@ class AuthorizationService:
|
||||||
Logger.log("w", "Unable to use the refresh token to get a new access token.")
|
Logger.log("w", "Unable to use the refresh token to get a new access token.")
|
||||||
# The token could not be refreshed using the refresh token. We should login again.
|
# The token could not be refreshed using the refresh token. We should login again.
|
||||||
return None
|
return None
|
||||||
|
# Ensure it gets stored as otherwise we only have it in memory. The stored refresh token has been deleted
|
||||||
|
# from the server already.
|
||||||
|
self._storeAuthData(self._auth_data)
|
||||||
return self._auth_helpers.parseJWT(self._auth_data.access_token)
|
return self._auth_helpers.parseJWT(self._auth_data.access_token)
|
||||||
|
|
||||||
## Get the access token as provided by the repsonse data.
|
## Get the access token as provided by the repsonse data.
|
||||||
|
@ -128,6 +132,7 @@ class AuthorizationService:
|
||||||
self._storeAuthData(response)
|
self._storeAuthData(response)
|
||||||
self.onAuthStateChanged.emit(logged_in = True)
|
self.onAuthStateChanged.emit(logged_in = True)
|
||||||
else:
|
else:
|
||||||
|
Logger.log("w", "Failed to get a new access token from the server.")
|
||||||
self.onAuthStateChanged.emit(logged_in = False)
|
self.onAuthStateChanged.emit(logged_in = False)
|
||||||
|
|
||||||
## Delete the authentication data that we have stored locally (eg; logout)
|
## Delete the authentication data that we have stored locally (eg; logout)
|
||||||
|
@ -198,6 +203,7 @@ class AuthorizationService:
|
||||||
|
|
||||||
## Store authentication data in preferences.
|
## Store authentication data in preferences.
|
||||||
def _storeAuthData(self, auth_data: Optional[AuthenticationResponse] = None) -> None:
|
def _storeAuthData(self, auth_data: Optional[AuthenticationResponse] = None) -> None:
|
||||||
|
Logger.log("d", "Attempting to store the auth data")
|
||||||
if self._preferences is None:
|
if self._preferences is None:
|
||||||
Logger.log("e", "Unable to save authentication data, since no preference has been set!")
|
Logger.log("e", "Unable to save authentication data, since no preference has been set!")
|
||||||
return
|
return
|
||||||
|
@ -210,6 +216,8 @@ class AuthorizationService:
|
||||||
self._user_profile = None
|
self._user_profile = None
|
||||||
self._preferences.resetPreference(self._settings.AUTH_DATA_PREFERENCE_KEY)
|
self._preferences.resetPreference(self._settings.AUTH_DATA_PREFERENCE_KEY)
|
||||||
|
|
||||||
|
self.accessTokenChanged.emit()
|
||||||
|
|
||||||
def _onMessageActionTriggered(self, _, action):
|
def _onMessageActionTriggered(self, _, action):
|
||||||
if action == "retry":
|
if action == "retry":
|
||||||
self.loadAuthDataFromPreferences()
|
self.loadAuthDataFromPreferences()
|
||||||
|
|
|
@ -62,7 +62,24 @@ class ExtruderConfigurationModel(QObject):
|
||||||
return " ".join(message_chunks)
|
return " ".join(message_chunks)
|
||||||
|
|
||||||
def __eq__(self, other) -> bool:
|
def __eq__(self, other) -> bool:
|
||||||
return hash(self) == hash(other)
|
if not isinstance(other, ExtruderConfigurationModel):
|
||||||
|
return False
|
||||||
|
|
||||||
|
if self._position != other.position:
|
||||||
|
return False
|
||||||
|
# Empty materials should be ignored for comparison
|
||||||
|
if self.activeMaterial is not None and other.activeMaterial is not None:
|
||||||
|
if self.activeMaterial.guid != other.activeMaterial.guid:
|
||||||
|
if self.activeMaterial.guid != "" and other.activeMaterial.guid != "":
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
# At this point there is no material, so it doesn't matter what the hotend is.
|
||||||
|
return True
|
||||||
|
|
||||||
|
if self.hotendID != other.hotendID:
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
# Calculating a hash function using the position of the extruder, the material GUID and the hotend id to check if is
|
# Calculating a hash function using the position of the extruder, the material GUID and the hotend id to check if is
|
||||||
# unique within a set
|
# unique within a set
|
||||||
|
|
|
@ -71,7 +71,23 @@ class PrinterConfigurationModel(QObject):
|
||||||
return "\n".join(message_chunks)
|
return "\n".join(message_chunks)
|
||||||
|
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
return hash(self) == hash(other)
|
if not isinstance(other, PrinterConfigurationModel):
|
||||||
|
return False
|
||||||
|
|
||||||
|
if self.printerType != other.printerType:
|
||||||
|
return False
|
||||||
|
|
||||||
|
if self.buildplateConfiguration != other.buildplateConfiguration:
|
||||||
|
return False
|
||||||
|
|
||||||
|
if len(self.extruderConfigurations) != len(other.extruderConfigurations):
|
||||||
|
return False
|
||||||
|
|
||||||
|
for self_extruder, other_extruder in zip(sorted(self._extruder_configurations, key=lambda x: x.position), sorted(other.extruderConfigurations, key=lambda x: x.position)):
|
||||||
|
if self_extruder != other_extruder:
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
## The hash function is used to compare and create unique sets. The configuration is unique if the configuration
|
## The hash function is used to compare and create unique sets. The configuration is unique if the configuration
|
||||||
# of the extruders is unique (the order of the extruders matters), and the type and buildplate is the same.
|
# of the extruders is unique (the order of the extruders matters), and the type and buildplate is the same.
|
||||||
|
|
4
cura/PrinterOutput/PrintJobOutputModel.py
Normal file
4
cura/PrinterOutput/PrintJobOutputModel.py
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
import warnings
|
||||||
|
warnings.warn("Importing cura.PrinterOutput.PrintJobOutputModel has been deprecated since 4.1, use cura.PrinterOutput.Models.PrintJobOutputModel inststad", DeprecationWarning, stacklevel=2)
|
||||||
|
# We moved the the models to one submodule deeper
|
||||||
|
from cura.PrinterOutput.Models.PrintJobOutputModel import PrintJobOutputModel
|
4
cura/PrinterOutput/PrinterOutputModel.py
Normal file
4
cura/PrinterOutput/PrinterOutputModel.py
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
import warnings
|
||||||
|
warnings.warn("Importing cura.PrinterOutput.PrinterOutputModel has been deprecated since 4.1, use cura.PrinterOutput.Models.PrinterOutputModel inststad", DeprecationWarning, stacklevel=2)
|
||||||
|
# We moved the the models to one submodule deeper
|
||||||
|
from cura.PrinterOutput.Models.PrinterOutputModel import PrinterOutputModel
|
4
cura/PrinterOutputDevice.py
Normal file
4
cura/PrinterOutputDevice.py
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
import warnings
|
||||||
|
warnings.warn("Importing cura.PrinterOutputDevice has been deprecated since 4.1, use cura.PrinterOutput.PrinterOutputDevice inststad", DeprecationWarning, stacklevel=2)
|
||||||
|
# We moved the PrinterOutput device to it's own submodule.
|
||||||
|
from cura.PrinterOutput.PrinterOutputDevice import PrinterOutputDevice, ConnectionState
|
|
@ -116,11 +116,11 @@ class CuraSceneNode(SceneNode):
|
||||||
if self._mesh_data:
|
if self._mesh_data:
|
||||||
self._aabb = self._mesh_data.getExtents(self.getWorldTransformation())
|
self._aabb = self._mesh_data.getExtents(self.getWorldTransformation())
|
||||||
|
|
||||||
for child in self._children:
|
for child in self.getAllChildren():
|
||||||
if child.callDecoration("isNonPrintingMesh"):
|
if child.callDecoration("isNonPrintingMesh"):
|
||||||
# Non-printing-meshes inside a group should not affect push apart or drop to build plate
|
# Non-printing-meshes inside a group should not affect push apart or drop to build plate
|
||||||
continue
|
continue
|
||||||
if not child._mesh_data:
|
if not child.getMeshData():
|
||||||
# Nodes without mesh data should not affect bounding boxes of their parents.
|
# Nodes without mesh data should not affect bounding boxes of their parents.
|
||||||
continue
|
continue
|
||||||
if self._aabb is None:
|
if self._aabb is None:
|
||||||
|
|
|
@ -4,6 +4,8 @@
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
import threading
|
import threading
|
||||||
from typing import Any, Dict, Optional, Set, TYPE_CHECKING, List
|
from typing import Any, Dict, Optional, Set, TYPE_CHECKING, List
|
||||||
|
import uuid
|
||||||
|
|
||||||
from PyQt5.QtCore import pyqtProperty, pyqtSlot, pyqtSignal
|
from PyQt5.QtCore import pyqtProperty, pyqtSlot, pyqtSignal
|
||||||
|
|
||||||
from UM.Decorators import override
|
from UM.Decorators import override
|
||||||
|
@ -34,6 +36,12 @@ class GlobalStack(CuraContainerStack):
|
||||||
|
|
||||||
self.setMetaDataEntry("type", "machine") # For backward compatibility
|
self.setMetaDataEntry("type", "machine") # For backward compatibility
|
||||||
|
|
||||||
|
# TL;DR: If Cura is looking for printers that belong to the same group, it should use "group_id".
|
||||||
|
# Each GlobalStack by default belongs to a group which is identified via "group_id". This group_id is used to
|
||||||
|
# figure out which GlobalStacks are in the printer cluster for example without knowing the implementation
|
||||||
|
# details such as the um_network_key or some other identifier that's used by the underlying device plugin.
|
||||||
|
self.setMetaDataEntry("group_id", str(uuid.uuid4())) # Assign a new GlobalStack to a unique group by default
|
||||||
|
|
||||||
self._extruders = {} # type: Dict[str, "ExtruderStack"]
|
self._extruders = {} # type: Dict[str, "ExtruderStack"]
|
||||||
|
|
||||||
# This property is used to track which settings we are calculating the "resolve" for
|
# This property is used to track which settings we are calculating the "resolve" for
|
||||||
|
@ -68,6 +76,10 @@ class GlobalStack(CuraContainerStack):
|
||||||
def maxExtruderCount(self):
|
def maxExtruderCount(self):
|
||||||
return len(self.getMetaDataEntry("machine_extruder_trains"))
|
return len(self.getMetaDataEntry("machine_extruder_trains"))
|
||||||
|
|
||||||
|
@pyqtProperty(bool, notify=configuredConnectionTypesChanged)
|
||||||
|
def supportsNetworkConnection(self):
|
||||||
|
return self.getMetaDataEntry("supports_network_connection", False)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def getLoadingPriority(cls) -> int:
|
def getLoadingPriority(cls) -> int:
|
||||||
return 2
|
return 2
|
||||||
|
|
|
@ -720,7 +720,7 @@ class MachineManager(QObject):
|
||||||
extruder_stack.userChanges.setProperty(key, "value", new_value)
|
extruder_stack.userChanges.setProperty(key, "value", new_value)
|
||||||
|
|
||||||
@pyqtProperty(str, notify = activeVariantChanged)
|
@pyqtProperty(str, notify = activeVariantChanged)
|
||||||
@deprecated("use Cura.activeStack.variant.name instead", "4.1")
|
@deprecated("use Cura.MachineManager.activeStack.variant.name instead", "4.1")
|
||||||
def activeVariantName(self) -> str:
|
def activeVariantName(self) -> str:
|
||||||
if self._active_container_stack:
|
if self._active_container_stack:
|
||||||
variant = self._active_container_stack.variant
|
variant = self._active_container_stack.variant
|
||||||
|
@ -730,7 +730,7 @@ class MachineManager(QObject):
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
@pyqtProperty(str, notify = activeVariantChanged)
|
@pyqtProperty(str, notify = activeVariantChanged)
|
||||||
@deprecated("use Cura.activeStack.variant.id instead", "4.1")
|
@deprecated("use Cura.MachineManager.activeStack.variant.id instead", "4.1")
|
||||||
def activeVariantId(self) -> str:
|
def activeVariantId(self) -> str:
|
||||||
if self._active_container_stack:
|
if self._active_container_stack:
|
||||||
variant = self._active_container_stack.variant
|
variant = self._active_container_stack.variant
|
||||||
|
@ -740,7 +740,7 @@ class MachineManager(QObject):
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
@pyqtProperty(str, notify = activeVariantChanged)
|
@pyqtProperty(str, notify = activeVariantChanged)
|
||||||
@deprecated("use Cura.activeMachine.variant.name instead", "4.1")
|
@deprecated("use Cura.MachineManager.activeMachine.variant.name instead", "4.1")
|
||||||
def activeVariantBuildplateName(self) -> str:
|
def activeVariantBuildplateName(self) -> str:
|
||||||
if self._global_container_stack:
|
if self._global_container_stack:
|
||||||
variant = self._global_container_stack.variant
|
variant = self._global_container_stack.variant
|
||||||
|
@ -750,7 +750,7 @@ class MachineManager(QObject):
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
@pyqtProperty(str, notify = globalContainerChanged)
|
@pyqtProperty(str, notify = globalContainerChanged)
|
||||||
@deprecated("use Cura.activeMachine.definition.id instead", "4.1")
|
@deprecated("use Cura.MachineManager.activeMachine.definition.id instead", "4.1")
|
||||||
def activeDefinitionId(self) -> str:
|
def activeDefinitionId(self) -> str:
|
||||||
if self._global_container_stack:
|
if self._global_container_stack:
|
||||||
return self._global_container_stack.definition.id
|
return self._global_container_stack.definition.id
|
||||||
|
@ -797,7 +797,6 @@ class MachineManager(QObject):
|
||||||
self.setActiveMachine(other_machine_stacks[0]["id"])
|
self.setActiveMachine(other_machine_stacks[0]["id"])
|
||||||
|
|
||||||
metadata = CuraContainerRegistry.getInstance().findContainerStacksMetadata(id = machine_id)[0]
|
metadata = CuraContainerRegistry.getInstance().findContainerStacksMetadata(id = machine_id)[0]
|
||||||
network_key = metadata.get("um_network_key", None)
|
|
||||||
ExtruderManager.getInstance().removeMachineExtruders(machine_id)
|
ExtruderManager.getInstance().removeMachineExtruders(machine_id)
|
||||||
containers = CuraContainerRegistry.getInstance().findInstanceContainersMetadata(type = "user", machine = machine_id)
|
containers = CuraContainerRegistry.getInstance().findInstanceContainersMetadata(type = "user", machine = machine_id)
|
||||||
for container in containers:
|
for container in containers:
|
||||||
|
@ -805,8 +804,9 @@ class MachineManager(QObject):
|
||||||
CuraContainerRegistry.getInstance().removeContainer(machine_id)
|
CuraContainerRegistry.getInstance().removeContainer(machine_id)
|
||||||
|
|
||||||
# If the printer that is being removed is a network printer, the hidden printers have to be also removed
|
# If the printer that is being removed is a network printer, the hidden printers have to be also removed
|
||||||
if network_key:
|
group_id = metadata.get("group_id", None)
|
||||||
metadata_filter = {"um_network_key": network_key}
|
if group_id:
|
||||||
|
metadata_filter = {"group_id": group_id}
|
||||||
hidden_containers = CuraContainerRegistry.getInstance().findContainerStacks(type = "machine", **metadata_filter)
|
hidden_containers = CuraContainerRegistry.getInstance().findContainerStacks(type = "machine", **metadata_filter)
|
||||||
if hidden_containers:
|
if hidden_containers:
|
||||||
# This reuses the method and remove all printers recursively
|
# This reuses the method and remove all printers recursively
|
||||||
|
@ -1263,8 +1263,8 @@ class MachineManager(QObject):
|
||||||
if self._global_container_stack is not None:
|
if self._global_container_stack is not None:
|
||||||
if Util.parseBool(self._global_container_stack.getMetaDataEntry("has_materials", False)):
|
if Util.parseBool(self._global_container_stack.getMetaDataEntry("has_materials", False)):
|
||||||
for position, extruder in self._global_container_stack.extruders.items():
|
for position, extruder in self._global_container_stack.extruders.items():
|
||||||
if extruder.isEnabled and not extruder.material.getMetaDataEntry("compatible"):
|
if not extruder.isEnabled:
|
||||||
return False
|
continue
|
||||||
if not extruder.material.getMetaDataEntry("compatible"):
|
if not extruder.material.getMetaDataEntry("compatible"):
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
@ -1360,21 +1360,24 @@ class MachineManager(QObject):
|
||||||
# Get the definition id corresponding to this machine name
|
# Get the definition id corresponding to this machine name
|
||||||
machine_definition_id = CuraContainerRegistry.getInstance().findDefinitionContainers(name = machine_name)[0].getId()
|
machine_definition_id = CuraContainerRegistry.getInstance().findDefinitionContainers(name = machine_name)[0].getId()
|
||||||
# Try to find a machine with the same network key
|
# Try to find a machine with the same network key
|
||||||
new_machine = self.getMachine(machine_definition_id, metadata_filter = {"um_network_key": self.activeMachineNetworkKey()})
|
metadata_filter = {"group_id": self._global_container_stack.getMetaDataEntry("group_id"),
|
||||||
|
"um_network_key": self.activeMachineNetworkKey(),
|
||||||
|
}
|
||||||
|
new_machine = self.getMachine(machine_definition_id, metadata_filter = metadata_filter)
|
||||||
# If there is no machine, then create a new one and set it to the non-hidden instance
|
# If there is no machine, then create a new one and set it to the non-hidden instance
|
||||||
if not new_machine:
|
if not new_machine:
|
||||||
new_machine = CuraStackBuilder.createMachine(machine_definition_id + "_sync", machine_definition_id)
|
new_machine = CuraStackBuilder.createMachine(machine_definition_id + "_sync", machine_definition_id)
|
||||||
if not new_machine:
|
if not new_machine:
|
||||||
return
|
return
|
||||||
|
new_machine.setMetaDataEntry("group_id", self._global_container_stack.getMetaDataEntry("group_id"))
|
||||||
new_machine.setMetaDataEntry("um_network_key", self.activeMachineNetworkKey())
|
new_machine.setMetaDataEntry("um_network_key", self.activeMachineNetworkKey())
|
||||||
new_machine.setMetaDataEntry("group_name", self.activeMachineNetworkGroupName)
|
new_machine.setMetaDataEntry("group_name", self.activeMachineNetworkGroupName)
|
||||||
new_machine.setMetaDataEntry("hidden", False)
|
|
||||||
new_machine.setMetaDataEntry("connection_type", self._global_container_stack.getMetaDataEntry("connection_type"))
|
new_machine.setMetaDataEntry("connection_type", self._global_container_stack.getMetaDataEntry("connection_type"))
|
||||||
else:
|
else:
|
||||||
Logger.log("i", "Found a %s with the key %s. Let's use it!", machine_name, self.activeMachineNetworkKey())
|
Logger.log("i", "Found a %s with the key %s. Let's use it!", machine_name, self.activeMachineNetworkKey())
|
||||||
new_machine.setMetaDataEntry("hidden", False)
|
|
||||||
|
|
||||||
# Set the current printer instance to hidden (the metadata entry must exist)
|
# Set the current printer instance to hidden (the metadata entry must exist)
|
||||||
|
new_machine.setMetaDataEntry("hidden", False)
|
||||||
self._global_container_stack.setMetaDataEntry("hidden", True)
|
self._global_container_stack.setMetaDataEntry("hidden", True)
|
||||||
|
|
||||||
self.setActiveMachine(new_machine.getId())
|
self.setActiveMachine(new_machine.getId())
|
||||||
|
@ -1640,6 +1643,13 @@ class MachineManager(QObject):
|
||||||
|
|
||||||
return abbr_machine
|
return abbr_machine
|
||||||
|
|
||||||
|
# Checks if the given machine type name in the available machine list.
|
||||||
|
# The machine type is a code name such as "ultimaker_3", while the machine type name is the human-readable name of
|
||||||
|
# the machine type, which is "Ultimaker 3" for "ultimaker_3".
|
||||||
|
def hasHumanReadableMachineTypeName(self, machine_type_name: str) -> bool:
|
||||||
|
results = self._container_registry.findDefinitionContainersMetadata(name = machine_type_name)
|
||||||
|
return len(results) > 0
|
||||||
|
|
||||||
@pyqtSlot(str, result = str)
|
@pyqtSlot(str, result = str)
|
||||||
def getMachineTypeNameFromId(self, machine_type_id: str) -> str:
|
def getMachineTypeNameFromId(self, machine_type_id: str) -> str:
|
||||||
machine_type_name = ""
|
machine_type_name = ""
|
||||||
|
@ -1647,3 +1657,7 @@ class MachineManager(QObject):
|
||||||
if results:
|
if results:
|
||||||
machine_type_name = results[0]["name"]
|
machine_type_name = results[0]["name"]
|
||||||
return machine_type_name
|
return machine_type_name
|
||||||
|
|
||||||
|
# Gets all machines that belong to the given group_id.
|
||||||
|
def getMachinesInGroup(self, group_id: str) -> List["GlobalStack"]:
|
||||||
|
return self._container_registry.findContainerStacks(type = "machine", group_id = group_id)
|
||||||
|
|
|
@ -113,21 +113,17 @@ class SettingOverrideDecorator(SceneNodeDecorator):
|
||||||
|
|
||||||
def _onSettingChanged(self, setting_key, property_name): # Reminder: 'property' is a built-in function
|
def _onSettingChanged(self, setting_key, property_name): # Reminder: 'property' is a built-in function
|
||||||
# We're only interested in a few settings and only if it's value changed.
|
# We're only interested in a few settings and only if it's value changed.
|
||||||
if property_name == "value" and (setting_key in self._non_printing_mesh_settings or setting_key in self._non_thumbnail_visible_settings):
|
if property_name == "value":
|
||||||
# Trigger slice/need slicing if the value has changed.
|
if setting_key in self._non_printing_mesh_settings or setting_key in self._non_thumbnail_visible_settings:
|
||||||
new_is_non_printing_mesh = self._evaluateIsNonPrintingMesh()
|
# Trigger slice/need slicing if the value has changed.
|
||||||
new_is_non_thumbnail_visible_mesh = self._evaluateIsNonThumbnailVisibleMesh()
|
new_is_non_printing_mesh = self._evaluateIsNonPrintingMesh()
|
||||||
changed = False
|
self._is_non_thumbnail_visible_mesh = self._evaluateIsNonThumbnailVisibleMesh()
|
||||||
if self._is_non_printing_mesh != new_is_non_printing_mesh:
|
|
||||||
self._is_non_printing_mesh = new_is_non_printing_mesh
|
|
||||||
self._node.setCalculateBoundingBox(not self._is_non_printing_mesh)
|
|
||||||
changed = True
|
|
||||||
if self._is_non_thumbnail_visible_mesh != new_is_non_thumbnail_visible_mesh:
|
|
||||||
changed = True
|
|
||||||
|
|
||||||
if changed:
|
if self._is_non_printing_mesh != new_is_non_printing_mesh:
|
||||||
Application.getInstance().getBackend().needsSlicing()
|
self._is_non_printing_mesh = new_is_non_printing_mesh
|
||||||
Application.getInstance().getBackend().tickle()
|
|
||||||
|
Application.getInstance().getBackend().needsSlicing()
|
||||||
|
Application.getInstance().getBackend().tickle()
|
||||||
|
|
||||||
## Makes sure that the stack upon which the container stack is placed is
|
## Makes sure that the stack upon which the container stack is placed is
|
||||||
# kept up to date.
|
# kept up to date.
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
# Copyright (c) 2018 Ultimaker B.V.
|
# Copyright (c) 2019 Ultimaker B.V.
|
||||||
# Cura is released under the terms of the LGPLv3 or higher.
|
# Cura is released under the terms of the LGPLv3 or higher.
|
||||||
|
|
||||||
import copy
|
import copy
|
||||||
|
|
||||||
from UM.Settings.constant_instance_containers import EMPTY_CONTAINER_ID, empty_container
|
from UM.Settings.constant_instance_containers import EMPTY_CONTAINER_ID, empty_container
|
||||||
|
from UM.i18n import i18nCatalog
|
||||||
|
catalog = i18nCatalog("cura")
|
||||||
|
|
||||||
|
|
||||||
# Empty definition changes
|
# Empty definition changes
|
||||||
|
@ -28,7 +30,7 @@ empty_material_container.setMetaDataEntry("type", "material")
|
||||||
EMPTY_QUALITY_CONTAINER_ID = "empty_quality"
|
EMPTY_QUALITY_CONTAINER_ID = "empty_quality"
|
||||||
empty_quality_container = copy.deepcopy(empty_container)
|
empty_quality_container = copy.deepcopy(empty_container)
|
||||||
empty_quality_container.setMetaDataEntry("id", EMPTY_QUALITY_CONTAINER_ID)
|
empty_quality_container.setMetaDataEntry("id", EMPTY_QUALITY_CONTAINER_ID)
|
||||||
empty_quality_container.setName("Not Supported")
|
empty_quality_container.setName(catalog.i18nc("@info:not supported profile", "Not supported"))
|
||||||
empty_quality_container.setMetaDataEntry("quality_type", "not_supported")
|
empty_quality_container.setMetaDataEntry("quality_type", "not_supported")
|
||||||
empty_quality_container.setMetaDataEntry("type", "quality")
|
empty_quality_container.setMetaDataEntry("type", "quality")
|
||||||
empty_quality_container.setMetaDataEntry("supported", False)
|
empty_quality_container.setMetaDataEntry("supported", False)
|
||||||
|
|
|
@ -15,6 +15,7 @@ class AddPrinterPagesModel(WelcomePagesModel):
|
||||||
"page_url": self._getBuiltinWelcomePagePath("AddNetworkOrLocalPrinterContent.qml"),
|
"page_url": self._getBuiltinWelcomePagePath("AddNetworkOrLocalPrinterContent.qml"),
|
||||||
"next_page_id": "machine_actions",
|
"next_page_id": "machine_actions",
|
||||||
"next_page_button_text": self._catalog.i18nc("@action:button", "Add"),
|
"next_page_button_text": self._catalog.i18nc("@action:button", "Add"),
|
||||||
|
"previous_page_button_text": self._catalog.i18nc("@action:button", "Cancel"),
|
||||||
})
|
})
|
||||||
self._pages.append({"id": "add_printer_by_ip",
|
self._pages.append({"id": "add_printer_by_ip",
|
||||||
"page_url": self._getBuiltinWelcomePagePath("AddPrinterByIpContent.qml"),
|
"page_url": self._getBuiltinWelcomePagePath("AddPrinterByIpContent.qml"),
|
||||||
|
|
|
@ -81,6 +81,7 @@ class PrintInformation(QObject):
|
||||||
"support_interface": catalog.i18nc("@tooltip", "Support Interface"),
|
"support_interface": catalog.i18nc("@tooltip", "Support Interface"),
|
||||||
"support": catalog.i18nc("@tooltip", "Support"),
|
"support": catalog.i18nc("@tooltip", "Support"),
|
||||||
"skirt": catalog.i18nc("@tooltip", "Skirt"),
|
"skirt": catalog.i18nc("@tooltip", "Skirt"),
|
||||||
|
"prime_tower": catalog.i18nc("@tooltip", "Prime Tower"),
|
||||||
"travel": catalog.i18nc("@tooltip", "Travel"),
|
"travel": catalog.i18nc("@tooltip", "Travel"),
|
||||||
"retract": catalog.i18nc("@tooltip", "Retractions"),
|
"retract": catalog.i18nc("@tooltip", "Retractions"),
|
||||||
"none": catalog.i18nc("@tooltip", "Other")
|
"none": catalog.i18nc("@tooltip", "Other")
|
||||||
|
|
|
@ -39,6 +39,7 @@ class WelcomePagesModel(ListModel):
|
||||||
PageUrlRole = Qt.UserRole + 2 # URL to the page's QML file
|
PageUrlRole = Qt.UserRole + 2 # URL to the page's QML file
|
||||||
NextPageIdRole = Qt.UserRole + 3 # The next page ID it should go to
|
NextPageIdRole = Qt.UserRole + 3 # The next page ID it should go to
|
||||||
NextPageButtonTextRole = Qt.UserRole + 4 # The text for the next page button
|
NextPageButtonTextRole = Qt.UserRole + 4 # The text for the next page button
|
||||||
|
PreviousPageButtonTextRole = Qt.UserRole + 5 # The text for the previous page button
|
||||||
|
|
||||||
def __init__(self, application: "CuraApplication", parent: Optional["QObject"] = None) -> None:
|
def __init__(self, application: "CuraApplication", parent: Optional["QObject"] = None) -> None:
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
|
@ -47,6 +48,7 @@ class WelcomePagesModel(ListModel):
|
||||||
self.addRoleName(self.PageUrlRole, "page_url")
|
self.addRoleName(self.PageUrlRole, "page_url")
|
||||||
self.addRoleName(self.NextPageIdRole, "next_page_id")
|
self.addRoleName(self.NextPageIdRole, "next_page_id")
|
||||||
self.addRoleName(self.NextPageButtonTextRole, "next_page_button_text")
|
self.addRoleName(self.NextPageButtonTextRole, "next_page_button_text")
|
||||||
|
self.addRoleName(self.PreviousPageButtonTextRole, "previous_page_button_text")
|
||||||
|
|
||||||
self._application = application
|
self._application = application
|
||||||
self._catalog = i18nCatalog("cura")
|
self._catalog = i18nCatalog("cura")
|
||||||
|
|
44
cura/Utils/NetworkingUtil.py
Normal file
44
cura/Utils/NetworkingUtil.py
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
# Copyright (c) 2019 Ultimaker B.V.
|
||||||
|
# Cura is released under the terms of the LGPLv3 or higher.
|
||||||
|
|
||||||
|
import socket
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from PyQt5.QtCore import QObject, pyqtSlot
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# This is a QObject because some of the functions can be used (and are useful) in QML.
|
||||||
|
#
|
||||||
|
class NetworkingUtil(QObject):
|
||||||
|
|
||||||
|
def __init__(self, parent: Optional["QObject"] = None) -> None:
|
||||||
|
super().__init__(parent = parent)
|
||||||
|
|
||||||
|
# Checks if the given string is a valid IPv4 address.
|
||||||
|
@pyqtSlot(str, result = bool)
|
||||||
|
def isIPv4(self, address: str) -> bool:
|
||||||
|
try:
|
||||||
|
socket.inet_pton(socket.AF_INET, address)
|
||||||
|
result = True
|
||||||
|
except:
|
||||||
|
result = False
|
||||||
|
return result
|
||||||
|
|
||||||
|
# Checks if the given string is a valid IPv6 address.
|
||||||
|
@pyqtSlot(str, result = bool)
|
||||||
|
def isIPv6(self, address: str) -> bool:
|
||||||
|
try:
|
||||||
|
socket.inet_pton(socket.AF_INET6, address)
|
||||||
|
result = True
|
||||||
|
except:
|
||||||
|
result = False
|
||||||
|
return result
|
||||||
|
|
||||||
|
# Checks if the given string is a valid IPv4 or IPv6 address.
|
||||||
|
@pyqtSlot(str, result = bool)
|
||||||
|
def isValidIP(self, address: str) -> bool:
|
||||||
|
return self.isIPv4(address) or self.isIPv6(address)
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = ["NetworkingUtil"]
|
|
@ -1,7 +1,7 @@
|
||||||
# Copyright (c) 2018 Ultimaker B.V.
|
# Copyright (c) 2019 Ultimaker B.V.
|
||||||
# Cura is released under the terms of the LGPLv3 or higher.
|
# Cura is released under the terms of the LGPLv3 or higher.
|
||||||
|
|
||||||
from typing import Optional
|
from typing import List, Optional, Union, TYPE_CHECKING
|
||||||
import os.path
|
import os.path
|
||||||
import zipfile
|
import zipfile
|
||||||
|
|
||||||
|
@ -9,15 +9,16 @@ import numpy
|
||||||
|
|
||||||
import Savitar
|
import Savitar
|
||||||
|
|
||||||
from UM.Application import Application
|
|
||||||
from UM.Logger import Logger
|
from UM.Logger import Logger
|
||||||
from UM.Math.Matrix import Matrix
|
from UM.Math.Matrix import Matrix
|
||||||
from UM.Math.Vector import Vector
|
from UM.Math.Vector import Vector
|
||||||
from UM.Mesh.MeshBuilder import MeshBuilder
|
from UM.Mesh.MeshBuilder import MeshBuilder
|
||||||
from UM.Mesh.MeshReader import MeshReader
|
from UM.Mesh.MeshReader import MeshReader
|
||||||
from UM.Scene.GroupDecorator import GroupDecorator
|
from UM.Scene.GroupDecorator import GroupDecorator
|
||||||
|
from UM.Scene.SceneNode import SceneNode #For typing.
|
||||||
from UM.MimeTypeDatabase import MimeTypeDatabase, MimeType
|
from UM.MimeTypeDatabase import MimeTypeDatabase, MimeType
|
||||||
|
|
||||||
|
from cura.CuraApplication import CuraApplication
|
||||||
from cura.Settings.ExtruderManager import ExtruderManager
|
from cura.Settings.ExtruderManager import ExtruderManager
|
||||||
from cura.Scene.CuraSceneNode import CuraSceneNode
|
from cura.Scene.CuraSceneNode import CuraSceneNode
|
||||||
from cura.Scene.BuildPlateDecorator import BuildPlateDecorator
|
from cura.Scene.BuildPlateDecorator import BuildPlateDecorator
|
||||||
|
@ -25,11 +26,9 @@ from cura.Scene.SliceableObjectDecorator import SliceableObjectDecorator
|
||||||
from cura.Scene.ZOffsetDecorator import ZOffsetDecorator
|
from cura.Scene.ZOffsetDecorator import ZOffsetDecorator
|
||||||
from cura.Machines.QualityManager import getMachineDefinitionIDForQualitySearch
|
from cura.Machines.QualityManager import getMachineDefinitionIDForQualitySearch
|
||||||
|
|
||||||
MYPY = False
|
|
||||||
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if not MYPY:
|
if not TYPE_CHECKING:
|
||||||
import xml.etree.cElementTree as ET
|
import xml.etree.cElementTree as ET
|
||||||
except ImportError:
|
except ImportError:
|
||||||
Logger.log("w", "Unable to load cElementTree, switching to slower version")
|
Logger.log("w", "Unable to load cElementTree, switching to slower version")
|
||||||
|
@ -55,7 +54,7 @@ class ThreeMFReader(MeshReader):
|
||||||
self._unit = None
|
self._unit = None
|
||||||
self._object_count = 0 # Used to name objects as there is no node name yet.
|
self._object_count = 0 # Used to name objects as there is no node name yet.
|
||||||
|
|
||||||
def _createMatrixFromTransformationString(self, transformation):
|
def _createMatrixFromTransformationString(self, transformation: str) -> Matrix:
|
||||||
if transformation == "":
|
if transformation == "":
|
||||||
return Matrix()
|
return Matrix()
|
||||||
|
|
||||||
|
@ -85,13 +84,13 @@ class ThreeMFReader(MeshReader):
|
||||||
|
|
||||||
return temp_mat
|
return temp_mat
|
||||||
|
|
||||||
## Convenience function that converts a SceneNode object (as obtained from libSavitar) to a Uranium scene node.
|
## Convenience function that converts a SceneNode object (as obtained from libSavitar) to a scene node.
|
||||||
# \returns Uranium scene node.
|
# \returns Scene node.
|
||||||
def _convertSavitarNodeToUMNode(self, savitar_node):
|
def _convertSavitarNodeToUMNode(self, savitar_node: Savitar.SceneNode) -> Optional[SceneNode]:
|
||||||
self._object_count += 1
|
self._object_count += 1
|
||||||
node_name = "Object %s" % self._object_count
|
node_name = "Object %s" % self._object_count
|
||||||
|
|
||||||
active_build_plate = Application.getInstance().getMultiBuildPlateModel().activeBuildPlate
|
active_build_plate = CuraApplication.getInstance().getMultiBuildPlateModel().activeBuildPlate
|
||||||
|
|
||||||
um_node = CuraSceneNode() # This adds a SettingOverrideDecorator
|
um_node = CuraSceneNode() # This adds a SettingOverrideDecorator
|
||||||
um_node.addDecorator(BuildPlateDecorator(active_build_plate))
|
um_node.addDecorator(BuildPlateDecorator(active_build_plate))
|
||||||
|
@ -122,7 +121,7 @@ class ThreeMFReader(MeshReader):
|
||||||
|
|
||||||
# Add the setting override decorator, so we can add settings to this node.
|
# Add the setting override decorator, so we can add settings to this node.
|
||||||
if settings:
|
if settings:
|
||||||
global_container_stack = Application.getInstance().getGlobalContainerStack()
|
global_container_stack = CuraApplication.getInstance().getGlobalContainerStack()
|
||||||
|
|
||||||
# Ensure the correct next container for the SettingOverride decorator is set.
|
# Ensure the correct next container for the SettingOverride decorator is set.
|
||||||
if global_container_stack:
|
if global_container_stack:
|
||||||
|
@ -161,7 +160,7 @@ class ThreeMFReader(MeshReader):
|
||||||
um_node.addDecorator(sliceable_decorator)
|
um_node.addDecorator(sliceable_decorator)
|
||||||
return um_node
|
return um_node
|
||||||
|
|
||||||
def _read(self, file_name):
|
def _read(self, file_name: str) -> Union[SceneNode, List[SceneNode]]:
|
||||||
result = []
|
result = []
|
||||||
self._object_count = 0 # Used to name objects as there is no node name yet.
|
self._object_count = 0 # Used to name objects as there is no node name yet.
|
||||||
# The base object of 3mf is a zipped archive.
|
# The base object of 3mf is a zipped archive.
|
||||||
|
@ -181,12 +180,13 @@ class ThreeMFReader(MeshReader):
|
||||||
mesh_data = um_node.getMeshData()
|
mesh_data = um_node.getMeshData()
|
||||||
if mesh_data is not None:
|
if mesh_data is not None:
|
||||||
extents = mesh_data.getExtents()
|
extents = mesh_data.getExtents()
|
||||||
center_vector = Vector(extents.center.x, extents.center.y, extents.center.z)
|
if extents is not None:
|
||||||
transform_matrix.setByTranslation(center_vector)
|
center_vector = Vector(extents.center.x, extents.center.y, extents.center.z)
|
||||||
|
transform_matrix.setByTranslation(center_vector)
|
||||||
transform_matrix.multiply(um_node.getLocalTransformation())
|
transform_matrix.multiply(um_node.getLocalTransformation())
|
||||||
um_node.setTransformation(transform_matrix)
|
um_node.setTransformation(transform_matrix)
|
||||||
|
|
||||||
global_container_stack = Application.getInstance().getGlobalContainerStack()
|
global_container_stack = CuraApplication.getInstance().getGlobalContainerStack()
|
||||||
|
|
||||||
# Create a transformation Matrix to convert from 3mf worldspace into ours.
|
# Create a transformation Matrix to convert from 3mf worldspace into ours.
|
||||||
# First step: flip the y and z axis.
|
# First step: flip the y and z axis.
|
||||||
|
@ -215,17 +215,20 @@ class ThreeMFReader(MeshReader):
|
||||||
um_node.setTransformation(um_node.getLocalTransformation().preMultiply(transformation_matrix))
|
um_node.setTransformation(um_node.getLocalTransformation().preMultiply(transformation_matrix))
|
||||||
|
|
||||||
# Check if the model is positioned below the build plate and honor that when loading project files.
|
# Check if the model is positioned below the build plate and honor that when loading project files.
|
||||||
if um_node.getMeshData() is not None:
|
node_meshdata = um_node.getMeshData()
|
||||||
minimum_z_value = um_node.getMeshData().getExtents(um_node.getWorldTransformation()).minimum.y # y is z in transformation coordinates
|
if node_meshdata is not None:
|
||||||
if minimum_z_value < 0:
|
aabb = node_meshdata.getExtents(um_node.getWorldTransformation())
|
||||||
um_node.addDecorator(ZOffsetDecorator())
|
if aabb is not None:
|
||||||
um_node.callDecoration("setZOffset", minimum_z_value)
|
minimum_z_value = aabb.minimum.y # y is z in transformation coordinates
|
||||||
|
if minimum_z_value < 0:
|
||||||
|
um_node.addDecorator(ZOffsetDecorator())
|
||||||
|
um_node.callDecoration("setZOffset", minimum_z_value)
|
||||||
|
|
||||||
result.append(um_node)
|
result.append(um_node)
|
||||||
|
|
||||||
except Exception:
|
except Exception:
|
||||||
Logger.logException("e", "An exception occurred in 3mf reader.")
|
Logger.logException("e", "An exception occurred in 3mf reader.")
|
||||||
return None
|
return []
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
173
plugins/AMFReader/AMFReader.py
Normal file
173
plugins/AMFReader/AMFReader.py
Normal file
|
@ -0,0 +1,173 @@
|
||||||
|
# Copyright (c) 2019 fieldOfView
|
||||||
|
# Cura is released under the terms of the LGPLv3 or higher.
|
||||||
|
|
||||||
|
# This AMF parser is based on the AMF parser in legacy cura:
|
||||||
|
# https://github.com/daid/LegacyCura/blob/ad7641e059048c7dcb25da1f47c0a7e95e7f4f7c/Cura/util/meshLoaders/amf.py
|
||||||
|
from UM.MimeTypeDatabase import MimeTypeDatabase, MimeType
|
||||||
|
from cura.CuraApplication import CuraApplication
|
||||||
|
from UM.Logger import Logger
|
||||||
|
|
||||||
|
from UM.Mesh.MeshData import MeshData, calculateNormalsFromIndexedVertices
|
||||||
|
from UM.Mesh.MeshReader import MeshReader
|
||||||
|
|
||||||
|
from cura.Scene.CuraSceneNode import CuraSceneNode
|
||||||
|
from cura.Scene.SliceableObjectDecorator import SliceableObjectDecorator
|
||||||
|
from cura.Scene.BuildPlateDecorator import BuildPlateDecorator
|
||||||
|
from cura.Scene.ConvexHullDecorator import ConvexHullDecorator
|
||||||
|
from UM.Scene.GroupDecorator import GroupDecorator
|
||||||
|
|
||||||
|
import numpy
|
||||||
|
import trimesh
|
||||||
|
import os.path
|
||||||
|
import zipfile
|
||||||
|
|
||||||
|
MYPY = False
|
||||||
|
try:
|
||||||
|
if not MYPY:
|
||||||
|
import xml.etree.cElementTree as ET
|
||||||
|
except ImportError:
|
||||||
|
import xml.etree.ElementTree as ET
|
||||||
|
|
||||||
|
from typing import Dict
|
||||||
|
|
||||||
|
|
||||||
|
class AMFReader(MeshReader):
|
||||||
|
def __init__(self) -> None:
|
||||||
|
super().__init__()
|
||||||
|
self._supported_extensions = [".amf"]
|
||||||
|
self._namespaces = {} # type: Dict[str, str]
|
||||||
|
|
||||||
|
MimeTypeDatabase.addMimeType(
|
||||||
|
MimeType(
|
||||||
|
name="application/x-amf",
|
||||||
|
comment="AMF",
|
||||||
|
suffixes=["amf"]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Main entry point
|
||||||
|
# Reads the file, returns a SceneNode (possibly with nested ones), or None
|
||||||
|
def _read(self, file_name):
|
||||||
|
base_name = os.path.basename(file_name)
|
||||||
|
try:
|
||||||
|
zipped_file = zipfile.ZipFile(file_name)
|
||||||
|
xml_document = zipped_file.read(zipped_file.namelist()[0])
|
||||||
|
zipped_file.close()
|
||||||
|
except zipfile.BadZipfile:
|
||||||
|
raw_file = open(file_name, "r")
|
||||||
|
xml_document = raw_file.read()
|
||||||
|
raw_file.close()
|
||||||
|
|
||||||
|
try:
|
||||||
|
amf_document = ET.fromstring(xml_document)
|
||||||
|
except ET.ParseError:
|
||||||
|
Logger.log("e", "Could not parse XML in file %s" % base_name)
|
||||||
|
return None
|
||||||
|
|
||||||
|
if "unit" in amf_document.attrib:
|
||||||
|
unit = amf_document.attrib["unit"].lower()
|
||||||
|
else:
|
||||||
|
unit = "millimeter"
|
||||||
|
if unit == "millimeter":
|
||||||
|
scale = 1.0
|
||||||
|
elif unit == "meter":
|
||||||
|
scale = 1000.0
|
||||||
|
elif unit == "inch":
|
||||||
|
scale = 25.4
|
||||||
|
elif unit == "feet":
|
||||||
|
scale = 304.8
|
||||||
|
elif unit == "micron":
|
||||||
|
scale = 0.001
|
||||||
|
else:
|
||||||
|
Logger.log("w", "Unknown unit in amf: %s. Using mm instead." % unit)
|
||||||
|
scale = 1.0
|
||||||
|
|
||||||
|
nodes = []
|
||||||
|
for amf_object in amf_document.iter("object"):
|
||||||
|
for amf_mesh in amf_object.iter("mesh"):
|
||||||
|
amf_mesh_vertices = []
|
||||||
|
for vertices in amf_mesh.iter("vertices"):
|
||||||
|
for vertex in vertices.iter("vertex"):
|
||||||
|
for coordinates in vertex.iter("coordinates"):
|
||||||
|
v = [0.0, 0.0, 0.0]
|
||||||
|
for t in coordinates:
|
||||||
|
if t.tag == "x":
|
||||||
|
v[0] = float(t.text) * scale
|
||||||
|
elif t.tag == "y":
|
||||||
|
v[2] = float(t.text) * scale
|
||||||
|
elif t.tag == "z":
|
||||||
|
v[1] = float(t.text) * scale
|
||||||
|
amf_mesh_vertices.append(v)
|
||||||
|
if not amf_mesh_vertices:
|
||||||
|
continue
|
||||||
|
|
||||||
|
indices = []
|
||||||
|
for volume in amf_mesh.iter("volume"):
|
||||||
|
for triangle in volume.iter("triangle"):
|
||||||
|
f = [0, 0, 0]
|
||||||
|
for t in triangle:
|
||||||
|
if t.tag == "v1":
|
||||||
|
f[0] = int(t.text)
|
||||||
|
elif t.tag == "v2":
|
||||||
|
f[1] = int(t.text)
|
||||||
|
elif t.tag == "v3":
|
||||||
|
f[2] = int(t.text)
|
||||||
|
indices.append(f)
|
||||||
|
|
||||||
|
mesh = trimesh.base.Trimesh(vertices=numpy.array(amf_mesh_vertices, dtype=numpy.float32), faces=numpy.array(indices, dtype=numpy.int32))
|
||||||
|
mesh.merge_vertices()
|
||||||
|
mesh.remove_unreferenced_vertices()
|
||||||
|
mesh.fix_normals()
|
||||||
|
mesh_data = self._toMeshData(mesh)
|
||||||
|
|
||||||
|
new_node = CuraSceneNode()
|
||||||
|
new_node.setSelectable(True)
|
||||||
|
new_node.setMeshData(mesh_data)
|
||||||
|
new_node.setName(base_name if len(nodes)==0 else "%s %d" % (base_name, len(nodes)))
|
||||||
|
new_node.addDecorator(BuildPlateDecorator(CuraApplication.getInstance().getMultiBuildPlateModel().activeBuildPlate))
|
||||||
|
new_node.addDecorator(SliceableObjectDecorator())
|
||||||
|
|
||||||
|
nodes.append(new_node)
|
||||||
|
|
||||||
|
if not nodes:
|
||||||
|
Logger.log("e", "No meshes in file %s" % base_name)
|
||||||
|
return None
|
||||||
|
|
||||||
|
if len(nodes) == 1:
|
||||||
|
return nodes[0]
|
||||||
|
|
||||||
|
# Add all scenenodes to a group so they stay together
|
||||||
|
group_node = CuraSceneNode()
|
||||||
|
group_node.addDecorator(GroupDecorator())
|
||||||
|
group_node.addDecorator(ConvexHullDecorator())
|
||||||
|
group_node.addDecorator(BuildPlateDecorator(CuraApplication.getInstance().getMultiBuildPlateModel().activeBuildPlate))
|
||||||
|
|
||||||
|
for node in nodes:
|
||||||
|
node.setParent(group_node)
|
||||||
|
|
||||||
|
return group_node
|
||||||
|
|
||||||
|
def _toMeshData(self, tri_node: trimesh.base.Trimesh) -> MeshData:
|
||||||
|
tri_faces = tri_node.faces
|
||||||
|
tri_vertices = tri_node.vertices
|
||||||
|
|
||||||
|
indices = []
|
||||||
|
vertices = []
|
||||||
|
|
||||||
|
index_count = 0
|
||||||
|
face_count = 0
|
||||||
|
for tri_face in tri_faces:
|
||||||
|
face = []
|
||||||
|
for tri_index in tri_face:
|
||||||
|
vertices.append(tri_vertices[tri_index])
|
||||||
|
face.append(index_count)
|
||||||
|
index_count += 1
|
||||||
|
indices.append(face)
|
||||||
|
face_count += 1
|
||||||
|
|
||||||
|
vertices = numpy.asarray(vertices, dtype=numpy.float32)
|
||||||
|
indices = numpy.asarray(indices, dtype=numpy.int32)
|
||||||
|
normals = calculateNormalsFromIndexedVertices(vertices, indices, face_count)
|
||||||
|
|
||||||
|
mesh_data = MeshData(vertices=vertices, indices=indices, normals=normals)
|
||||||
|
return mesh_data
|
21
plugins/AMFReader/__init__.py
Normal file
21
plugins/AMFReader/__init__.py
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
# Copyright (c) 2019 fieldOfView
|
||||||
|
# Cura is released under the terms of the LGPLv3 or higher.
|
||||||
|
|
||||||
|
from . import AMFReader
|
||||||
|
|
||||||
|
from UM.i18n import i18nCatalog
|
||||||
|
i18n_catalog = i18nCatalog("uranium")
|
||||||
|
|
||||||
|
|
||||||
|
def getMetaData():
|
||||||
|
return {
|
||||||
|
"mesh_reader": [
|
||||||
|
{
|
||||||
|
"extension": "amf",
|
||||||
|
"description": i18n_catalog.i18nc("@item:inlistbox", "AMF File")
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
def register(app):
|
||||||
|
return {"mesh_reader": AMFReader.AMFReader()}
|
7
plugins/AMFReader/plugin.json
Normal file
7
plugins/AMFReader/plugin.json
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"name": "AMF Reader",
|
||||||
|
"author": "fieldOfView",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "Provides support for reading AMF files.",
|
||||||
|
"api": "6.0.0"
|
||||||
|
}
|
|
@ -45,7 +45,7 @@ class DriveApiService:
|
||||||
"Authorization": "Bearer {}".format(access_token)
|
"Authorization": "Bearer {}".format(access_token)
|
||||||
})
|
})
|
||||||
except requests.exceptions.ConnectionError:
|
except requests.exceptions.ConnectionError:
|
||||||
Logger.log("w", "Unable to connect with the server.")
|
Logger.logException("w", "Unable to connect with the server.")
|
||||||
return []
|
return []
|
||||||
|
|
||||||
# HTTP status 300s mean redirection. 400s and 500s are errors.
|
# HTTP status 300s mean redirection. 400s and 500s are errors.
|
||||||
|
@ -98,7 +98,12 @@ class DriveApiService:
|
||||||
# If there is no download URL, we can't restore the backup.
|
# If there is no download URL, we can't restore the backup.
|
||||||
return self._emitRestoreError()
|
return self._emitRestoreError()
|
||||||
|
|
||||||
download_package = requests.get(download_url, stream = True)
|
try:
|
||||||
|
download_package = requests.get(download_url, stream = True)
|
||||||
|
except requests.exceptions.ConnectionError:
|
||||||
|
Logger.logException("e", "Unable to connect with the server")
|
||||||
|
return self._emitRestoreError()
|
||||||
|
|
||||||
if download_package.status_code >= 300:
|
if download_package.status_code >= 300:
|
||||||
# Something went wrong when attempting to download the backup.
|
# Something went wrong when attempting to download the backup.
|
||||||
Logger.log("w", "Could not download backup from url %s: %s", download_url, download_package.text)
|
Logger.log("w", "Could not download backup from url %s: %s", download_url, download_package.text)
|
||||||
|
@ -142,9 +147,14 @@ class DriveApiService:
|
||||||
Logger.log("w", "Could not get access token.")
|
Logger.log("w", "Could not get access token.")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
delete_backup = requests.delete("{}/{}".format(self.BACKUP_URL, backup_id), headers = {
|
try:
|
||||||
"Authorization": "Bearer {}".format(access_token)
|
delete_backup = requests.delete("{}/{}".format(self.BACKUP_URL, backup_id), headers = {
|
||||||
})
|
"Authorization": "Bearer {}".format(access_token)
|
||||||
|
})
|
||||||
|
except requests.exceptions.ConnectionError:
|
||||||
|
Logger.logException("e", "Unable to connect with the server")
|
||||||
|
return False
|
||||||
|
|
||||||
if delete_backup.status_code >= 300:
|
if delete_backup.status_code >= 300:
|
||||||
Logger.log("w", "Could not delete backup: %s", delete_backup.text)
|
Logger.log("w", "Could not delete backup: %s", delete_backup.text)
|
||||||
return False
|
return False
|
||||||
|
@ -159,15 +169,19 @@ class DriveApiService:
|
||||||
if not access_token:
|
if not access_token:
|
||||||
Logger.log("w", "Could not get access token.")
|
Logger.log("w", "Could not get access token.")
|
||||||
return None
|
return None
|
||||||
|
try:
|
||||||
backup_upload_request = requests.put(self.BACKUP_URL, json = {
|
backup_upload_request = requests.put(
|
||||||
"data": {
|
self.BACKUP_URL,
|
||||||
"backup_size": backup_size,
|
json = {"data": {"backup_size": backup_size,
|
||||||
"metadata": backup_metadata
|
"metadata": backup_metadata
|
||||||
}
|
}
|
||||||
}, headers = {
|
},
|
||||||
"Authorization": "Bearer {}".format(access_token)
|
headers = {
|
||||||
})
|
"Authorization": "Bearer {}".format(access_token)
|
||||||
|
})
|
||||||
|
except requests.exceptions.ConnectionError:
|
||||||
|
Logger.logException("e", "Unable to connect with the server")
|
||||||
|
return None
|
||||||
|
|
||||||
# Any status code of 300 or above indicates an error.
|
# Any status code of 300 or above indicates an error.
|
||||||
if backup_upload_request.status_code >= 300:
|
if backup_upload_request.status_code >= 300:
|
||||||
|
|
|
@ -517,9 +517,6 @@ class CuraEngineBackend(QObject, Backend):
|
||||||
self._build_plates_to_be_sliced.append(build_plate_number)
|
self._build_plates_to_be_sliced.append(build_plate_number)
|
||||||
self.printDurationMessage.emit(source_build_plate_number, {}, [])
|
self.printDurationMessage.emit(source_build_plate_number, {}, [])
|
||||||
self.processingProgress.emit(0.0)
|
self.processingProgress.emit(0.0)
|
||||||
self.setState(BackendState.NotStarted)
|
|
||||||
# if not self._use_timer:
|
|
||||||
# With manually having to slice, we want to clear the old invalid layer data.
|
|
||||||
self._clearLayerData(build_plate_changed)
|
self._clearLayerData(build_plate_changed)
|
||||||
|
|
||||||
self._invokeSlice()
|
self._invokeSlice()
|
||||||
|
@ -563,10 +560,10 @@ class CuraEngineBackend(QObject, Backend):
|
||||||
|
|
||||||
## Convenient function: mark everything to slice, emit state and clear layer data
|
## Convenient function: mark everything to slice, emit state and clear layer data
|
||||||
def needsSlicing(self) -> None:
|
def needsSlicing(self) -> None:
|
||||||
|
self.determineAutoSlicing()
|
||||||
self.stopSlicing()
|
self.stopSlicing()
|
||||||
self.markSliceAll()
|
self.markSliceAll()
|
||||||
self.processingProgress.emit(0.0)
|
self.processingProgress.emit(0.0)
|
||||||
self.setState(BackendState.NotStarted)
|
|
||||||
if not self._use_timer:
|
if not self._use_timer:
|
||||||
# With manually having to slice, we want to clear the old invalid layer data.
|
# With manually having to slice, we want to clear the old invalid layer data.
|
||||||
self._clearLayerData()
|
self._clearLayerData()
|
||||||
|
@ -735,6 +732,7 @@ class CuraEngineBackend(QObject, Backend):
|
||||||
"support_interface": message.time_support_interface,
|
"support_interface": message.time_support_interface,
|
||||||
"support": message.time_support,
|
"support": message.time_support,
|
||||||
"skirt": message.time_skirt,
|
"skirt": message.time_skirt,
|
||||||
|
"prime_tower": message.time_prime_tower,
|
||||||
"travel": message.time_travel,
|
"travel": message.time_travel,
|
||||||
"retract": message.time_retract,
|
"retract": message.time_retract,
|
||||||
"none": message.time_none
|
"none": message.time_none
|
||||||
|
|
|
@ -256,10 +256,7 @@ class StartSliceJob(Job):
|
||||||
self._buildGlobalInheritsStackMessage(stack)
|
self._buildGlobalInheritsStackMessage(stack)
|
||||||
|
|
||||||
# Build messages for extruder stacks
|
# Build messages for extruder stacks
|
||||||
# Send the extruder settings in the order of extruder positions. Somehow, if you send e.g. extruder 3 first,
|
for extruder_stack in global_stack.extruderList:
|
||||||
# then CuraEngine can slice with the wrong settings. This I think should be fixed in CuraEngine as well.
|
|
||||||
extruder_stack_list = sorted(list(global_stack.extruders.items()), key = lambda item: int(item[0]))
|
|
||||||
for _, extruder_stack in extruder_stack_list:
|
|
||||||
self._buildExtruderMessage(extruder_stack)
|
self._buildExtruderMessage(extruder_stack)
|
||||||
|
|
||||||
for group in filtered_object_groups:
|
for group in filtered_object_groups:
|
||||||
|
@ -334,25 +331,29 @@ class StartSliceJob(Job):
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
def _cacheAllExtruderSettings(self):
|
||||||
|
global_stack = cast(ContainerStack, CuraApplication.getInstance().getGlobalContainerStack())
|
||||||
|
|
||||||
|
# NB: keys must be strings for the string formatter
|
||||||
|
self._all_extruders_settings = {
|
||||||
|
"-1": self._buildReplacementTokens(global_stack)
|
||||||
|
}
|
||||||
|
for extruder_stack in ExtruderManager.getInstance().getActiveExtruderStacks():
|
||||||
|
extruder_nr = extruder_stack.getProperty("extruder_nr", "value")
|
||||||
|
self._all_extruders_settings[str(extruder_nr)] = self._buildReplacementTokens(extruder_stack)
|
||||||
|
|
||||||
## Replace setting tokens in a piece of g-code.
|
## Replace setting tokens in a piece of g-code.
|
||||||
# \param value A piece of g-code to replace tokens in.
|
# \param value A piece of g-code to replace tokens in.
|
||||||
# \param default_extruder_nr Stack nr to use when no stack nr is specified, defaults to the global stack
|
# \param default_extruder_nr Stack nr to use when no stack nr is specified, defaults to the global stack
|
||||||
def _expandGcodeTokens(self, value: str, default_extruder_nr: int = -1) -> str:
|
def _expandGcodeTokens(self, value: str, default_extruder_nr: int = -1) -> str:
|
||||||
if not self._all_extruders_settings:
|
if not self._all_extruders_settings:
|
||||||
global_stack = cast(ContainerStack, CuraApplication.getInstance().getGlobalContainerStack())
|
self._cacheAllExtruderSettings()
|
||||||
|
|
||||||
# NB: keys must be strings for the string formatter
|
|
||||||
self._all_extruders_settings = {
|
|
||||||
"-1": self._buildReplacementTokens(global_stack)
|
|
||||||
}
|
|
||||||
|
|
||||||
for extruder_stack in ExtruderManager.getInstance().getActiveExtruderStacks():
|
|
||||||
extruder_nr = extruder_stack.getProperty("extruder_nr", "value")
|
|
||||||
self._all_extruders_settings[str(extruder_nr)] = self._buildReplacementTokens(extruder_stack)
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# any setting can be used as a token
|
# any setting can be used as a token
|
||||||
fmt = GcodeStartEndFormatter(default_extruder_nr = default_extruder_nr)
|
fmt = GcodeStartEndFormatter(default_extruder_nr = default_extruder_nr)
|
||||||
|
if self._all_extruders_settings is None:
|
||||||
|
return ""
|
||||||
settings = self._all_extruders_settings.copy()
|
settings = self._all_extruders_settings.copy()
|
||||||
settings["default_extruder_nr"] = default_extruder_nr
|
settings["default_extruder_nr"] = default_extruder_nr
|
||||||
return str(fmt.format(value, **settings))
|
return str(fmt.format(value, **settings))
|
||||||
|
@ -364,8 +365,14 @@ class StartSliceJob(Job):
|
||||||
def _buildExtruderMessage(self, stack: ContainerStack) -> None:
|
def _buildExtruderMessage(self, stack: ContainerStack) -> None:
|
||||||
message = self._slice_message.addRepeatedMessage("extruders")
|
message = self._slice_message.addRepeatedMessage("extruders")
|
||||||
message.id = int(stack.getMetaDataEntry("position"))
|
message.id = int(stack.getMetaDataEntry("position"))
|
||||||
|
if not self._all_extruders_settings:
|
||||||
|
self._cacheAllExtruderSettings()
|
||||||
|
|
||||||
settings = self._buildReplacementTokens(stack)
|
if self._all_extruders_settings is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
extruder_nr = stack.getProperty("extruder_nr", "value")
|
||||||
|
settings = self._all_extruders_settings[str(extruder_nr)].copy()
|
||||||
|
|
||||||
# Also send the material GUID. This is a setting in fdmprinter, but we have no interface for it.
|
# Also send the material GUID. This is a setting in fdmprinter, but we have no interface for it.
|
||||||
settings["material_guid"] = stack.material.getMetaDataEntry("GUID", "")
|
settings["material_guid"] = stack.material.getMetaDataEntry("GUID", "")
|
||||||
|
@ -389,7 +396,13 @@ class StartSliceJob(Job):
|
||||||
# The settings are taken from the global stack. This does not include any
|
# The settings are taken from the global stack. This does not include any
|
||||||
# per-extruder settings or per-object settings.
|
# per-extruder settings or per-object settings.
|
||||||
def _buildGlobalSettingsMessage(self, stack: ContainerStack) -> None:
|
def _buildGlobalSettingsMessage(self, stack: ContainerStack) -> None:
|
||||||
settings = self._buildReplacementTokens(stack)
|
if not self._all_extruders_settings:
|
||||||
|
self._cacheAllExtruderSettings()
|
||||||
|
|
||||||
|
if self._all_extruders_settings is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
settings = self._all_extruders_settings["-1"].copy()
|
||||||
|
|
||||||
# Pre-compute material material_bed_temp_prepend and material_print_temp_prepend
|
# Pre-compute material material_bed_temp_prepend and material_print_temp_prepend
|
||||||
start_gcode = settings["machine_start_gcode"]
|
start_gcode = settings["machine_start_gcode"]
|
||||||
|
|
|
@ -370,6 +370,8 @@ class FlavorParser:
|
||||||
self._layer_type = LayerPolygon.InfillType
|
self._layer_type = LayerPolygon.InfillType
|
||||||
elif type == "SUPPORT-INTERFACE":
|
elif type == "SUPPORT-INTERFACE":
|
||||||
self._layer_type = LayerPolygon.SupportInterfaceType
|
self._layer_type = LayerPolygon.SupportInterfaceType
|
||||||
|
elif type == "PRIME-TOWER":
|
||||||
|
self._layer_type = LayerPolygon.PrimeTowerType
|
||||||
else:
|
else:
|
||||||
Logger.log("w", "Encountered a unknown type (%s) while parsing g-code.", type)
|
Logger.log("w", "Encountered a unknown type (%s) while parsing g-code.", type)
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@ Item
|
||||||
|
|
||||||
property int columnWidth: ((parent.width - 2 * UM.Theme.getSize("default_margin").width) / 2) | 0
|
property int columnWidth: ((parent.width - 2 * UM.Theme.getSize("default_margin").width) / 2) | 0
|
||||||
property int columnSpacing: 3 * screenScaleFactor
|
property int columnSpacing: 3 * screenScaleFactor
|
||||||
property int propertyStoreIndex: manager.storeContainerIndex // definition_changes
|
property int propertyStoreIndex: manager ? manager.storeContainerIndex : 1 // definition_changes
|
||||||
|
|
||||||
property string extruderStackId: ""
|
property string extruderStackId: ""
|
||||||
property int extruderPosition: 0
|
property int extruderPosition: 0
|
||||||
|
|
|
@ -26,7 +26,7 @@ Item
|
||||||
|
|
||||||
property int columnWidth: ((parent.width - 2 * UM.Theme.getSize("default_margin").width) / 2) | 0
|
property int columnWidth: ((parent.width - 2 * UM.Theme.getSize("default_margin").width) / 2) | 0
|
||||||
property int columnSpacing: 3 * screenScaleFactor
|
property int columnSpacing: 3 * screenScaleFactor
|
||||||
property int propertyStoreIndex: manager.storeContainerIndex // definition_changes
|
property int propertyStoreIndex: manager ? manager.storeContainerIndex : 1 // definition_changes
|
||||||
|
|
||||||
property string machineStackId: Cura.MachineManager.activeMachineId
|
property string machineStackId: Cura.MachineManager.activeMachineId
|
||||||
|
|
||||||
|
@ -285,18 +285,30 @@ Item
|
||||||
optionModel: ListModel
|
optionModel: ListModel
|
||||||
{
|
{
|
||||||
id: extruderCountModel
|
id: extruderCountModel
|
||||||
|
|
||||||
Component.onCompleted:
|
Component.onCompleted:
|
||||||
{
|
{
|
||||||
extruderCountModel.clear()
|
update()
|
||||||
|
}
|
||||||
|
|
||||||
|
function update()
|
||||||
|
{
|
||||||
|
clear()
|
||||||
for (var i = 1; i <= Cura.MachineManager.activeMachine.maxExtruderCount; i++)
|
for (var i = 1; i <= Cura.MachineManager.activeMachine.maxExtruderCount; i++)
|
||||||
{
|
{
|
||||||
// Use String as value. JavaScript only has Number. PropertyProvider.setPropertyValue()
|
// Use String as value. JavaScript only has Number. PropertyProvider.setPropertyValue()
|
||||||
// takes a QVariant as value, and Number gets translated into a float. This will cause problem
|
// takes a QVariant as value, and Number gets translated into a float. This will cause problem
|
||||||
// for integer settings such as "Number of Extruders".
|
// for integer settings such as "Number of Extruders".
|
||||||
extruderCountModel.append({ text: String(i), value: String(i) })
|
append({ text: String(i), value: String(i) })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Connections
|
||||||
|
{
|
||||||
|
target: Cura.MachineManager
|
||||||
|
onGlobalContainerChanged: extruderCountModel.update()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,15 @@ Rectangle
|
||||||
id: viewportOverlay
|
id: viewportOverlay
|
||||||
|
|
||||||
property bool isConnected: Cura.MachineManager.activeMachineHasNetworkConnection || Cura.MachineManager.activeMachineHasCloudConnection
|
property bool isConnected: Cura.MachineManager.activeMachineHasNetworkConnection || Cura.MachineManager.activeMachineHasCloudConnection
|
||||||
property bool isNetworkConfigurable: ["Ultimaker 3", "Ultimaker 3 Extended", "Ultimaker S5"].indexOf(Cura.MachineManager.activeMachineDefinitionName) > -1
|
property bool isNetworkConfigurable:
|
||||||
|
{
|
||||||
|
if(Cura.MachineManager.activeMachine === null)
|
||||||
|
{
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return Cura.MachineManager.activeMachine.supportsNetworkConnection
|
||||||
|
}
|
||||||
|
|
||||||
property bool isNetworkConfigured:
|
property bool isNetworkConfigured:
|
||||||
{
|
{
|
||||||
// Readability:
|
// Readability:
|
||||||
|
@ -98,7 +106,6 @@ Rectangle
|
||||||
width: contentWidth
|
width: contentWidth
|
||||||
}
|
}
|
||||||
|
|
||||||
// CASE 3: CAN NOT MONITOR
|
|
||||||
Label
|
Label
|
||||||
{
|
{
|
||||||
id: noNetworkLabel
|
id: noNetworkLabel
|
||||||
|
@ -106,24 +113,8 @@ Rectangle
|
||||||
{
|
{
|
||||||
horizontalCenter: parent.horizontalCenter
|
horizontalCenter: parent.horizontalCenter
|
||||||
}
|
}
|
||||||
visible: !isNetworkConfigured
|
|
||||||
text: catalog.i18nc("@info", "Please select a network connected printer to monitor.")
|
|
||||||
font: UM.Theme.getFont("medium")
|
|
||||||
color: UM.Theme.getColor("monitor_text_primary")
|
|
||||||
wrapMode: Text.WordWrap
|
|
||||||
width: contentWidth
|
|
||||||
lineHeight: UM.Theme.getSize("monitor_text_line_large").height
|
|
||||||
lineHeightMode: Text.FixedHeight
|
|
||||||
}
|
|
||||||
Label
|
|
||||||
{
|
|
||||||
id: noNetworkUltimakerLabel
|
|
||||||
anchors
|
|
||||||
{
|
|
||||||
horizontalCenter: parent.horizontalCenter
|
|
||||||
}
|
|
||||||
visible: !isNetworkConfigured && isNetworkConfigurable
|
visible: !isNetworkConfigured && isNetworkConfigurable
|
||||||
text: catalog.i18nc("@info", "Please connect your Ultimaker printer to your local network.")
|
text: catalog.i18nc("@info", "Please connect your printer to the network.")
|
||||||
font: UM.Theme.getFont("medium")
|
font: UM.Theme.getFont("medium")
|
||||||
color: UM.Theme.getColor("monitor_text_primary")
|
color: UM.Theme.getColor("monitor_text_primary")
|
||||||
wrapMode: Text.WordWrap
|
wrapMode: Text.WordWrap
|
||||||
|
@ -135,7 +126,7 @@ Rectangle
|
||||||
{
|
{
|
||||||
anchors
|
anchors
|
||||||
{
|
{
|
||||||
left: noNetworkUltimakerLabel.left
|
left: noNetworkLabel.left
|
||||||
}
|
}
|
||||||
visible: !isNetworkConfigured && isNetworkConfigurable
|
visible: !isNetworkConfigured && isNetworkConfigurable
|
||||||
height: UM.Theme.getSize("monitor_text_line").height
|
height: UM.Theme.getSize("monitor_text_line").height
|
||||||
|
@ -160,7 +151,7 @@ Rectangle
|
||||||
verticalCenter: externalLinkIcon.verticalCenter
|
verticalCenter: externalLinkIcon.verticalCenter
|
||||||
}
|
}
|
||||||
color: UM.Theme.getColor("monitor_text_link")
|
color: UM.Theme.getColor("monitor_text_link")
|
||||||
font: UM.Theme.getFont("medium") // 14pt, regular
|
font: UM.Theme.getFont("medium")
|
||||||
linkColor: UM.Theme.getColor("monitor_text_link")
|
linkColor: UM.Theme.getColor("monitor_text_link")
|
||||||
text: catalog.i18nc("@label link to technical assistance", "View user manuals online")
|
text: catalog.i18nc("@label link to technical assistance", "View user manuals online")
|
||||||
renderType: Text.NativeRendering
|
renderType: Text.NativeRendering
|
||||||
|
@ -170,14 +161,8 @@ Rectangle
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
onClicked: Qt.openUrlExternally("https://ultimaker.com/en/resources/manuals/ultimaker-3d-printers")
|
onClicked: Qt.openUrlExternally("https://ultimaker.com/en/resources/manuals/ultimaker-3d-printers")
|
||||||
onEntered:
|
onEntered: manageQueueText.font.underline = true
|
||||||
{
|
onExited: manageQueueText.font.underline = false
|
||||||
manageQueueText.font.underline = true
|
|
||||||
}
|
|
||||||
onExited:
|
|
||||||
{
|
|
||||||
manageQueueText.font.underline = false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
# Copyright (c) 2019 Ultimaker B.V.
|
# Copyright (c) 2019 Ultimaker B.V.
|
||||||
# The PostProcessingPlugin is released under the terms of the AGPLv3 or higher.
|
# The PostProcessingPlugin is released under the terms of the AGPLv3 or higher.
|
||||||
|
|
||||||
from typing import Optional, Tuple
|
from typing import List
|
||||||
|
|
||||||
from UM.Logger import Logger
|
|
||||||
from ..Script import Script
|
from ..Script import Script
|
||||||
|
|
||||||
class FilamentChange(Script):
|
class FilamentChange(Script):
|
||||||
|
@ -65,9 +63,10 @@ class FilamentChange(Script):
|
||||||
}
|
}
|
||||||
}"""
|
}"""
|
||||||
|
|
||||||
def execute(self, data: list):
|
## Inserts the filament change g-code at specific layer numbers.
|
||||||
|
# \param data A list of layers of g-code.
|
||||||
"""data is a list. Each index contains a layer"""
|
# \return A similar list, with filament change commands inserted.
|
||||||
|
def execute(self, data: List[str]):
|
||||||
layer_nums = self.getSettingValueByKey("layer_number")
|
layer_nums = self.getSettingValueByKey("layer_number")
|
||||||
initial_retract = self.getSettingValueByKey("initial_retract")
|
initial_retract = self.getSettingValueByKey("initial_retract")
|
||||||
later_retract = self.getSettingValueByKey("later_retract")
|
later_retract = self.getSettingValueByKey("later_retract")
|
||||||
|
@ -88,32 +87,13 @@ class FilamentChange(Script):
|
||||||
if y_pos is not None:
|
if y_pos is not None:
|
||||||
color_change = color_change + (" Y%.2f" % y_pos)
|
color_change = color_change + (" Y%.2f" % y_pos)
|
||||||
|
|
||||||
color_change = color_change + " ; Generated by FilamentChange plugin"
|
color_change = color_change + " ; Generated by FilamentChange plugin\n"
|
||||||
|
|
||||||
layer_targets = layer_nums.split(",")
|
layer_targets = layer_nums.split(",")
|
||||||
if len(layer_targets) > 0:
|
if len(layer_targets) > 0:
|
||||||
for layer_num in layer_targets:
|
for layer_num in layer_targets:
|
||||||
layer_num = int(layer_num.strip())
|
layer_num = int(layer_num.strip()) + 1
|
||||||
if layer_num <= len(data):
|
if layer_num <= len(data):
|
||||||
index, layer_data = self._searchLayerData(data, layer_num - 1)
|
data[layer_num] = color_change + data[layer_num]
|
||||||
if layer_data is None:
|
|
||||||
Logger.log("e", "Could not find the layer {layer_num}".format(layer_num = layer_num))
|
|
||||||
continue
|
|
||||||
lines = layer_data.split("\n")
|
|
||||||
lines.insert(2, color_change)
|
|
||||||
final_line = "\n".join(lines)
|
|
||||||
data[index] = final_line
|
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
## This method returns the data corresponding with the indicated layer number, looking in the gcode for
|
|
||||||
# the occurrence of this layer number.
|
|
||||||
def _searchLayerData(self, data: list, layer_num: int) -> Tuple[int, Optional[str]]:
|
|
||||||
for index, layer_data in enumerate(data):
|
|
||||||
first_line = layer_data.split("\n")[0]
|
|
||||||
# The first line should contain the layer number at the beginning.
|
|
||||||
if first_line[:len(self._layer_keyword)] == self._layer_keyword:
|
|
||||||
# If found the layer that we are looking for, then return the data
|
|
||||||
if first_line[len(self._layer_keyword):] == str(layer_num):
|
|
||||||
return index, layer_data
|
|
||||||
return 0, None
|
|
|
@ -20,11 +20,19 @@ Item
|
||||||
name: "cura"
|
name: "cura"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
anchors
|
||||||
|
{
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
leftMargin: UM.Theme.getSize("wide_margin").width
|
||||||
|
rightMargin: UM.Theme.getSize("wide_margin").width
|
||||||
|
}
|
||||||
|
|
||||||
// Item to ensure that all of the buttons are nicely centered.
|
// Item to ensure that all of the buttons are nicely centered.
|
||||||
Item
|
Item
|
||||||
{
|
{
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
width: openFileButton.width + itemRow.width + UM.Theme.getSize("default_margin").width
|
width: parent.width - 2 * UM.Theme.getSize("wide_margin").width
|
||||||
height: parent.height
|
height: parent.height
|
||||||
|
|
||||||
RowLayout
|
RowLayout
|
||||||
|
@ -32,9 +40,9 @@ Item
|
||||||
id: itemRow
|
id: itemRow
|
||||||
|
|
||||||
anchors.left: openFileButton.right
|
anchors.left: openFileButton.right
|
||||||
|
anchors.right: parent.right
|
||||||
anchors.leftMargin: UM.Theme.getSize("default_margin").width
|
anchors.leftMargin: UM.Theme.getSize("default_margin").width
|
||||||
|
|
||||||
width: Math.round(0.9 * prepareMenu.width)
|
|
||||||
height: parent.height
|
height: parent.height
|
||||||
spacing: 0
|
spacing: 0
|
||||||
|
|
||||||
|
|
|
@ -20,15 +20,21 @@ Item
|
||||||
name: "cura"
|
name: "cura"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
anchors
|
||||||
|
{
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
leftMargin: UM.Theme.getSize("wide_margin").width
|
||||||
|
rightMargin: UM.Theme.getSize("wide_margin").width
|
||||||
|
}
|
||||||
|
|
||||||
Row
|
Row
|
||||||
{
|
{
|
||||||
id: stageMenuRow
|
id: stageMenuRow
|
||||||
anchors.centerIn: parent
|
|
||||||
height: parent.height
|
|
||||||
width: childrenRect.width
|
|
||||||
|
|
||||||
// We want this row to have a preferred with equals to the 85% of the parent
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
property int preferredWidth: Math.round(0.85 * previewMenu.width)
|
width: parent.width - 2 * UM.Theme.getSize("wide_margin").width
|
||||||
|
height: parent.height
|
||||||
|
|
||||||
Cura.ViewsSelector
|
Cura.ViewsSelector
|
||||||
{
|
{
|
||||||
|
@ -49,12 +55,12 @@ Item
|
||||||
color: UM.Theme.getColor("lining")
|
color: UM.Theme.getColor("lining")
|
||||||
}
|
}
|
||||||
|
|
||||||
// This component will grow freely up to complete the preferredWidth of the row.
|
// This component will grow freely up to complete the width of the row.
|
||||||
Loader
|
Loader
|
||||||
{
|
{
|
||||||
id: viewPanel
|
id: viewPanel
|
||||||
height: parent.height
|
height: parent.height
|
||||||
width: source != "" ? (stageMenuRow.preferredWidth - viewsSelector.width - printSetupSelectorItem.width - 2 * UM.Theme.getSize("default_lining").width) : 0
|
width: source != "" ? (previewMenu.width - viewsSelector.width - printSetupSelectorItem.width - 2 * (UM.Theme.getSize("wide_margin").width + UM.Theme.getSize("default_lining").width)) : 0
|
||||||
source: UM.Controller.activeView != null && UM.Controller.activeView.stageMenuComponent != null ? UM.Controller.activeView.stageMenuComponent : ""
|
source: UM.Controller.activeView != null && UM.Controller.activeView.stageMenuComponent != null ? UM.Controller.activeView.stageMenuComponent : ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,8 @@ Cura.ExpandableComponent
|
||||||
{
|
{
|
||||||
id: base
|
id: base
|
||||||
|
|
||||||
|
dragPreferencesNamePrefix: "view/colorscheme"
|
||||||
|
|
||||||
contentHeaderTitle: catalog.i18nc("@label", "Color scheme")
|
contentHeaderTitle: catalog.i18nc("@label", "Color scheme")
|
||||||
|
|
||||||
Connections
|
Connections
|
||||||
|
|
|
@ -71,7 +71,7 @@ Window
|
||||||
left: parent.left
|
left: parent.left
|
||||||
right: parent.right
|
right: parent.right
|
||||||
}
|
}
|
||||||
text: catalog.i18nc("@text:window", "Cura sends anonymous data to Ultimaker in order to improve the print quality and user experience. Below is an example of all the data that is sent.")
|
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:")
|
||||||
wrapMode: Text.WordWrap
|
wrapMode: Text.WordWrap
|
||||||
renderType: Text.NativeRendering
|
renderType: Text.NativeRendering
|
||||||
}
|
}
|
||||||
|
@ -89,6 +89,8 @@ Window
|
||||||
}
|
}
|
||||||
|
|
||||||
textArea.text: manager.getExampleData()
|
textArea.text: manager.getExampleData()
|
||||||
|
textArea.textFormat: Text.RichText
|
||||||
|
textArea.wrapMode: Text.Wrap
|
||||||
textArea.readOnly: true
|
textArea.readOnly: true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -104,7 +106,7 @@ Window
|
||||||
Cura.RadioButton
|
Cura.RadioButton
|
||||||
{
|
{
|
||||||
id: dontSendButton
|
id: dontSendButton
|
||||||
text: catalog.i18nc("@text:window", "I don't want to send this data")
|
text: catalog.i18nc("@text:window", "I don't want to send anonymous data")
|
||||||
onClicked:
|
onClicked:
|
||||||
{
|
{
|
||||||
baseDialog.allowSendData = !checked
|
baseDialog.allowSendData = !checked
|
||||||
|
@ -113,7 +115,7 @@ Window
|
||||||
Cura.RadioButton
|
Cura.RadioButton
|
||||||
{
|
{
|
||||||
id: allowSendButton
|
id: allowSendButton
|
||||||
text: catalog.i18nc("@text:window", "Allow sending this data to Ultimaker and help us improve Cura")
|
text: catalog.i18nc("@text:window", "Allow sending anonymous data")
|
||||||
onClicked:
|
onClicked:
|
||||||
{
|
{
|
||||||
baseDialog.allowSendData = checked
|
baseDialog.allowSendData = checked
|
||||||
|
|
|
@ -77,7 +77,7 @@ class SliceInfo(QObject, Extension):
|
||||||
if not plugin_path:
|
if not plugin_path:
|
||||||
Logger.log("e", "Could not get plugin path!", self.getPluginId())
|
Logger.log("e", "Could not get plugin path!", self.getPluginId())
|
||||||
return None
|
return None
|
||||||
file_path = os.path.join(plugin_path, "example_data.json")
|
file_path = os.path.join(plugin_path, "example_data.html")
|
||||||
if file_path:
|
if file_path:
|
||||||
with open(file_path, "r", encoding = "utf-8") as f:
|
with open(file_path, "r", encoding = "utf-8") as f:
|
||||||
self._example_data_content = f.read()
|
self._example_data_content = f.read()
|
||||||
|
|
64
plugins/SliceInfoPlugin/example_data.html
Normal file
64
plugins/SliceInfoPlugin/example_data.html
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
<b>Cura Version:</b> 4.0<br/>
|
||||||
|
<b>Operating System:</b> Windows 10<br/>
|
||||||
|
<b>Language:</b> en_US<br/>
|
||||||
|
<b>Machine Type:</b> Ultimaker S5<br/>
|
||||||
|
<b>Quality Profile:</b> Fast<br/>
|
||||||
|
<b>Using Custom Settings:</b> No
|
||||||
|
|
||||||
|
<h3>Extruder 1:</h3>
|
||||||
|
<ul>
|
||||||
|
<li><b>Material Type:</b> PLA</li>
|
||||||
|
<li><b>Print Core:</b> AA 0.4</li>
|
||||||
|
<li><b>Material Used:</b> 1240 mm</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h3>Extruder 2:</h3>
|
||||||
|
<ul>
|
||||||
|
<li><b>Material Type:</b> PVA</li>
|
||||||
|
<li><b>Print Core:</b> BB 0.4</li>
|
||||||
|
<li><b>Material Used:</b> 432 mm</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h3>Print Settings:</h3>
|
||||||
|
<ul>
|
||||||
|
<li><b>Layer Height:</b> 0.15</li>
|
||||||
|
<li><b>Wall Line Count:</b> 3</li>
|
||||||
|
<li><b>Enable Retraction:</b> no</li>
|
||||||
|
<li><b>Infill Density:</b> 20%</li>
|
||||||
|
<li><b>Infill Pattern:</b> triangles</li>
|
||||||
|
<li><b>Gradual Infill Steps:</b> 0</li>
|
||||||
|
<li><b>Printing Temperature:</b> 220 °C</li>
|
||||||
|
<li><b>Generate Support:</b> yes</li>
|
||||||
|
<li><b>Support Extruder:</b> 1</li>
|
||||||
|
<li><b>Build Plate Adhesion Type:</b> brim</li>
|
||||||
|
<li><b>Enable Prime Tower:</b> yes</li>
|
||||||
|
<li><b>Print Sequence:</b> All at once</li>
|
||||||
|
<li>...</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h3>Model Information:</h3>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<b>Model 1</b>
|
||||||
|
<ul>
|
||||||
|
<li><b>Hash:</b> b72789b9b...</li>
|
||||||
|
<li><b>Transformation:</b> [transformation matrix]</li>
|
||||||
|
<li><b>Bounding Box:</b> [minimum x, y, z; maximum x, y, z]</li>
|
||||||
|
<li><b>Is Helper Mesh:</b> no</li>
|
||||||
|
<li><b>Helper Mesh Type:</b> support mesh</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h3>Print Times:</h3>
|
||||||
|
<ul>
|
||||||
|
<li>Infill: 61200 sec.</li>
|
||||||
|
<li>Support: 25480 sec.</li>
|
||||||
|
<li>Travel: 6224 sec.</li>
|
||||||
|
<li>Walls: 10225 sec.</li>
|
||||||
|
<li>Total: 103129 sec.</li>
|
||||||
|
</ul>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -1,114 +0,0 @@
|
||||||
{
|
|
||||||
"time_stamp": 1523973715.486928,
|
|
||||||
"schema_version": 0,
|
|
||||||
"cura_version": "3.3",
|
|
||||||
"active_mode": "custom",
|
|
||||||
"machine_settings_changed_by_user": true,
|
|
||||||
"language": "en_US",
|
|
||||||
"os": {
|
|
||||||
"type": "Linux",
|
|
||||||
"version": "#43~16.04.1-Ubuntu SMP Wed Mar 14 17:48:43 UTC 2018"
|
|
||||||
},
|
|
||||||
"active_machine": {
|
|
||||||
"definition_id": "ultimaker3",
|
|
||||||
"manufacturer": "Ultimaker B.V."
|
|
||||||
},
|
|
||||||
"extruders": [
|
|
||||||
{
|
|
||||||
"active": true,
|
|
||||||
"material": {
|
|
||||||
"GUID": "506c9f0d-e3aa-4bd4-b2d2-23e2425b1aa9",
|
|
||||||
"type": "PLA",
|
|
||||||
"brand": "Generic"
|
|
||||||
},
|
|
||||||
"material_used": 0.84,
|
|
||||||
"variant": "AA 0.4",
|
|
||||||
"nozzle_size": 0.4,
|
|
||||||
"extruder_settings": {
|
|
||||||
"wall_line_count": 3,
|
|
||||||
"retraction_enable": true,
|
|
||||||
"infill_sparse_density": 30,
|
|
||||||
"infill_pattern": "triangles",
|
|
||||||
"gradual_infill_steps": 0,
|
|
||||||
"default_material_print_temperature": 200,
|
|
||||||
"material_print_temperature": 200
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"active": false,
|
|
||||||
"material": {
|
|
||||||
"GUID": "86a89ceb-4159-47f6-ab97-e9953803d70f",
|
|
||||||
"type": "PVA",
|
|
||||||
"brand": "Generic"
|
|
||||||
},
|
|
||||||
"material_used": 0.5,
|
|
||||||
"variant": "BB 0.4",
|
|
||||||
"nozzle_size": 0.4,
|
|
||||||
"extruder_settings": {
|
|
||||||
"wall_line_count": 3,
|
|
||||||
"retraction_enable": true,
|
|
||||||
"infill_sparse_density": 20,
|
|
||||||
"infill_pattern": "triangles",
|
|
||||||
"gradual_infill_steps": 0,
|
|
||||||
"default_material_print_temperature": 215,
|
|
||||||
"material_print_temperature": 220
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"quality_profile": "fast",
|
|
||||||
"user_modified_setting_keys": ["layer_height", "wall_line_width", "infill_sparse_density"],
|
|
||||||
"models": [
|
|
||||||
{
|
|
||||||
"hash": "b72789b9beb5366dff20b1cf501020c3d4d4df7dc2295ecd0fddd0a6436df070",
|
|
||||||
"bounding_box": {
|
|
||||||
"minimum": {
|
|
||||||
"x": -10.0,
|
|
||||||
"y": 0.0,
|
|
||||||
"z": -5.0
|
|
||||||
},
|
|
||||||
"maximum": {
|
|
||||||
"x": 9.999999046325684,
|
|
||||||
"y": 40.0,
|
|
||||||
"z": 5.0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"transformation": {
|
|
||||||
"data": "[[ 1. 0. 0. 0.] [ 0. 1. 0. 20.] [ 0. 0. 1. 0.] [ 0. 0. 0. 1.]]"
|
|
||||||
},
|
|
||||||
"extruder": 0,
|
|
||||||
"model_settings": {
|
|
||||||
"support_enabled": true,
|
|
||||||
"support_extruder_nr": 1,
|
|
||||||
"infill_mesh": false,
|
|
||||||
"cutting_mesh": false,
|
|
||||||
"support_mesh": false,
|
|
||||||
"anti_overhang_mesh": false,
|
|
||||||
"wall_line_count": 3,
|
|
||||||
"retraction_enable": true,
|
|
||||||
"infill_sparse_density": 30,
|
|
||||||
"infill_pattern": "triangles",
|
|
||||||
"gradual_infill_steps": 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"print_times": {
|
|
||||||
"travel": 187,
|
|
||||||
"support": 825,
|
|
||||||
"infill": 351,
|
|
||||||
"total": 7234
|
|
||||||
},
|
|
||||||
"print_settings": {
|
|
||||||
"layer_height": 0.15,
|
|
||||||
"support_enabled": true,
|
|
||||||
"support_extruder_nr": 1,
|
|
||||||
"adhesion_type": "brim",
|
|
||||||
"wall_line_count": 3,
|
|
||||||
"retraction_enable": true,
|
|
||||||
"prime_tower_enable": true,
|
|
||||||
"infill_sparse_density": 20,
|
|
||||||
"infill_pattern": "triangles",
|
|
||||||
"gradual_infill_steps": 0,
|
|
||||||
"print_sequence": "all_at_once"
|
|
||||||
},
|
|
||||||
"output_to": "LocalFileOutputDevice"
|
|
||||||
}
|
|
|
@ -19,7 +19,7 @@ import math
|
||||||
class SolidView(View):
|
class SolidView(View):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
application = Application.getInstance()
|
application = Application.getInstance()
|
||||||
application.getPreferences().addPreference("view/show_overhang", True)
|
application.getPreferences().addPreference("view/show_overhang", True)
|
||||||
application.globalContainerStackChanged.connect(self._onGlobalContainerChanged)
|
application.globalContainerStackChanged.connect(self._onGlobalContainerChanged)
|
||||||
self._enabled_shader = None
|
self._enabled_shader = None
|
||||||
|
@ -58,7 +58,7 @@ class SolidView(View):
|
||||||
global_container_stack = Application.getInstance().getGlobalContainerStack()
|
global_container_stack = Application.getInstance().getGlobalContainerStack()
|
||||||
if global_container_stack:
|
if global_container_stack:
|
||||||
support_extruder_nr = global_container_stack.getExtruderPositionValueWithDefault("support_extruder_nr")
|
support_extruder_nr = global_container_stack.getExtruderPositionValueWithDefault("support_extruder_nr")
|
||||||
support_angle_stack = Application.getInstance().getExtruderManager().getExtruderStack(support_extruder_nr)
|
support_angle_stack = global_container_stack.extruders.get(str(support_extruder_nr))
|
||||||
if support_angle_stack:
|
if support_angle_stack:
|
||||||
self._support_angle = support_angle_stack.getProperty("support_angle", "value")
|
self._support_angle = support_angle_stack.getProperty("support_angle", "value")
|
||||||
|
|
||||||
|
|
|
@ -109,6 +109,8 @@ Item
|
||||||
top: description.bottom
|
top: description.bottom
|
||||||
left: properties.right
|
left: properties.right
|
||||||
leftMargin: UM.Theme.getSize("default_margin").width
|
leftMargin: UM.Theme.getSize("default_margin").width
|
||||||
|
right: parent.right
|
||||||
|
rightMargin: UM.Theme.getSize("default_margin").width
|
||||||
topMargin: UM.Theme.getSize("default_margin").height
|
topMargin: UM.Theme.getSize("default_margin").height
|
||||||
}
|
}
|
||||||
spacing: Math.floor(UM.Theme.getSize("narrow_margin").height)
|
spacing: Math.floor(UM.Theme.getSize("narrow_margin").height)
|
||||||
|
@ -123,6 +125,8 @@ Item
|
||||||
}
|
}
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
width: parent.width
|
||||||
|
elide: Text.ElideRight
|
||||||
font: UM.Theme.getFont("default")
|
font: UM.Theme.getFont("default")
|
||||||
color: UM.Theme.getColor("text")
|
color: UM.Theme.getColor("text")
|
||||||
linkColor: UM.Theme.getColor("text_link")
|
linkColor: UM.Theme.getColor("text_link")
|
||||||
|
|
|
@ -40,6 +40,7 @@ Column
|
||||||
|
|
||||||
Cura.SecondaryButton
|
Cura.SecondaryButton
|
||||||
{
|
{
|
||||||
|
id: installedButton
|
||||||
visible: installed
|
visible: installed
|
||||||
onClicked: toolbox.viewCategory = "installed"
|
onClicked: toolbox.viewCategory = "installed"
|
||||||
text: catalog.i18nc("@action:button", "Installed")
|
text: catalog.i18nc("@action:button", "Installed")
|
||||||
|
|
|
@ -105,6 +105,7 @@ class Toolbox(QObject, Extension):
|
||||||
|
|
||||||
self._application.initializationFinished.connect(self._onAppInitialized)
|
self._application.initializationFinished.connect(self._onAppInitialized)
|
||||||
self._application.getCuraAPI().account.loginStateChanged.connect(self._updateRequestHeader)
|
self._application.getCuraAPI().account.loginStateChanged.connect(self._updateRequestHeader)
|
||||||
|
self._application.getCuraAPI().account.accessTokenChanged.connect(self._updateRequestHeader)
|
||||||
|
|
||||||
# Signals:
|
# Signals:
|
||||||
# --------------------------------------------------------------------------
|
# --------------------------------------------------------------------------
|
||||||
|
|
|
@ -1,10 +1,15 @@
|
||||||
#Copyright (c) 2019 Ultimaker B.V.
|
#Copyright (c) 2019 Ultimaker B.V.
|
||||||
#Cura is released under the terms of the LGPLv3 or higher.
|
#Cura is released under the terms of the LGPLv3 or higher.
|
||||||
|
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from UM.Logger import Logger
|
||||||
|
try:
|
||||||
|
from . import UFPReader
|
||||||
|
except ImportError:
|
||||||
|
Logger.log("w", "Could not import UFPReader; libCharon may be missing")
|
||||||
|
|
||||||
from UM.i18n import i18nCatalog
|
from UM.i18n import i18nCatalog
|
||||||
|
|
||||||
from . import UFPReader
|
|
||||||
|
|
||||||
i18n_catalog = i18nCatalog("cura")
|
i18n_catalog = i18nCatalog("cura")
|
||||||
|
|
||||||
|
|
||||||
|
@ -21,6 +26,9 @@ def getMetaData():
|
||||||
|
|
||||||
|
|
||||||
def register(app):
|
def register(app):
|
||||||
|
if "UFPReader.UFPReader" not in sys.modules:
|
||||||
|
return {}
|
||||||
|
|
||||||
app.addNonSliceableExtension(".ufp")
|
app.addNonSliceableExtension(".ufp")
|
||||||
return {"mesh_reader": UFPReader.UFPReader()}
|
return {"mesh_reader": UFPReader.UFPReader()}
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
// Copyright (c) 2018 Ultimaker B.V.
|
// Copyright (c) 2019 Ultimaker B.V.
|
||||||
// Cura is released under the terms of the LGPLv3 or higher.
|
// Cura is released under the terms of the LGPLv3 or higher.
|
||||||
|
|
||||||
import UM 1.2 as UM
|
import UM 1.2 as UM
|
||||||
import Cura 1.0 as Cura
|
import Cura 1.5 as Cura
|
||||||
|
|
||||||
import QtQuick 2.2
|
import QtQuick 2.2
|
||||||
import QtQuick.Controls 1.1
|
import QtQuick.Controls 1.1
|
||||||
|
@ -14,22 +14,27 @@ Cura.MachineAction
|
||||||
{
|
{
|
||||||
id: base
|
id: base
|
||||||
anchors.fill: parent;
|
anchors.fill: parent;
|
||||||
|
property alias currentItemIndex: listview.currentIndex
|
||||||
property var selectedDevice: null
|
property var selectedDevice: null
|
||||||
property bool completeProperties: true
|
property bool completeProperties: true
|
||||||
|
|
||||||
|
// For validating IP addresses
|
||||||
|
property var networkingUtil: Cura.NetworkingUtil {}
|
||||||
|
|
||||||
function connectToPrinter()
|
function connectToPrinter()
|
||||||
{
|
{
|
||||||
if(base.selectedDevice && base.completeProperties)
|
if(base.selectedDevice && base.completeProperties)
|
||||||
{
|
{
|
||||||
var printerKey = base.selectedDevice.key
|
var printerKey = base.selectedDevice.key
|
||||||
var printerName = base.selectedDevice.name // TODO To change when the groups have a name
|
var printerName = base.selectedDevice.name // TODO To change when the groups have a name
|
||||||
if (manager.getStoredKey() != printerKey)
|
if (Cura.API.machines.getCurrentMachine().um_network_key != printerKey) // TODO: change to hostname
|
||||||
{
|
{
|
||||||
// Check if there is another instance with the same key
|
// Check if there is another instance with the same key
|
||||||
if (!manager.existsKey(printerKey))
|
if (!manager.existsKey(printerKey))
|
||||||
{
|
{
|
||||||
manager.associateActiveMachineWithPrinterDevice(base.selectedDevice)
|
Cura.API.machines.addOutputDeviceToCurrentMachine(base.selectedDevice)
|
||||||
manager.setGroupName(printerName) // TODO To change when the groups have a name
|
Cura.API.machines.setCurrentMachineGroupName(printerName) // TODO To change when the groups have a name
|
||||||
|
manager.refreshConnections()
|
||||||
completed()
|
completed()
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -152,7 +157,7 @@ Cura.MachineAction
|
||||||
var selectedKey = manager.getLastManualEntryKey()
|
var selectedKey = manager.getLastManualEntryKey()
|
||||||
// If there is no last manual entry key, then we select the stored key (if any)
|
// If there is no last manual entry key, then we select the stored key (if any)
|
||||||
if (selectedKey == "")
|
if (selectedKey == "")
|
||||||
selectedKey = manager.getStoredKey()
|
selectedKey = Cura.API.machines.getCurrentMachine().um_network_key // TODO: change to host name
|
||||||
for(var i = 0; i < model.length; i++) {
|
for(var i = 0; i < model.length; i++) {
|
||||||
if(model[i].key == selectedKey)
|
if(model[i].key == selectedKey)
|
||||||
{
|
{
|
||||||
|
@ -342,6 +347,17 @@ Cura.MachineAction
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MessageDialog
|
||||||
|
{
|
||||||
|
id: invalidIPAddressMessageDialog
|
||||||
|
x: (parent.x + (parent.width) / 2) | 0
|
||||||
|
y: (parent.y + (parent.height) / 2) | 0
|
||||||
|
title: catalog.i18nc("@title:window", "Invalid IP address")
|
||||||
|
text: catalog.i18nc("@text", "Please enter a valid IP address.")
|
||||||
|
icon: StandardIcon.Warning
|
||||||
|
standardButtons: StandardButton.Ok
|
||||||
|
}
|
||||||
|
|
||||||
UM.Dialog
|
UM.Dialog
|
||||||
{
|
{
|
||||||
id: manualPrinterDialog
|
id: manualPrinterDialog
|
||||||
|
@ -404,6 +420,26 @@ Cura.MachineAction
|
||||||
text: catalog.i18nc("@action:button", "OK")
|
text: catalog.i18nc("@action:button", "OK")
|
||||||
onClicked:
|
onClicked:
|
||||||
{
|
{
|
||||||
|
// Validate the input first
|
||||||
|
if (!networkingUtil.isValidIP(manualPrinterDialog.addressText))
|
||||||
|
{
|
||||||
|
invalidIPAddressMessageDialog.open()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the entered IP address has already been discovered, switch the current item to that item
|
||||||
|
// and do nothing else.
|
||||||
|
for (var i = 0; i < manager.foundDevices.length; i++)
|
||||||
|
{
|
||||||
|
var device = manager.foundDevices[i]
|
||||||
|
if (device.address == manualPrinterDialog.addressText)
|
||||||
|
{
|
||||||
|
currentItemIndex = i
|
||||||
|
manualPrinterDialog.hide()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
manager.setManualDevice(manualPrinterDialog.printerKey, manualPrinterDialog.addressText)
|
manager.setManualDevice(manualPrinterDialog.printerKey, manualPrinterDialog.addressText)
|
||||||
manualPrinterDialog.hide()
|
manualPrinterDialog.hide()
|
||||||
}
|
}
|
||||||
|
|
|
@ -69,6 +69,7 @@ Item
|
||||||
// FIXED-LINE-HEIGHT:
|
// FIXED-LINE-HEIGHT:
|
||||||
height: 18 * screenScaleFactor // TODO: Theme!
|
height: 18 * screenScaleFactor // TODO: Theme!
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
renderType: Text.NativeRendering
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -30,6 +30,21 @@ UM.Dialog
|
||||||
OutputDevice.forceSendJob(printer.activePrintJob.key)
|
OutputDevice.forceSendJob(printer.activePrintJob.key)
|
||||||
overrideConfirmationDialog.close()
|
overrideConfirmationDialog.close()
|
||||||
}
|
}
|
||||||
|
visible:
|
||||||
|
{
|
||||||
|
if (!printer || !printer.activePrintJob)
|
||||||
|
{
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
var canOverride = false
|
||||||
|
for (var i = 0; i < printer.activePrintJob.configurationChanges.length; i++)
|
||||||
|
{
|
||||||
|
var change = printer.activePrintJob.configurationChanges[i]
|
||||||
|
canOverride = canOverride || change.typeOfChange === "material_change";
|
||||||
|
}
|
||||||
|
return canOverride
|
||||||
|
}
|
||||||
},
|
},
|
||||||
Button
|
Button
|
||||||
{
|
{
|
||||||
|
@ -52,6 +67,7 @@ UM.Dialog
|
||||||
bottomMargin: 56 * screenScaleFactor // TODO: Theme!
|
bottomMargin: 56 * screenScaleFactor // TODO: Theme!
|
||||||
}
|
}
|
||||||
wrapMode: Text.WordWrap
|
wrapMode: Text.WordWrap
|
||||||
|
renderType: Text.NativeRendering
|
||||||
text:
|
text:
|
||||||
{
|
{
|
||||||
if (!printer || !printer.activePrintJob)
|
if (!printer || !printer.activePrintJob)
|
||||||
|
|
|
@ -23,6 +23,7 @@ Button
|
||||||
horizontalAlignment: Text.AlignHCenter
|
horizontalAlignment: Text.AlignHCenter
|
||||||
text: base.text
|
text: base.text
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
renderType: Text.NativeRendering;
|
||||||
}
|
}
|
||||||
height: width
|
height: width
|
||||||
hoverEnabled: enabled
|
hoverEnabled: enabled
|
||||||
|
|
|
@ -66,6 +66,7 @@ Item
|
||||||
// FIXED-LINE-HEIGHT:
|
// FIXED-LINE-HEIGHT:
|
||||||
height: parent.height
|
height: parent.height
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
renderType: Text.NativeRendering
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,6 +96,7 @@ Item
|
||||||
// FIXED-LINE-HEIGHT:
|
// FIXED-LINE-HEIGHT:
|
||||||
height: parent.height
|
height: parent.height
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
renderType: Text.NativeRendering
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -48,5 +48,6 @@ Item
|
||||||
x: Math.round(size * 0.25)
|
x: Math.round(size * 0.25)
|
||||||
y: Math.round(size * 0.15625)
|
y: Math.round(size * 0.15625)
|
||||||
visible: position >= 0
|
visible: position >= 0
|
||||||
|
renderType: Text.NativeRendering
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -40,6 +40,7 @@ Item
|
||||||
width: 240 * screenScaleFactor // TODO: Theme!
|
width: 240 * screenScaleFactor // TODO: Theme!
|
||||||
color: UM.Theme.getColor("monitor_tooltip_text")
|
color: UM.Theme.getColor("monitor_tooltip_text")
|
||||||
font: UM.Theme.getFont("default")
|
font: UM.Theme.getFont("default")
|
||||||
|
renderType: Text.NativeRendering
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -71,6 +71,7 @@ Item
|
||||||
// FIXED-LINE-HEIGHT:
|
// FIXED-LINE-HEIGHT:
|
||||||
height: parent.height
|
height: parent.height
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
renderType: Text.NativeRendering
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,6 +99,7 @@ Item
|
||||||
// FIXED-LINE-HEIGHT:
|
// FIXED-LINE-HEIGHT:
|
||||||
height: 18 * screenScaleFactor // TODO: Theme!
|
height: 18 * screenScaleFactor // TODO: Theme!
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
renderType: Text.NativeRendering
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -143,6 +145,7 @@ Item
|
||||||
// FIXED-LINE-HEIGHT:
|
// FIXED-LINE-HEIGHT:
|
||||||
height: parent.height
|
height: parent.height
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
renderType: Text.NativeRendering
|
||||||
}
|
}
|
||||||
|
|
||||||
Row
|
Row
|
||||||
|
@ -158,14 +161,9 @@ Item
|
||||||
spacing: 6 // TODO: Theme!
|
spacing: 6 // TODO: Theme!
|
||||||
visible: printJob
|
visible: printJob
|
||||||
|
|
||||||
Repeater
|
MonitorPrinterPill
|
||||||
{
|
{
|
||||||
id: compatiblePills
|
text: printJob.configuration.printerType
|
||||||
delegate: MonitorPrinterPill
|
|
||||||
{
|
|
||||||
text: modelData
|
|
||||||
}
|
|
||||||
model: printJob ? printJob.compatibleMachineFamilies : []
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -202,6 +200,7 @@ Item
|
||||||
// FIXED-LINE-HEIGHT:
|
// FIXED-LINE-HEIGHT:
|
||||||
height: 18 * screenScaleFactor // TODO: Theme!
|
height: 18 * screenScaleFactor // TODO: Theme!
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
renderType: Text.NativeRendering
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,6 +47,7 @@ Item
|
||||||
// FIXED-LINE-HEIGHT:
|
// FIXED-LINE-HEIGHT:
|
||||||
height: 18 * screenScaleFactor // TODO: Theme!
|
height: 18 * screenScaleFactor // TODO: Theme!
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
renderType: Text.NativeRendering
|
||||||
}
|
}
|
||||||
Label
|
Label
|
||||||
{
|
{
|
||||||
|
@ -99,5 +100,6 @@ Item
|
||||||
// FIXED-LINE-HEIGHT:
|
// FIXED-LINE-HEIGHT:
|
||||||
height: 18 * screenScaleFactor // TODO: Theme!
|
height: 18 * screenScaleFactor // TODO: Theme!
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
renderType: Text.NativeRendering
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -112,6 +112,7 @@ Item
|
||||||
// FIXED-LINE-HEIGHT:
|
// FIXED-LINE-HEIGHT:
|
||||||
height: parent.height
|
height: parent.height
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
renderType: Text.NativeRendering
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -315,6 +316,7 @@ Item
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
visible: text !== ""
|
visible: text !== ""
|
||||||
|
renderType: Text.NativeRendering
|
||||||
}
|
}
|
||||||
|
|
||||||
Item
|
Item
|
||||||
|
@ -356,6 +358,7 @@ Item
|
||||||
// FIXED-LINE-HEIGHT:
|
// FIXED-LINE-HEIGHT:
|
||||||
height: 18 * screenScaleFactor // TODO: Theme!
|
height: 18 * screenScaleFactor // TODO: Theme!
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
renderType: Text.NativeRendering
|
||||||
}
|
}
|
||||||
|
|
||||||
Label
|
Label
|
||||||
|
@ -376,6 +379,7 @@ Item
|
||||||
// FIXED-LINE-HEIGHT:
|
// FIXED-LINE-HEIGHT:
|
||||||
height: 18 * screenScaleFactor // TODO: Theme!
|
height: 18 * screenScaleFactor // TODO: Theme!
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
renderType: Text.NativeRendering
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -403,6 +407,7 @@ Item
|
||||||
// FIXED-LINE-HEIGHT:
|
// FIXED-LINE-HEIGHT:
|
||||||
height: 18 * screenScaleFactor // TODO: Theme!
|
height: 18 * screenScaleFactor // TODO: Theme!
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
renderType: Text.NativeRendering
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -437,6 +442,7 @@ Item
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
horizontalAlignment: Text.AlignHCenter
|
horizontalAlignment: Text.AlignHCenter
|
||||||
height: 18 * screenScaleFactor // TODO: Theme!
|
height: 18 * screenScaleFactor // TODO: Theme!
|
||||||
|
renderType: Text.NativeRendering
|
||||||
}
|
}
|
||||||
implicitHeight: 32 * screenScaleFactor // TODO: Theme!
|
implicitHeight: 32 * screenScaleFactor // TODO: Theme!
|
||||||
implicitWidth: 96 * screenScaleFactor // TODO: Theme!
|
implicitWidth: 96 * screenScaleFactor // TODO: Theme!
|
||||||
|
|
|
@ -43,5 +43,6 @@ Item
|
||||||
text: tagText
|
text: tagText
|
||||||
font.pointSize: 10 // TODO: Theme!
|
font.pointSize: 10 // TODO: Theme!
|
||||||
visible: text !== ""
|
visible: text !== ""
|
||||||
|
renderType: Text.NativeRendering
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -29,6 +29,7 @@ Item
|
||||||
color: UM.Theme.getColor("monitor_text_primary")
|
color: UM.Theme.getColor("monitor_text_primary")
|
||||||
font: UM.Theme.getFont("large")
|
font: UM.Theme.getFont("large")
|
||||||
text: catalog.i18nc("@label", "Queued")
|
text: catalog.i18nc("@label", "Queued")
|
||||||
|
renderType: Text.NativeRendering
|
||||||
}
|
}
|
||||||
|
|
||||||
Item
|
Item
|
||||||
|
@ -109,6 +110,7 @@ Item
|
||||||
// FIXED-LINE-HEIGHT:
|
// FIXED-LINE-HEIGHT:
|
||||||
height: 18 * screenScaleFactor // TODO: Theme!
|
height: 18 * screenScaleFactor // TODO: Theme!
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
renderType: Text.NativeRendering
|
||||||
}
|
}
|
||||||
|
|
||||||
Label
|
Label
|
||||||
|
@ -123,6 +125,7 @@ Item
|
||||||
// FIXED-LINE-HEIGHT:
|
// FIXED-LINE-HEIGHT:
|
||||||
height: 18 * screenScaleFactor // TODO: Theme!
|
height: 18 * screenScaleFactor // TODO: Theme!
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
renderType: Text.NativeRendering
|
||||||
}
|
}
|
||||||
|
|
||||||
Label
|
Label
|
||||||
|
@ -137,6 +140,7 @@ Item
|
||||||
// FIXED-LINE-HEIGHT:
|
// FIXED-LINE-HEIGHT:
|
||||||
height: 18 * screenScaleFactor // TODO: Theme!
|
height: 18 * screenScaleFactor // TODO: Theme!
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
renderType: Text.NativeRendering
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -213,6 +217,7 @@ Item
|
||||||
text: i18n.i18nc("@info", "All jobs are printed.")
|
text: i18n.i18nc("@info", "All jobs are printed.")
|
||||||
color: UM.Theme.getColor("monitor_text_primary")
|
color: UM.Theme.getColor("monitor_text_primary")
|
||||||
font: UM.Theme.getFont("medium") // 14pt, regular
|
font: UM.Theme.getFont("medium") // 14pt, regular
|
||||||
|
renderType: Text.NativeRendering
|
||||||
}
|
}
|
||||||
|
|
||||||
Item
|
Item
|
||||||
|
|
|
@ -16,6 +16,7 @@ Button {
|
||||||
text: parent.text
|
text: parent.text
|
||||||
horizontalAlignment: Text.AlignLeft;
|
horizontalAlignment: Text.AlignLeft;
|
||||||
verticalAlignment: Text.AlignVCenter;
|
verticalAlignment: Text.AlignVCenter;
|
||||||
|
renderType: Text.NativeRendering;
|
||||||
}
|
}
|
||||||
height: visible ? 39 * screenScaleFactor : 0; // TODO: Theme!
|
height: visible ? 39 * screenScaleFactor : 0; // TODO: Theme!
|
||||||
hoverEnabled: true;
|
hoverEnabled: true;
|
||||||
|
|
|
@ -78,6 +78,7 @@ UM.Dialog {
|
||||||
height: 20 * screenScaleFactor;
|
height: 20 * screenScaleFactor;
|
||||||
text: catalog.i18nc("@label", "Printer selection");
|
text: catalog.i18nc("@label", "Printer selection");
|
||||||
wrapMode: Text.Wrap;
|
wrapMode: Text.Wrap;
|
||||||
|
renderType: Text.NativeRendering;
|
||||||
}
|
}
|
||||||
|
|
||||||
ComboBox {
|
ComboBox {
|
||||||
|
|
|
@ -535,6 +535,7 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
|
||||||
extruder.setMaterial(self._createMaterialOutputModel(extruder_data.get("material", {})))
|
extruder.setMaterial(self._createMaterialOutputModel(extruder_data.get("material", {})))
|
||||||
|
|
||||||
configuration.setExtruderConfigurations(extruders)
|
configuration.setExtruderConfigurations(extruders)
|
||||||
|
configuration.setPrinterType(data.get("machine_variant", ""))
|
||||||
print_job.updateConfiguration(configuration)
|
print_job.updateConfiguration(configuration)
|
||||||
print_job.setCompatibleMachineFamilies(data.get("compatible_machine_families", []))
|
print_job.setCompatibleMachineFamilies(data.get("compatible_machine_families", []))
|
||||||
print_job.stateChanged.connect(self._printJobStateChanged)
|
print_job.stateChanged.connect(self._printJobStateChanged)
|
||||||
|
|
|
@ -34,7 +34,10 @@ class DiscoverUM3Action(MachineAction):
|
||||||
|
|
||||||
self.__additional_components_view = None #type: Optional[QObject]
|
self.__additional_components_view = None #type: Optional[QObject]
|
||||||
|
|
||||||
CuraApplication.getInstance().engineCreatedSignal.connect(self._createAdditionalComponentsView)
|
self._application = CuraApplication.getInstance()
|
||||||
|
self._api = self._application.getCuraAPI()
|
||||||
|
|
||||||
|
self._application.engineCreatedSignal.connect(self._createAdditionalComponentsView)
|
||||||
|
|
||||||
self._last_zero_conf_event_time = time.time() #type: float
|
self._last_zero_conf_event_time = time.time() #type: float
|
||||||
|
|
||||||
|
@ -50,7 +53,7 @@ class DiscoverUM3Action(MachineAction):
|
||||||
def startDiscovery(self):
|
def startDiscovery(self):
|
||||||
if not self._network_plugin:
|
if not self._network_plugin:
|
||||||
Logger.log("d", "Starting device discovery.")
|
Logger.log("d", "Starting device discovery.")
|
||||||
self._network_plugin = CuraApplication.getInstance().getOutputDeviceManager().getOutputDevicePlugin("UM3NetworkPrinting")
|
self._network_plugin = self._application.getOutputDeviceManager().getOutputDevicePlugin("UM3NetworkPrinting")
|
||||||
self._network_plugin.discoveredDevicesChanged.connect(self._onDeviceDiscoveryChanged)
|
self._network_plugin.discoveredDevicesChanged.connect(self._onDeviceDiscoveryChanged)
|
||||||
self.discoveredDevicesChanged.emit()
|
self.discoveredDevicesChanged.emit()
|
||||||
|
|
||||||
|
@ -105,72 +108,27 @@ class DiscoverUM3Action(MachineAction):
|
||||||
else:
|
else:
|
||||||
return []
|
return []
|
||||||
|
|
||||||
@pyqtSlot(str)
|
@pyqtSlot()
|
||||||
def setGroupName(self, group_name: str) -> None:
|
def refreshConnections(self) -> None:
|
||||||
Logger.log("d", "Attempting to set the group name of the active machine to %s", group_name)
|
|
||||||
global_container_stack = CuraApplication.getInstance().getGlobalContainerStack()
|
|
||||||
if global_container_stack:
|
|
||||||
meta_data = global_container_stack.getMetaData()
|
|
||||||
if "group_name" in meta_data:
|
|
||||||
previous_connect_group_name = meta_data["group_name"]
|
|
||||||
global_container_stack.setMetaDataEntry("group_name", group_name)
|
|
||||||
# Find all the places where there is the same group name and change it accordingly
|
|
||||||
self._replaceContainersMetadata(key = "group_name", value = previous_connect_group_name, new_value = group_name)
|
|
||||||
else:
|
|
||||||
global_container_stack.setMetaDataEntry("group_name", group_name)
|
|
||||||
# Set the default value for "hidden", which is used when you have a group with multiple types of printers
|
|
||||||
global_container_stack.setMetaDataEntry("hidden", False)
|
|
||||||
|
|
||||||
if self._network_plugin:
|
if self._network_plugin:
|
||||||
# Ensure that the connection states are refreshed.
|
|
||||||
self._network_plugin.refreshConnections()
|
self._network_plugin.refreshConnections()
|
||||||
|
|
||||||
## Find all container stacks that has the pair 'key = value' in its metadata and replaces the value with 'new_value'
|
# TODO: Improve naming
|
||||||
def _replaceContainersMetadata(self, key: str, value: str, new_value: str) -> None:
|
# TODO: CHANGE TO HOSTNAME
|
||||||
machines = CuraContainerRegistry.getInstance().findContainerStacks(type="machine")
|
|
||||||
for machine in machines:
|
|
||||||
if machine.getMetaDataEntry(key) == value:
|
|
||||||
machine.setMetaDataEntry(key, new_value)
|
|
||||||
|
|
||||||
# Associates the currently active machine with the given printer device. The network connection information will be
|
|
||||||
# stored into the metadata of the currently active machine.
|
|
||||||
@pyqtSlot(QObject)
|
|
||||||
def associateActiveMachineWithPrinterDevice(self, printer_device: Optional["PrinterOutputDevice"]) -> None:
|
|
||||||
if self._network_plugin:
|
|
||||||
self._network_plugin.associateActiveMachineWithPrinterDevice(printer_device)
|
|
||||||
|
|
||||||
@pyqtSlot(result = str)
|
|
||||||
def getStoredKey(self) -> str:
|
|
||||||
global_container_stack = CuraApplication.getInstance().getGlobalContainerStack()
|
|
||||||
if global_container_stack:
|
|
||||||
meta_data = global_container_stack.getMetaData()
|
|
||||||
if "um_network_key" in meta_data:
|
|
||||||
return global_container_stack.getMetaDataEntry("um_network_key")
|
|
||||||
|
|
||||||
return ""
|
|
||||||
|
|
||||||
@pyqtSlot(result = str)
|
@pyqtSlot(result = str)
|
||||||
def getLastManualEntryKey(self) -> str:
|
def getLastManualEntryKey(self) -> str:
|
||||||
if self._network_plugin:
|
if self._network_plugin:
|
||||||
return self._network_plugin.getLastManualDevice()
|
return self._network_plugin.getLastManualDevice()
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
|
# TODO: Better naming needed. Exists where? On the current machine? On all machines?
|
||||||
|
# TODO: CHANGE TO HOSTNAME
|
||||||
@pyqtSlot(str, result = bool)
|
@pyqtSlot(str, result = bool)
|
||||||
def existsKey(self, key: str) -> bool:
|
def existsKey(self, key: str) -> bool:
|
||||||
metadata_filter = {"um_network_key": key}
|
metadata_filter = {"um_network_key": key}
|
||||||
containers = CuraContainerRegistry.getInstance().findContainerStacks(type="machine", **metadata_filter)
|
containers = CuraContainerRegistry.getInstance().findContainerStacks(type="machine", **metadata_filter)
|
||||||
return bool(containers)
|
return bool(containers)
|
||||||
|
|
||||||
@pyqtSlot()
|
|
||||||
def loadConfigurationFromPrinter(self) -> None:
|
|
||||||
machine_manager = CuraApplication.getInstance().getMachineManager()
|
|
||||||
hotend_ids = machine_manager.printerOutputDevices[0].hotendIds
|
|
||||||
for index in range(len(hotend_ids)):
|
|
||||||
machine_manager.printerOutputDevices[0].hotendIdChanged.emit(index, hotend_ids[index])
|
|
||||||
material_ids = machine_manager.printerOutputDevices[0].materialIds
|
|
||||||
for index in range(len(material_ids)):
|
|
||||||
machine_manager.printerOutputDevices[0].materialIdChanged.emit(index, material_ids[index])
|
|
||||||
|
|
||||||
def _createAdditionalComponentsView(self) -> None:
|
def _createAdditionalComponentsView(self) -> None:
|
||||||
Logger.log("d", "Creating additional ui components for UM3.")
|
Logger.log("d", "Creating additional ui components for UM3.")
|
||||||
|
|
||||||
|
@ -179,10 +137,10 @@ class DiscoverUM3Action(MachineAction):
|
||||||
if not plugin_path:
|
if not plugin_path:
|
||||||
return
|
return
|
||||||
path = os.path.join(plugin_path, "resources/qml/UM3InfoComponents.qml")
|
path = os.path.join(plugin_path, "resources/qml/UM3InfoComponents.qml")
|
||||||
self.__additional_components_view = CuraApplication.getInstance().createQmlComponent(path, {"manager": self})
|
self.__additional_components_view = self._application.createQmlComponent(path, {"manager": self})
|
||||||
if not self.__additional_components_view:
|
if not self.__additional_components_view:
|
||||||
Logger.log("w", "Could not create ui components for UM3.")
|
Logger.log("w", "Could not create ui components for UM3.")
|
||||||
return
|
return
|
||||||
|
|
||||||
# Create extra components
|
# Create extra components
|
||||||
CuraApplication.getInstance().addAdditionalComponent("monitorButtons", self.__additional_components_view.findChild(QObject, "networkPrinterConnectButton"))
|
self._application.addAdditionalComponent("monitorButtons", self.__additional_components_view.findChild(QObject, "networkPrinterConnectButton"))
|
||||||
|
|
|
@ -5,11 +5,11 @@ import os
|
||||||
from queue import Queue
|
from queue import Queue
|
||||||
from threading import Event, Thread
|
from threading import Event, Thread
|
||||||
from time import time
|
from time import time
|
||||||
from typing import Optional, TYPE_CHECKING, Dict
|
from typing import Optional, TYPE_CHECKING, Dict, Callable
|
||||||
|
|
||||||
from zeroconf import Zeroconf, ServiceBrowser, ServiceStateChange, ServiceInfo
|
from zeroconf import Zeroconf, ServiceBrowser, ServiceStateChange, ServiceInfo
|
||||||
|
|
||||||
from PyQt5.QtNetwork import QNetworkRequest, QNetworkAccessManager
|
from PyQt5.QtNetwork import QNetworkRequest, QNetworkReply, QNetworkAccessManager
|
||||||
from PyQt5.QtCore import QUrl
|
from PyQt5.QtCore import QUrl
|
||||||
from PyQt5.QtGui import QDesktopServices
|
from PyQt5.QtGui import QDesktopServices
|
||||||
|
|
||||||
|
@ -39,6 +39,22 @@ if TYPE_CHECKING:
|
||||||
i18n_catalog = i18nCatalog("cura")
|
i18n_catalog = i18nCatalog("cura")
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Represents a request for adding a manual printer. It has the following fields:
|
||||||
|
# - address: The string of the (IP) address of the manual printer
|
||||||
|
# - callback: (Optional) Once the HTTP request to the printer to get printer information is done, whether successful
|
||||||
|
# or not, this callback will be invoked to notify about the result. The callback must have a signature of
|
||||||
|
# func(success: bool, address: str) -> None
|
||||||
|
# - network_reply: This is the QNetworkReply instance for this request if the request has been issued and still in
|
||||||
|
# progress. It is kept here so we can cancel a request when needed.
|
||||||
|
#
|
||||||
|
class ManualPrinterRequest:
|
||||||
|
def __init__(self, address: str, callback: Optional[Callable[[bool, str], None]] = None) -> None:
|
||||||
|
self.address = address
|
||||||
|
self.callback = callback
|
||||||
|
self.network_reply = None # type: Optional["QNetworkReply"]
|
||||||
|
|
||||||
|
|
||||||
## This plugin handles the connection detection & creation of output device objects for the UM3 printer.
|
## This plugin handles the connection detection & creation of output device objects for the UM3 printer.
|
||||||
# Zero-Conf is used to detect printers, which are saved in a dict.
|
# Zero-Conf is used to detect printers, which are saved in a dict.
|
||||||
# If we discover a printer that has the same key as the active machine instance a connection is made.
|
# If we discover a printer that has the same key as the active machine instance a connection is made.
|
||||||
|
@ -51,11 +67,11 @@ class UM3OutputDevicePlugin(OutputDevicePlugin):
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
||||||
self._zero_conf = None
|
self._zero_conf = None
|
||||||
self._zero_conf_browser = None
|
self._zero_conf_browser = None
|
||||||
|
|
||||||
self._application = CuraApplication.getInstance()
|
self._application = CuraApplication.getInstance()
|
||||||
|
self._api = self._application.getCuraAPI()
|
||||||
|
|
||||||
# Create a cloud output device manager that abstracts all cloud connection logic away.
|
# Create a cloud output device manager that abstracts all cloud connection logic away.
|
||||||
self._cloud_output_device_manager = CloudOutputDeviceManager()
|
self._cloud_output_device_manager = CloudOutputDeviceManager()
|
||||||
|
@ -80,14 +96,16 @@ class UM3OutputDevicePlugin(OutputDevicePlugin):
|
||||||
self._cluster_api_prefix = "/cluster-api/v" + self._cluster_api_version + "/"
|
self._cluster_api_prefix = "/cluster-api/v" + self._cluster_api_version + "/"
|
||||||
|
|
||||||
# Get list of manual instances from preferences
|
# Get list of manual instances from preferences
|
||||||
self._preferences = CuraApplication.getInstance().getPreferences()
|
self._preferences = self._application.getPreferences()
|
||||||
self._preferences.addPreference("um3networkprinting/manual_instances",
|
self._preferences.addPreference("um3networkprinting/manual_instances",
|
||||||
"") # A comma-separated list of ip adresses or hostnames
|
"") # A comma-separated list of ip adresses or hostnames
|
||||||
|
|
||||||
self._manual_instances = self._preferences.getValue("um3networkprinting/manual_instances").split(",")
|
manual_instances = self._preferences.getValue("um3networkprinting/manual_instances").split(",")
|
||||||
|
self._manual_instances = {address: ManualPrinterRequest(address)
|
||||||
|
for address in manual_instances} # type: Dict[str, ManualPrinterRequest]
|
||||||
|
|
||||||
# Store the last manual entry key
|
# Store the last manual entry key
|
||||||
self._last_manual_entry_key = "" # type: str
|
self._last_manual_entry_key = "" # type: str
|
||||||
|
|
||||||
# The zero-conf service changed requests are handled in a separate thread, so we can re-schedule the requests
|
# The zero-conf service changed requests are handled in a separate thread, so we can re-schedule the requests
|
||||||
# which fail to get detailed service info.
|
# which fail to get detailed service info.
|
||||||
|
@ -98,7 +116,7 @@ class UM3OutputDevicePlugin(OutputDevicePlugin):
|
||||||
self._service_changed_request_thread = Thread(target=self._handleOnServiceChangedRequests, daemon=True)
|
self._service_changed_request_thread = Thread(target=self._handleOnServiceChangedRequests, daemon=True)
|
||||||
self._service_changed_request_thread.start()
|
self._service_changed_request_thread.start()
|
||||||
|
|
||||||
self._account = self._application.getCuraAPI().account
|
self._account = self._api.account
|
||||||
|
|
||||||
# Check if cloud flow is possible when user logs in
|
# Check if cloud flow is possible when user logs in
|
||||||
self._account.loginStateChanged.connect(self.checkCloudFlowIsPossible)
|
self._account.loginStateChanged.connect(self.checkCloudFlowIsPossible)
|
||||||
|
@ -149,10 +167,11 @@ class UM3OutputDevicePlugin(OutputDevicePlugin):
|
||||||
for address in self._manual_instances:
|
for address in self._manual_instances:
|
||||||
if address:
|
if address:
|
||||||
self.addManualDevice(address)
|
self.addManualDevice(address)
|
||||||
self.resetLastManualDevice()
|
self.resetLastManu
|
||||||
|
|
||||||
|
# TODO: CHANGE TO HOSTNAME
|
||||||
def refreshConnections(self):
|
def refreshConnections(self):
|
||||||
active_machine = CuraApplication.getInstance().getGlobalContainerStack()
|
active_machine = self._application.getGlobalContainerStack()
|
||||||
if not active_machine:
|
if not active_machine:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -179,14 +198,12 @@ class UM3OutputDevicePlugin(OutputDevicePlugin):
|
||||||
return
|
return
|
||||||
if self._discovered_devices[key].isConnected():
|
if self._discovered_devices[key].isConnected():
|
||||||
# Sometimes the status changes after changing the global container and maybe the device doesn't belong to this machine
|
# Sometimes the status changes after changing the global container and maybe the device doesn't belong to this machine
|
||||||
um_network_key = CuraApplication.getInstance().getGlobalContainerStack().getMetaDataEntry("um_network_key")
|
um_network_key = self._application.getGlobalContainerStack().getMetaDataEntry("um_network_key")
|
||||||
if key == um_network_key:
|
if key == um_network_key:
|
||||||
self.getOutputDeviceManager().addOutputDevice(self._discovered_devices[key])
|
self.getOutputDeviceManager().addOutputDevice(self._discovered_devices[key])
|
||||||
self.checkCloudFlowIsPossible(None)
|
self.checkCloudFlowIsPossible(None)
|
||||||
else:
|
else:
|
||||||
self.getOutputDeviceManager().removeOutputDevice(key)
|
self.getOutputDeviceManager().removeOutputDevice(key)
|
||||||
if key.startswith("manual:"):
|
|
||||||
self.removeManualDeviceSignal.emit(self.getPluginId(), key, self._discovered_devices[key].address)
|
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
if self._zero_conf is not None:
|
if self._zero_conf is not None:
|
||||||
|
@ -198,7 +215,10 @@ class UM3OutputDevicePlugin(OutputDevicePlugin):
|
||||||
# This plugin should always be the fallback option (at least try it):
|
# This plugin should always be the fallback option (at least try it):
|
||||||
return ManualDeviceAdditionAttempt.POSSIBLE
|
return ManualDeviceAdditionAttempt.POSSIBLE
|
||||||
|
|
||||||
def removeManualDevice(self, key, address = None):
|
def removeManualDevice(self, key: str, address: Optional[str] = None) -> None:
|
||||||
|
if key not in self._discovered_devices and address is not None:
|
||||||
|
key = "manual:%s" % address
|
||||||
|
|
||||||
if key in self._discovered_devices:
|
if key in self._discovered_devices:
|
||||||
if not address:
|
if not address:
|
||||||
address = self._discovered_devices[key].ipAddress
|
address = self._discovered_devices[key].ipAddress
|
||||||
|
@ -206,15 +226,22 @@ class UM3OutputDevicePlugin(OutputDevicePlugin):
|
||||||
self.resetLastManualDevice()
|
self.resetLastManualDevice()
|
||||||
|
|
||||||
if address in self._manual_instances:
|
if address in self._manual_instances:
|
||||||
self._manual_instances.remove(address)
|
manual_printer_request = self._manual_instances.pop(address)
|
||||||
self._preferences.setValue("um3networkprinting/manual_instances", ",".join(self._manual_instances))
|
self._preferences.setValue("um3networkprinting/manual_instances", ",".join(self._manual_instances.keys()))
|
||||||
|
|
||||||
self.removeManualDeviceSignal.emit(self.getPluginId(), key, address)
|
if manual_printer_request.network_reply is not None:
|
||||||
|
manual_printer_request.network_reply.abort()
|
||||||
|
|
||||||
def addManualDevice(self, address):
|
if manual_printer_request.callback is not None:
|
||||||
if address not in self._manual_instances:
|
self._application.callLater(manual_printer_request.callback, False, address)
|
||||||
self._manual_instances.append(address)
|
|
||||||
self._preferences.setValue("um3networkprinting/manual_instances", ",".join(self._manual_instances))
|
def addManualDevice(self, address: str, callback: Optional[Callable[[bool, str], None]] = None) -> None:
|
||||||
|
if address in self._manual_instances:
|
||||||
|
Logger.log("i", "Manual printer with address [%s] has already been added, do nothing", address)
|
||||||
|
return
|
||||||
|
|
||||||
|
self._manual_instances[address] = ManualPrinterRequest(address, callback = callback)
|
||||||
|
self._preferences.setValue("um3networkprinting/manual_instances", ",".join(self._manual_instances.keys()))
|
||||||
|
|
||||||
instance_name = "manual:%s" % address
|
instance_name = "manual:%s" % address
|
||||||
properties = {
|
properties = {
|
||||||
|
@ -230,7 +257,8 @@ class UM3OutputDevicePlugin(OutputDevicePlugin):
|
||||||
self._onAddDevice(instance_name, address, properties)
|
self._onAddDevice(instance_name, address, properties)
|
||||||
self._last_manual_entry_key = instance_name
|
self._last_manual_entry_key = instance_name
|
||||||
|
|
||||||
self._checkManualDevice(address)
|
reply = self._checkManualDevice(address)
|
||||||
|
self._manual_instances[address].network_reply = reply
|
||||||
|
|
||||||
def _createMachineFromDiscoveredPrinter(self, key: str) -> None:
|
def _createMachineFromDiscoveredPrinter(self, key: str) -> None:
|
||||||
discovered_device = self._discovered_devices.get(key)
|
discovered_device = self._discovered_devices.get(key)
|
||||||
|
@ -245,56 +273,22 @@ class UM3OutputDevicePlugin(OutputDevicePlugin):
|
||||||
key, group_name, machine_type_id)
|
key, group_name, machine_type_id)
|
||||||
|
|
||||||
self._application.getMachineManager().addMachine(machine_type_id, group_name)
|
self._application.getMachineManager().addMachine(machine_type_id, group_name)
|
||||||
|
|
||||||
# connect the new machine to that network printer
|
# connect the new machine to that network printer
|
||||||
self.associateActiveMachineWithPrinterDevice(discovered_device)
|
self._api.machines.addOutputDeviceToCurrentMachine(discovered_device)
|
||||||
|
|
||||||
# ensure that the connection states are refreshed.
|
# ensure that the connection states are refreshed.
|
||||||
self.refreshConnections()
|
self.refreshConnections()
|
||||||
|
|
||||||
def associateActiveMachineWithPrinterDevice(self, printer_device: Optional["PrinterOutputDevice"]) -> None:
|
def _checkManualDevice(self, address: str) -> Optional[QNetworkReply]:
|
||||||
if not printer_device:
|
|
||||||
return
|
|
||||||
|
|
||||||
Logger.log("d", "Attempting to set the network key of the active machine to %s", printer_device.key)
|
|
||||||
|
|
||||||
global_container_stack = CuraApplication.getInstance().getGlobalContainerStack()
|
|
||||||
if not global_container_stack:
|
|
||||||
return
|
|
||||||
|
|
||||||
meta_data = global_container_stack.getMetaData()
|
|
||||||
|
|
||||||
if "um_network_key" in meta_data: # Global stack already had a connection, but it's changed.
|
|
||||||
old_network_key = meta_data["um_network_key"]
|
|
||||||
# Since we might have a bunch of hidden stacks, we also need to change it there.
|
|
||||||
metadata_filter = {"um_network_key": old_network_key}
|
|
||||||
containers = self._application.getContainerRegistry().findContainerStacks(type = "machine", **metadata_filter)
|
|
||||||
|
|
||||||
for container in containers:
|
|
||||||
container.setMetaDataEntry("um_network_key", printer_device.key)
|
|
||||||
|
|
||||||
# Delete old authentication data.
|
|
||||||
Logger.log("d", "Removing old authentication id %s for device %s",
|
|
||||||
global_container_stack.getMetaDataEntry("network_authentication_id", None), printer_device.key)
|
|
||||||
|
|
||||||
container.removeMetaDataEntry("network_authentication_id")
|
|
||||||
container.removeMetaDataEntry("network_authentication_key")
|
|
||||||
|
|
||||||
# Ensure that these containers do know that they are configured for network connection
|
|
||||||
container.addConfiguredConnectionType(printer_device.connectionType.value)
|
|
||||||
|
|
||||||
else: # Global stack didn't have a connection yet, configure it.
|
|
||||||
global_container_stack.setMetaDataEntry("um_network_key", printer_device.key)
|
|
||||||
global_container_stack.addConfiguredConnectionType(printer_device.connectionType.value)
|
|
||||||
|
|
||||||
self.refreshConnections()
|
|
||||||
|
|
||||||
def _checkManualDevice(self, address: str) -> None:
|
|
||||||
# Check if a UM3 family device exists at this address.
|
# Check if a UM3 family device exists at this address.
|
||||||
# If a printer responds, it will replace the preliminary printer created above
|
# If a printer responds, it will replace the preliminary printer created above
|
||||||
# origin=manual is for tracking back the origin of the call
|
# origin=manual is for tracking back the origin of the call
|
||||||
url = QUrl("http://" + address + self._api_prefix + "system")
|
url = QUrl("http://" + address + self._api_prefix + "system")
|
||||||
name_request = QNetworkRequest(url)
|
name_request = QNetworkRequest(url)
|
||||||
self._network_manager.get(name_request)
|
return self._network_manager.get(name_request)
|
||||||
|
|
||||||
|
## This is the function which handles the above network request's reply when it comes back.
|
||||||
def _onNetworkRequestFinished(self, reply: "QNetworkReply") -> None:
|
def _onNetworkRequestFinished(self, reply: "QNetworkReply") -> None:
|
||||||
reply_url = reply.url().toString()
|
reply_url = reply.url().toString()
|
||||||
|
|
||||||
|
@ -319,6 +313,12 @@ class UM3OutputDevicePlugin(OutputDevicePlugin):
|
||||||
Logger.log("e", "Something went wrong converting the JSON.")
|
Logger.log("e", "Something went wrong converting the JSON.")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if address in self._manual_instances:
|
||||||
|
manual_printer_request = self._manual_instances[address]
|
||||||
|
manual_printer_request.network_reply = None
|
||||||
|
if manual_printer_request.callback is not None:
|
||||||
|
self._application.callLater(manual_printer_request.callback, True, address)
|
||||||
|
|
||||||
has_cluster_capable_firmware = Version(system_info["firmware"]) > self._min_cluster_version
|
has_cluster_capable_firmware = Version(system_info["firmware"]) > self._min_cluster_version
|
||||||
instance_name = "manual:%s" % address
|
instance_name = "manual:%s" % address
|
||||||
properties = {
|
properties = {
|
||||||
|
@ -362,10 +362,6 @@ class UM3OutputDevicePlugin(OutputDevicePlugin):
|
||||||
self._onRemoveDevice(instance_name)
|
self._onRemoveDevice(instance_name)
|
||||||
self._onAddDevice(instance_name, address, properties)
|
self._onAddDevice(instance_name, address, properties)
|
||||||
|
|
||||||
if device and address in self._manual_instances:
|
|
||||||
self.getOutputDeviceManager().addOutputDevice(device)
|
|
||||||
self.addManualDeviceSignal.emit(self.getPluginId(), device.getId(), address, properties)
|
|
||||||
|
|
||||||
def _onRemoveDevice(self, device_id: str) -> None:
|
def _onRemoveDevice(self, device_id: str) -> None:
|
||||||
device = self._discovered_devices.pop(device_id, None)
|
device = self._discovered_devices.pop(device_id, None)
|
||||||
if device:
|
if device:
|
||||||
|
@ -401,11 +397,13 @@ class UM3OutputDevicePlugin(OutputDevicePlugin):
|
||||||
device = ClusterUM3OutputDevice.ClusterUM3OutputDevice(name, address, properties)
|
device = ClusterUM3OutputDevice.ClusterUM3OutputDevice(name, address, properties)
|
||||||
else:
|
else:
|
||||||
device = LegacyUM3OutputDevice.LegacyUM3OutputDevice(name, address, properties)
|
device = LegacyUM3OutputDevice.LegacyUM3OutputDevice(name, address, properties)
|
||||||
self._application.getDiscoveredPrintersModel().addDiscoveredPrinter(address, device.getId(), name, self._createMachineFromDiscoveredPrinter, properties[b"printer_type"].decode("utf-8"), device)
|
self._application.getDiscoveredPrintersModel().addDiscoveredPrinter(
|
||||||
|
address, device.getId(), properties[b"name"].decode("utf-8"), self._createMachineFromDiscoveredPrinter,
|
||||||
|
properties[b"printer_type"].decode("utf-8"), device)
|
||||||
self._discovered_devices[device.getId()] = device
|
self._discovered_devices[device.getId()] = device
|
||||||
self.discoveredDevicesChanged.emit()
|
self.discoveredDevicesChanged.emit()
|
||||||
|
|
||||||
global_container_stack = CuraApplication.getInstance().getGlobalContainerStack()
|
global_container_stack = self._application.getGlobalContainerStack()
|
||||||
if global_container_stack and device.getId() == global_container_stack.getMetaDataEntry("um_network_key"):
|
if global_container_stack and device.getId() == global_container_stack.getMetaDataEntry("um_network_key"):
|
||||||
# Ensure that the configured connection type is set.
|
# Ensure that the configured connection type is set.
|
||||||
global_container_stack.addConfiguredConnectionType(device.connectionType.value)
|
global_container_stack.addConfiguredConnectionType(device.connectionType.value)
|
||||||
|
@ -425,7 +423,7 @@ class UM3OutputDevicePlugin(OutputDevicePlugin):
|
||||||
self._service_changed_request_event.wait(timeout = 5.0)
|
self._service_changed_request_event.wait(timeout = 5.0)
|
||||||
|
|
||||||
# Stop if the application is shutting down
|
# Stop if the application is shutting down
|
||||||
if CuraApplication.getInstance().isShuttingDown():
|
if self._application.isShuttingDown():
|
||||||
return
|
return
|
||||||
|
|
||||||
self._service_changed_request_event.clear()
|
self._service_changed_request_event.clear()
|
||||||
|
|
|
@ -4,6 +4,8 @@
|
||||||
import threading
|
import threading
|
||||||
import time
|
import time
|
||||||
import serial.tools.list_ports
|
import serial.tools.list_ports
|
||||||
|
from os import environ
|
||||||
|
from re import search
|
||||||
|
|
||||||
from PyQt5.QtCore import QObject, pyqtSignal
|
from PyQt5.QtCore import QObject, pyqtSignal
|
||||||
|
|
||||||
|
@ -112,6 +114,27 @@ class USBPrinterOutputDeviceManager(QObject, OutputDevicePlugin):
|
||||||
port = (port.device, port.description, port.hwid)
|
port = (port.device, port.description, port.hwid)
|
||||||
if only_list_usb and not port[2].startswith("USB"):
|
if only_list_usb and not port[2].startswith("USB"):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
# To prevent cura from messing with serial ports of other devices,
|
||||||
|
# filter by regular expressions passed in as environment variables.
|
||||||
|
# Get possible patterns with python3 -m serial.tools.list_ports -v
|
||||||
|
|
||||||
|
# set CURA_DEVICENAMES=USB[1-9] -> e.g. not matching /dev/ttyUSB0
|
||||||
|
pattern = environ.get('CURA_DEVICENAMES')
|
||||||
|
if pattern and not search(pattern, port[0]):
|
||||||
|
continue
|
||||||
|
|
||||||
|
# set CURA_DEVICETYPES=CP2102 -> match a type of serial converter
|
||||||
|
pattern = environ.get('CURA_DEVICETYPES')
|
||||||
|
if pattern and not search(pattern, port[1]):
|
||||||
|
continue
|
||||||
|
|
||||||
|
# set CURA_DEVICEINFOS=LOCATION=2-1.4 -> match a physical port
|
||||||
|
# set CURA_DEVICEINFOS=VID:PID=10C4:EA60 -> match a vendor:product
|
||||||
|
pattern = environ.get('CURA_DEVICEINFOS')
|
||||||
|
if pattern and not search(pattern, port[2]):
|
||||||
|
continue
|
||||||
|
|
||||||
base_list += [port[0]]
|
base_list += [port[0]]
|
||||||
|
|
||||||
return list(base_list)
|
return list(base_list)
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
import configparser
|
import configparser
|
||||||
import io
|
import io
|
||||||
|
import uuid
|
||||||
from typing import Dict, List, Tuple
|
from typing import Dict, List, Tuple
|
||||||
|
|
||||||
from UM.VersionUpgrade import VersionUpgrade
|
from UM.VersionUpgrade import VersionUpgrade
|
||||||
|
@ -18,6 +19,7 @@ _renamed_quality_profiles = {
|
||||||
"gmax15plus_pla_very_thick": "gmax15plus_global_very_thick"
|
"gmax15plus_pla_very_thick": "gmax15plus_global_very_thick"
|
||||||
} # type: Dict[str, str]
|
} # type: Dict[str, str]
|
||||||
|
|
||||||
|
|
||||||
## Upgrades configurations from the state they were in at version 4.0 to the
|
## Upgrades configurations from the state they were in at version 4.0 to the
|
||||||
# state they should be in at version 4.1.
|
# state they should be in at version 4.1.
|
||||||
class VersionUpgrade40to41(VersionUpgrade):
|
class VersionUpgrade40to41(VersionUpgrade):
|
||||||
|
@ -95,6 +97,13 @@ class VersionUpgrade40to41(VersionUpgrade):
|
||||||
if parser["containers"]["4"] in _renamed_quality_profiles:
|
if parser["containers"]["4"] in _renamed_quality_profiles:
|
||||||
parser["containers"]["4"] = _renamed_quality_profiles[parser["containers"]["4"]]
|
parser["containers"]["4"] = _renamed_quality_profiles[parser["containers"]["4"]]
|
||||||
|
|
||||||
|
# Assign a GlobalStack to a unique group_id. If the GlobalStack has a UM network connection, use the UM network
|
||||||
|
# key as the group_id.
|
||||||
|
if "um_network_key" in parser["metadata"]:
|
||||||
|
parser["metadata"]["group_id"] = parser["metadata"]["um_network_key"]
|
||||||
|
elif "group_id" not in parser["metadata"]:
|
||||||
|
parser["metadata"]["group_id"] = str(uuid.uuid4())
|
||||||
|
|
||||||
result = io.StringIO()
|
result = io.StringIO()
|
||||||
parser.write(result)
|
parser.write(result)
|
||||||
return [filename], [result.getvalue()]
|
return [filename], [result.getvalue()]
|
||||||
|
|
|
@ -10,21 +10,21 @@ from UM.Math.Color import Color
|
||||||
from UM.PluginRegistry import PluginRegistry
|
from UM.PluginRegistry import PluginRegistry
|
||||||
from UM.Platform import Platform
|
from UM.Platform import Platform
|
||||||
from UM.Event import Event
|
from UM.Event import Event
|
||||||
from UM.View.View import View
|
|
||||||
from UM.Scene.Iterator.BreadthFirstIterator import BreadthFirstIterator
|
from UM.Scene.Iterator.BreadthFirstIterator import BreadthFirstIterator
|
||||||
|
|
||||||
from UM.View.RenderBatch import RenderBatch
|
from UM.View.RenderBatch import RenderBatch
|
||||||
from UM.View.GL.OpenGL import OpenGL
|
from UM.View.GL.OpenGL import OpenGL
|
||||||
|
|
||||||
from cura.CuraApplication import CuraApplication
|
from cura.CuraApplication import CuraApplication
|
||||||
|
from cura.CuraView import CuraView
|
||||||
from cura.Scene.ConvexHullNode import ConvexHullNode
|
from cura.Scene.ConvexHullNode import ConvexHullNode
|
||||||
|
|
||||||
from . import XRayPass
|
from . import XRayPass
|
||||||
|
|
||||||
## View used to display a see-through version of objects with errors highlighted.
|
## View used to display a see-through version of objects with errors highlighted.
|
||||||
class XRayView(View):
|
class XRayView(CuraView):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__()
|
super().__init__(parent = None, use_empty_menu_placeholder = True)
|
||||||
|
|
||||||
self._xray_shader = None
|
self._xray_shader = None
|
||||||
self._xray_pass = None
|
self._xray_pass = None
|
||||||
|
|
|
@ -144,7 +144,7 @@ class XmlMaterialProfile(InstanceContainer):
|
||||||
# setting_version is derived from the "version" tag in the schema, so don't serialize it into a file
|
# setting_version is derived from the "version" tag in the schema, so don't serialize it into a file
|
||||||
if ignored_metadata_keys is None:
|
if ignored_metadata_keys is None:
|
||||||
ignored_metadata_keys = set()
|
ignored_metadata_keys = set()
|
||||||
ignored_metadata_keys |= {"setting_version", "definition", "status", "variant", "type", "base_file", "approximate_diameter", "id", "container_type", "name"}
|
ignored_metadata_keys |= {"setting_version", "definition", "status", "variant", "type", "base_file", "approximate_diameter", "id", "container_type", "name", "compatible"}
|
||||||
# remove the keys that we want to ignore in the metadata
|
# remove the keys that we want to ignore in the metadata
|
||||||
for key in ignored_metadata_keys:
|
for key in ignored_metadata_keys:
|
||||||
if key in metadata:
|
if key in metadata:
|
||||||
|
@ -1179,6 +1179,7 @@ class XmlMaterialProfile(InstanceContainer):
|
||||||
"adhesion tendency": "material_adhesion_tendency",
|
"adhesion tendency": "material_adhesion_tendency",
|
||||||
"surface energy": "material_surface_energy",
|
"surface energy": "material_surface_energy",
|
||||||
"shrinkage percentage": "material_shrinkage_percentage",
|
"shrinkage percentage": "material_shrinkage_percentage",
|
||||||
|
"build volume temperature": "build_volume_temperature",
|
||||||
}
|
}
|
||||||
__unmapped_settings = [
|
__unmapped_settings = [
|
||||||
"hardware compatible",
|
"hardware compatible",
|
||||||
|
|
|
@ -33,6 +33,23 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"AMFReader": {
|
||||||
|
"package_info": {
|
||||||
|
"package_id": "AMFReader",
|
||||||
|
"package_type": "plugin",
|
||||||
|
"display_name": "AMF Reader",
|
||||||
|
"description": "Provides support for reading AMF files.",
|
||||||
|
"package_version": "1.0.0",
|
||||||
|
"sdk_version": "6.0.0",
|
||||||
|
"website": "https://ultimaker.com",
|
||||||
|
"author": {
|
||||||
|
"author_id": "fieldOfView",
|
||||||
|
"display_name": "fieldOfView",
|
||||||
|
"email": "plugins@ultimaker.com",
|
||||||
|
"website": "https://ultimaker.com"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"CuraDrive": {
|
"CuraDrive": {
|
||||||
"package_info": {
|
"package_info": {
|
||||||
"package_id": "CuraDrive",
|
"package_id": "CuraDrive",
|
||||||
|
@ -1619,4 +1636,4 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,7 +38,7 @@
|
||||||
"speed_wall": { "value": "speed_print * 0.7" },
|
"speed_wall": { "value": "speed_print * 0.7" },
|
||||||
"speed_topbottom": { "value": "speed_print * 0.7" },
|
"speed_topbottom": { "value": "speed_print * 0.7" },
|
||||||
"speed_layer_0": { "value": "speed_print * 0.7" },
|
"speed_layer_0": { "value": "speed_print * 0.7" },
|
||||||
"gantry_height": { "default_value": 0 },
|
"gantry_height": { "value": "0" },
|
||||||
"retraction_speed": { "default_value" : 10 },
|
"retraction_speed": { "default_value" : 10 },
|
||||||
"retraction_amount": { "default_value" : 2.5 },
|
"retraction_amount": { "default_value" : 2.5 },
|
||||||
"machine_gcode_flavor": { "default_value": "RepRap (Marlin/Sprinter)" },
|
"machine_gcode_flavor": { "default_value": "RepRap (Marlin/Sprinter)" },
|
||||||
|
|
|
@ -45,7 +45,7 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"gantry_height": {
|
"gantry_height": {
|
||||||
"default_value": 30
|
"value": "30"
|
||||||
},
|
},
|
||||||
"machine_start_gcode": {
|
"machine_start_gcode": {
|
||||||
"default_value": ";Sliced at: {day} {date} {time}\nM104 S{material_print_temperature} ;set temperatures\nM140 S{material_bed_temperature}\nM109 S{material_print_temperature} ;wait for temperatures\nM190 S{material_bed_temperature}\nG21 ;metric values\nG90 ;absolute positioning\nM82 ;set extruder to absolute mode\nM107 ;start with the fan off\nG28 Z0 ;move Z to min endstops\nG28 X0 Y0 ;move X/Y to min endstops\nG29 ;Auto Level\nG1 Z0.6 F{speed_travel} ;move the Nozzle near the Bed\nG92 E0\nG1 Y0 ;zero the extruded length\nG1 X10 E30 F500 ;printing a Line from right to left\nG92 E0 ;zero the extruded length again\nG1 Z2\nG1 F{speed_travel}\nM117 Printing...;Put printing message on LCD screen\nM150 R255 U255 B255 P4 ;Change LED Color to white" },
|
"default_value": ";Sliced at: {day} {date} {time}\nM104 S{material_print_temperature} ;set temperatures\nM140 S{material_bed_temperature}\nM109 S{material_print_temperature} ;wait for temperatures\nM190 S{material_bed_temperature}\nG21 ;metric values\nG90 ;absolute positioning\nM82 ;set extruder to absolute mode\nM107 ;start with the fan off\nG28 Z0 ;move Z to min endstops\nG28 X0 Y0 ;move X/Y to min endstops\nG29 ;Auto Level\nG1 Z0.6 F{speed_travel} ;move the Nozzle near the Bed\nG92 E0\nG1 Y0 ;zero the extruded length\nG1 X10 E30 F500 ;printing a Line from right to left\nG92 E0 ;zero the extruded length again\nG1 Z2\nG1 F{speed_travel}\nM117 Printing...;Put printing message on LCD screen\nM150 R255 U255 B255 P4 ;Change LED Color to white" },
|
||||||
|
|
|
@ -8,12 +8,12 @@
|
||||||
"author": "TheUltimakerCommunity",
|
"author": "TheUltimakerCommunity",
|
||||||
"manufacturer": "Foehnsturm",
|
"manufacturer": "Foehnsturm",
|
||||||
"category": "Other",
|
"category": "Other",
|
||||||
|
"weight": 0,
|
||||||
"has_variants": true,
|
"has_variants": true,
|
||||||
"has_materials": true,
|
"has_materials": true,
|
||||||
"has_machine_materials": false,
|
"has_machine_materials": false,
|
||||||
"has_machine_quality": false,
|
"has_machine_quality": false,
|
||||||
"has_variant_materials": false,
|
"has_variant_materials": false,
|
||||||
"weight": 2,
|
|
||||||
"file_formats": "text/x-gcode",
|
"file_formats": "text/x-gcode",
|
||||||
"icon": "icon_ultimaker.png",
|
"icon": "icon_ultimaker.png",
|
||||||
"platform": "ultimaker2_platform.obj",
|
"platform": "ultimaker2_platform.obj",
|
||||||
|
@ -37,7 +37,7 @@
|
||||||
"default_value": 203
|
"default_value": 203
|
||||||
},
|
},
|
||||||
"gantry_height": {
|
"gantry_height": {
|
||||||
"default_value": 52
|
"value": "52"
|
||||||
},
|
},
|
||||||
"machine_center_is_zero": {
|
"machine_center_is_zero": {
|
||||||
"default_value": false
|
"default_value": false
|
||||||
|
|
|
@ -39,7 +39,7 @@
|
||||||
"default_value": false
|
"default_value": false
|
||||||
},
|
},
|
||||||
"gantry_height": {
|
"gantry_height": {
|
||||||
"default_value": 10
|
"value": "10"
|
||||||
},
|
},
|
||||||
"machine_gcode_flavor": {
|
"machine_gcode_flavor": {
|
||||||
"default_value": "RepRap (Marlin/Sprinter)"
|
"default_value": "RepRap (Marlin/Sprinter)"
|
||||||
|
|
|
@ -36,7 +36,7 @@
|
||||||
"retraction_enable": { "default_value": true },
|
"retraction_enable": { "default_value": true },
|
||||||
"retraction_amount": { "default_value": 5 },
|
"retraction_amount": { "default_value": 5 },
|
||||||
"retraction_speed": { "default_value": 45 },
|
"retraction_speed": { "default_value": 45 },
|
||||||
"gantry_height": { "default_value": 25 },
|
"gantry_height": { "value": "25" },
|
||||||
"machine_width": { "default_value": 220 },
|
"machine_width": { "default_value": 220 },
|
||||||
"machine_height": { "default_value": 250 },
|
"machine_height": { "default_value": 250 },
|
||||||
"machine_depth": { "default_value": 220 },
|
"machine_depth": { "default_value": 220 },
|
||||||
|
|
|
@ -30,7 +30,7 @@
|
||||||
"machine_height": { "default_value": 133 },
|
"machine_height": { "default_value": 133 },
|
||||||
"machine_depth": { "default_value": 100 },
|
"machine_depth": { "default_value": 100 },
|
||||||
"machine_center_is_zero": { "default_value": false },
|
"machine_center_is_zero": { "default_value": false },
|
||||||
"gantry_height": { "default_value": 55 },
|
"gantry_height": { "value": "55"},
|
||||||
"retraction_amount": { "default_value": 1.5 },
|
"retraction_amount": { "default_value": 1.5 },
|
||||||
"support_enable": { "default_value": true},
|
"support_enable": { "default_value": true},
|
||||||
"machine_head_with_fans_polygon": {
|
"machine_head_with_fans_polygon": {
|
||||||
|
|
|
@ -30,7 +30,7 @@
|
||||||
"machine_height": { "default_value": 170 },
|
"machine_height": { "default_value": 170 },
|
||||||
"machine_depth": { "default_value": 160 },
|
"machine_depth": { "default_value": 160 },
|
||||||
"machine_center_is_zero": { "default_value": false },
|
"machine_center_is_zero": { "default_value": false },
|
||||||
"gantry_height": { "default_value": 55 },
|
"gantry_height": { "value": "55"},
|
||||||
"retraction_amount": { "default_value": 1.5 },
|
"retraction_amount": { "default_value": 1.5 },
|
||||||
"support_enable": { "default_value": true},
|
"support_enable": { "default_value": true},
|
||||||
"machine_head_with_fans_polygon": {
|
"machine_head_with_fans_polygon": {
|
||||||
|
|
45
resources/definitions/anet_a6.def.json
Normal file
45
resources/definitions/anet_a6.def.json
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
{
|
||||||
|
"version": 2,
|
||||||
|
"name": "Anet A6",
|
||||||
|
"inherits": "fdmprinter",
|
||||||
|
"metadata": {
|
||||||
|
"visible": true,
|
||||||
|
"author": "Mark",
|
||||||
|
"manufacturer": "Anet",
|
||||||
|
"file_formats": "text/x-gcode",
|
||||||
|
"platform": "aneta6_platform.stl",
|
||||||
|
"platform_offset": [0, -3.4, 0],
|
||||||
|
"machine_extruder_trains":
|
||||||
|
{
|
||||||
|
"0": "anet_a6_extruder_0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"overrides": {
|
||||||
|
"machine_name": { "default_value": "Anet A6" },
|
||||||
|
"machine_heated_bed": {
|
||||||
|
"default_value": true
|
||||||
|
},
|
||||||
|
"machine_width": {
|
||||||
|
"default_value": 220
|
||||||
|
},
|
||||||
|
"machine_height": {
|
||||||
|
"default_value": 250
|
||||||
|
},
|
||||||
|
"machine_depth": {
|
||||||
|
"default_value": 220
|
||||||
|
},
|
||||||
|
"machine_center_is_zero": {
|
||||||
|
"default_value": false
|
||||||
|
},
|
||||||
|
"gantry_height": {
|
||||||
|
"value": "55"
|
||||||
|
},
|
||||||
|
"machine_start_gcode": {
|
||||||
|
"default_value": "G21 ;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\nM84 ;steppers off\nM0 S12 ;wait 12 seconds\nM17 ;turn steppers on\nG1 Z10.0 F300 ;move the platform down 10mm\nG92 E0 ;zero the extruded length\nG1 F200 E8 ;extrude 8mm of feed stock\nG92 E0 ;zero the extruded length again\nM0 S5 ;wait 5 seconds\nG1 F9000\nM117 Printing..."
|
||||||
|
},
|
||||||
|
"machine_end_gcode": {
|
||||||
|
"default_value": "M104 S0 ;extruder heater off\nM140 S0 ;heated bed heater off (if you have it)\nG91 ;relative positioning\nG1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\nG1 Z+4 E-5 X-20 Y-20 F9000 ;move Z up a bit and retract filament even more\nG28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\nG1 Y210 F9000 ;move out to get part off\nM84 ;steppers off\nG90 ;absolute positioning"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -50,7 +50,7 @@
|
||||||
"jerk_wall": { "value": "math.ceil(jerk_print * 10 / 25)" },
|
"jerk_wall": { "value": "math.ceil(jerk_print * 10 / 25)" },
|
||||||
"jerk_wall_0": { "value": "math.ceil(jerk_wall * 5 / 10)" },
|
"jerk_wall_0": { "value": "math.ceil(jerk_wall * 5 / 10)" },
|
||||||
|
|
||||||
"gantry_height": { "default_value": 25.0 },
|
"gantry_height": { "value": "25.0" },
|
||||||
"skin_overlap": { "value": "10" },
|
"skin_overlap": { "value": "10" },
|
||||||
|
|
||||||
"acceleration_enabled": { "value": "True" },
|
"acceleration_enabled": { "value": "True" },
|
||||||
|
|
|
@ -52,7 +52,7 @@
|
||||||
},
|
},
|
||||||
"gantry_height":
|
"gantry_height":
|
||||||
{
|
{
|
||||||
"default_value": 35
|
"value": "35"
|
||||||
},
|
},
|
||||||
"machine_head_with_fans_polygon":
|
"machine_head_with_fans_polygon":
|
||||||
{
|
{
|
||||||
|
|
|
@ -46,7 +46,7 @@
|
||||||
},
|
},
|
||||||
"gantry_height":
|
"gantry_height":
|
||||||
{
|
{
|
||||||
"default_value": 0
|
"value": "0"
|
||||||
},
|
},
|
||||||
"machine_gcode_flavor":
|
"machine_gcode_flavor":
|
||||||
{
|
{
|
||||||
|
|
|
@ -64,7 +64,7 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"gantry_height": {
|
"gantry_height": {
|
||||||
"default_value": 12
|
"value": "12"
|
||||||
},
|
},
|
||||||
"machine_use_extruder_offset_to_offset_coords": {
|
"machine_use_extruder_offset_to_offset_coords": {
|
||||||
"default_value": true
|
"default_value": true
|
||||||
|
|
|
@ -93,7 +93,7 @@
|
||||||
"machine_nozzle_heat_up_speed": { "default_value": 2 },
|
"machine_nozzle_heat_up_speed": { "default_value": 2 },
|
||||||
"machine_nozzle_cool_down_speed": { "default_value": 2 },
|
"machine_nozzle_cool_down_speed": { "default_value": 2 },
|
||||||
"machine_head_polygon": { "default_value": [[-75, -18],[-75, 35],[18, 35],[18, -18]] },
|
"machine_head_polygon": { "default_value": [[-75, -18],[-75, 35],[18, 35],[18, -18]] },
|
||||||
"gantry_height": { "default_value": 55 },
|
"gantry_height": { "value": "55" },
|
||||||
"machine_max_feedrate_x": { "default_value": 300 },
|
"machine_max_feedrate_x": { "default_value": 300 },
|
||||||
"machine_max_feedrate_y": { "default_value": 300 },
|
"machine_max_feedrate_y": { "default_value": 300 },
|
||||||
"machine_max_feedrate_z": { "default_value": 40 },
|
"machine_max_feedrate_z": { "default_value": 40 },
|
||||||
|
|
|
@ -93,7 +93,7 @@
|
||||||
"machine_nozzle_heat_up_speed": { "default_value": 2 },
|
"machine_nozzle_heat_up_speed": { "default_value": 2 },
|
||||||
"machine_nozzle_cool_down_speed": { "default_value": 2 },
|
"machine_nozzle_cool_down_speed": { "default_value": 2 },
|
||||||
"machine_head_polygon": { "default_value": [[-75, -18],[-75, 35],[18, 35],[18, -18]] },
|
"machine_head_polygon": { "default_value": [[-75, -18],[-75, 35],[18, 35],[18, -18]] },
|
||||||
"gantry_height": { "default_value": 55 },
|
"gantry_height": { "value": "55" },
|
||||||
"machine_max_feedrate_x": { "default_value": 300 },
|
"machine_max_feedrate_x": { "default_value": 300 },
|
||||||
"machine_max_feedrate_y": { "default_value": 300 },
|
"machine_max_feedrate_y": { "default_value": 300 },
|
||||||
"machine_max_feedrate_z": { "default_value": 40 },
|
"machine_max_feedrate_z": { "default_value": 40 },
|
||||||
|
|
|
@ -92,7 +92,7 @@
|
||||||
"machine_nozzle_heat_up_speed": { "default_value": 2 },
|
"machine_nozzle_heat_up_speed": { "default_value": 2 },
|
||||||
"machine_nozzle_cool_down_speed": { "default_value": 2 },
|
"machine_nozzle_cool_down_speed": { "default_value": 2 },
|
||||||
"machine_head_polygon": { "default_value": [[-75, -18],[-75, 35],[18, 35],[18, -18]] },
|
"machine_head_polygon": { "default_value": [[-75, -18],[-75, 35],[18, 35],[18, -18]] },
|
||||||
"gantry_height": { "default_value": 55 },
|
"gantry_height": { "value": "55" },
|
||||||
"machine_max_feedrate_x": { "default_value": 300 },
|
"machine_max_feedrate_x": { "default_value": 300 },
|
||||||
"machine_max_feedrate_y": { "default_value": 300 },
|
"machine_max_feedrate_y": { "default_value": 300 },
|
||||||
"machine_max_feedrate_z": { "default_value": 40 },
|
"machine_max_feedrate_z": { "default_value": 40 },
|
||||||
|
|
|
@ -35,7 +35,7 @@
|
||||||
"machine_extruder_count": { "default_value": 2 },
|
"machine_extruder_count": { "default_value": 2 },
|
||||||
"machine_heated_bed": { "default_value": true },
|
"machine_heated_bed": { "default_value": true },
|
||||||
"machine_center_is_zero": { "default_value": false },
|
"machine_center_is_zero": { "default_value": false },
|
||||||
"gantry_height": { "default_value": 35 },
|
"gantry_height": { "value": "35" },
|
||||||
"machine_height": { "default_value": 400 },
|
"machine_height": { "default_value": 400 },
|
||||||
"machine_depth": { "default_value": 270 },
|
"machine_depth": { "default_value": 270 },
|
||||||
"machine_width": { "default_value": 430 },
|
"machine_width": { "default_value": 430 },
|
||||||
|
|
|
@ -39,7 +39,7 @@
|
||||||
"default_value": false
|
"default_value": false
|
||||||
},
|
},
|
||||||
"gantry_height": {
|
"gantry_height": {
|
||||||
"default_value": 10
|
"value": "10"
|
||||||
},
|
},
|
||||||
"machine_gcode_flavor": {
|
"machine_gcode_flavor": {
|
||||||
"default_value": "RepRap (Marlin/Sprinter)"
|
"default_value": "RepRap (Marlin/Sprinter)"
|
||||||
|
|
|
@ -30,7 +30,7 @@
|
||||||
"retraction_amount": { "default_value": 3 },
|
"retraction_amount": { "default_value": 3 },
|
||||||
"retraction_speed": { "default_value": 70},
|
"retraction_speed": { "default_value": 70},
|
||||||
"adhesion_type": { "default_value": "skirt" },
|
"adhesion_type": { "default_value": "skirt" },
|
||||||
"gantry_height": { "default_value": 30 },
|
"gantry_height": { "value": "30" },
|
||||||
"speed_print": { "default_value": 60 },
|
"speed_print": { "default_value": 60 },
|
||||||
"speed_travel": { "default_value": 120 },
|
"speed_travel": { "default_value": 120 },
|
||||||
"machine_max_acceleration_x": { "default_value": 500 },
|
"machine_max_acceleration_x": { "default_value": 500 },
|
||||||
|
|
|
@ -71,7 +71,7 @@
|
||||||
"default_value": true
|
"default_value": true
|
||||||
},
|
},
|
||||||
"gantry_height": {
|
"gantry_height": {
|
||||||
"default_value": 30
|
"value": "30"
|
||||||
},
|
},
|
||||||
"acceleration_enabled": {
|
"acceleration_enabled": {
|
||||||
"default_value": true
|
"default_value": true
|
||||||
|
|
|
@ -31,7 +31,7 @@
|
||||||
"default_value": true
|
"default_value": true
|
||||||
},
|
},
|
||||||
"gantry_height": {
|
"gantry_height": {
|
||||||
"default_value": 30
|
"value": "30"
|
||||||
},
|
},
|
||||||
"machine_head_polygon": {
|
"machine_head_polygon": {
|
||||||
"default_value": [
|
"default_value": [
|
||||||
|
@ -59,6 +59,9 @@
|
||||||
"jerk_travel": {
|
"jerk_travel": {
|
||||||
"value": "jerk_print"
|
"value": "jerk_print"
|
||||||
},
|
},
|
||||||
|
"machine_max_jerk_z": {
|
||||||
|
"default_value": 0.3
|
||||||
|
},
|
||||||
"layer_height_0": {
|
"layer_height_0": {
|
||||||
"default_value": 0.2
|
"default_value": 0.2
|
||||||
},
|
},
|
||||||
|
@ -69,10 +72,10 @@
|
||||||
"default_value": 0.6
|
"default_value": 0.6
|
||||||
},
|
},
|
||||||
"retraction_amount": {
|
"retraction_amount": {
|
||||||
"default_value": 5
|
"default_value": 6
|
||||||
},
|
},
|
||||||
"retraction_speed": {
|
"retraction_speed": {
|
||||||
"default_value": 40
|
"default_value": 25
|
||||||
},
|
},
|
||||||
"cool_min_layer_time": {
|
"cool_min_layer_time": {
|
||||||
"default_value": 10
|
"default_value": 10
|
||||||
|
|
|
@ -24,8 +24,8 @@
|
||||||
"machine_depth": { "default_value": 250 },
|
"machine_depth": { "default_value": 250 },
|
||||||
"machine_heated_bed": { "default_value": true },
|
"machine_heated_bed": { "default_value": true },
|
||||||
"machine_shape": { "default_value": "elliptic" },
|
"machine_shape": { "default_value": "elliptic" },
|
||||||
"machine_max_feedrate_z": { "default_value": 300 },
|
"machine_max_feedrate_z": { "default_value": 300 },
|
||||||
"gantry_height": {"default_value": 43},
|
"gantry_height": {"value": "43"},
|
||||||
"layer_height": { "default_value": 0.1 },
|
"layer_height": { "default_value": 0.1 },
|
||||||
"relative_extrusion": { "default_value": false },
|
"relative_extrusion": { "default_value": false },
|
||||||
"retraction_combing": { "default_value": "off" },
|
"retraction_combing": { "default_value": "off" },
|
||||||
|
|
|
@ -38,7 +38,7 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"gantry_height": {
|
"gantry_height": {
|
||||||
"default_value": 10
|
"value": "10"
|
||||||
},
|
},
|
||||||
"machine_start_gcode": {
|
"machine_start_gcode": {
|
||||||
"default_value": ";Gcode by Cura\nG90\nM106 S255\nG28 X Y\nG1 X50\nM109 R90\nG28\nM104 S{material_print_temperature_layer_0}\nG29\nM107\nG1 X100 Y20 F3000\nG1 Z0.5\nM109 S{material_print_temperature_layer_0}\nM82\nG92 E0\nG1 F200 E10\nG92 E0\nG1 Z3\nG1 F6000\n"
|
"default_value": ";Gcode by Cura\nG90\nM106 S255\nG28 X Y\nG1 X50\nM109 R90\nG28\nM104 S{material_print_temperature_layer_0}\nG29\nM107\nG1 X100 Y20 F3000\nG1 Z0.5\nM109 S{material_print_temperature_layer_0}\nM82\nG92 E0\nG1 F200 E10\nG92 E0\nG1 Z3\nG1 F6000\n"
|
||||||
|
|
|
@ -38,7 +38,7 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"gantry_height": {
|
"gantry_height": {
|
||||||
"default_value": 0
|
"value": "0"
|
||||||
},
|
},
|
||||||
"machine_shape": {
|
"machine_shape": {
|
||||||
"default_value": "elliptic"
|
"default_value": "elliptic"
|
||||||
|
|
|
@ -38,7 +38,7 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"gantry_height": {
|
"gantry_height": {
|
||||||
"default_value": 0
|
"value": "0"
|
||||||
},
|
},
|
||||||
"machine_shape": {
|
"machine_shape": {
|
||||||
"default_value": "elliptic"
|
"default_value": "elliptic"
|
||||||
|
|
|
@ -28,7 +28,7 @@
|
||||||
"machine_end_gcode": {
|
"machine_end_gcode": {
|
||||||
"default_value": "M104 S0 ;extruder heater off\nM140 S0 ;heated bed heater off (if you have it)\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-3 X+5 Y+5 F5000 ;move Z up a bit and retract filament even more\n;end of the print\nM84 ;steppers off\nG90 ;absolute positioning\nM300 S2 ;FAB bep bep (end print)"
|
"default_value": "M104 S0 ;extruder heater off\nM140 S0 ;heated bed heater off (if you have it)\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-3 X+5 Y+5 F5000 ;move Z up a bit and retract filament even more\n;end of the print\nM84 ;steppers off\nG90 ;absolute positioning\nM300 S2 ;FAB bep bep (end print)"
|
||||||
},
|
},
|
||||||
"gantry_height": { "default_value": 55 },
|
"gantry_height": { "value": "55" },
|
||||||
"machine_width": { "default_value": 214 },
|
"machine_width": { "default_value": 214 },
|
||||||
"machine_height": { "default_value": 241.5 },
|
"machine_height": { "default_value": 241.5 },
|
||||||
"machine_depth": { "default_value": 234 },
|
"machine_depth": { "default_value": 234 },
|
||||||
|
|
|
@ -17,7 +17,8 @@
|
||||||
{
|
{
|
||||||
"0": "fdmextruder"
|
"0": "fdmextruder"
|
||||||
},
|
},
|
||||||
"supports_usb_connection": true
|
"supports_usb_connection": true,
|
||||||
|
"supports_network_connection": false
|
||||||
},
|
},
|
||||||
"settings":
|
"settings":
|
||||||
{
|
{
|
||||||
|
@ -227,7 +228,7 @@
|
||||||
},
|
},
|
||||||
"extruders_enabled_count":
|
"extruders_enabled_count":
|
||||||
{
|
{
|
||||||
"label": "Number of Extruders that are enabled",
|
"label": "Number of Extruders That Are Enabled",
|
||||||
"description": "Number of extruder trains that are enabled; automatically set in software",
|
"description": "Number of extruder trains that are enabled; automatically set in software",
|
||||||
"value": "machine_extruder_count",
|
"value": "machine_extruder_count",
|
||||||
"default_value": 1,
|
"default_value": 1,
|
||||||
|
@ -240,7 +241,7 @@
|
||||||
},
|
},
|
||||||
"machine_nozzle_tip_outer_diameter":
|
"machine_nozzle_tip_outer_diameter":
|
||||||
{
|
{
|
||||||
"label": "Outer nozzle diameter",
|
"label": "Outer Nozzle Diameter",
|
||||||
"description": "The outer diameter of the tip of the nozzle.",
|
"description": "The outer diameter of the tip of the nozzle.",
|
||||||
"unit": "mm",
|
"unit": "mm",
|
||||||
"default_value": 1,
|
"default_value": 1,
|
||||||
|
@ -252,7 +253,7 @@
|
||||||
},
|
},
|
||||||
"machine_nozzle_head_distance":
|
"machine_nozzle_head_distance":
|
||||||
{
|
{
|
||||||
"label": "Nozzle length",
|
"label": "Nozzle Length",
|
||||||
"description": "The height difference between the tip of the nozzle and the lowest part of the print head.",
|
"description": "The height difference between the tip of the nozzle and the lowest part of the print head.",
|
||||||
"unit": "mm",
|
"unit": "mm",
|
||||||
"default_value": 3,
|
"default_value": 3,
|
||||||
|
@ -263,7 +264,7 @@
|
||||||
},
|
},
|
||||||
"machine_nozzle_expansion_angle":
|
"machine_nozzle_expansion_angle":
|
||||||
{
|
{
|
||||||
"label": "Nozzle angle",
|
"label": "Nozzle Angle",
|
||||||
"description": "The angle between the horizontal plane and the conical part right above the tip of the nozzle.",
|
"description": "The angle between the horizontal plane and the conical part right above the tip of the nozzle.",
|
||||||
"unit": "°",
|
"unit": "°",
|
||||||
"type": "int",
|
"type": "int",
|
||||||
|
@ -276,7 +277,7 @@
|
||||||
},
|
},
|
||||||
"machine_heat_zone_length":
|
"machine_heat_zone_length":
|
||||||
{
|
{
|
||||||
"label": "Heat zone length",
|
"label": "Heat Zone Length",
|
||||||
"description": "The distance from the tip of the nozzle in which heat from the nozzle is transferred to the filament.",
|
"description": "The distance from the tip of the nozzle in which heat from the nozzle is transferred to the filament.",
|
||||||
"unit": "mm",
|
"unit": "mm",
|
||||||
"default_value": 16,
|
"default_value": 16,
|
||||||
|
@ -310,7 +311,7 @@
|
||||||
},
|
},
|
||||||
"machine_nozzle_heat_up_speed":
|
"machine_nozzle_heat_up_speed":
|
||||||
{
|
{
|
||||||
"label": "Heat up speed",
|
"label": "Heat Up Speed",
|
||||||
"description": "The speed (°C/s) by which the nozzle heats up averaged over the window of normal printing temperatures and the standby temperature.",
|
"description": "The speed (°C/s) by which the nozzle heats up averaged over the window of normal printing temperatures and the standby temperature.",
|
||||||
"default_value": 2.0,
|
"default_value": 2.0,
|
||||||
"unit": "°C/s",
|
"unit": "°C/s",
|
||||||
|
@ -321,7 +322,7 @@
|
||||||
},
|
},
|
||||||
"machine_nozzle_cool_down_speed":
|
"machine_nozzle_cool_down_speed":
|
||||||
{
|
{
|
||||||
"label": "Cool down speed",
|
"label": "Cool Down Speed",
|
||||||
"description": "The speed (°C/s) by which the nozzle cools down averaged over the window of normal printing temperatures and the standby temperature.",
|
"description": "The speed (°C/s) by which the nozzle cools down averaged over the window of normal printing temperatures and the standby temperature.",
|
||||||
"default_value": 2.0,
|
"default_value": 2.0,
|
||||||
"unit": "°C/s",
|
"unit": "°C/s",
|
||||||
|
@ -343,7 +344,7 @@
|
||||||
},
|
},
|
||||||
"machine_gcode_flavor":
|
"machine_gcode_flavor":
|
||||||
{
|
{
|
||||||
"label": "G-code flavour",
|
"label": "G-code Flavour",
|
||||||
"description": "The type of g-code to be generated.",
|
"description": "The type of g-code to be generated.",
|
||||||
"type": "enum",
|
"type": "enum",
|
||||||
"options":
|
"options":
|
||||||
|
@ -376,7 +377,7 @@
|
||||||
},
|
},
|
||||||
"machine_disallowed_areas":
|
"machine_disallowed_areas":
|
||||||
{
|
{
|
||||||
"label": "Disallowed areas",
|
"label": "Disallowed Areas",
|
||||||
"description": "A list of polygons with areas the print head is not allowed to enter.",
|
"description": "A list of polygons with areas the print head is not allowed to enter.",
|
||||||
"type": "polygons",
|
"type": "polygons",
|
||||||
"default_value":
|
"default_value":
|
||||||
|
@ -400,7 +401,7 @@
|
||||||
},
|
},
|
||||||
"machine_head_polygon":
|
"machine_head_polygon":
|
||||||
{
|
{
|
||||||
"label": "Machine head polygon",
|
"label": "Machine Head Polygon",
|
||||||
"description": "A 2D silhouette of the print head (fan caps excluded).",
|
"description": "A 2D silhouette of the print head (fan caps excluded).",
|
||||||
"type": "polygon",
|
"type": "polygon",
|
||||||
"default_value":
|
"default_value":
|
||||||
|
@ -428,7 +429,7 @@
|
||||||
},
|
},
|
||||||
"machine_head_with_fans_polygon":
|
"machine_head_with_fans_polygon":
|
||||||
{
|
{
|
||||||
"label": "Machine head & Fan polygon",
|
"label": "Machine Head & Fan Polygon",
|
||||||
"description": "A 2D silhouette of the print head (fan caps included).",
|
"description": "A 2D silhouette of the print head (fan caps included).",
|
||||||
"type": "polygon",
|
"type": "polygon",
|
||||||
"default_value":
|
"default_value":
|
||||||
|
@ -456,7 +457,7 @@
|
||||||
},
|
},
|
||||||
"gantry_height":
|
"gantry_height":
|
||||||
{
|
{
|
||||||
"label": "Gantry height",
|
"label": "Gantry Height",
|
||||||
"description": "The height difference between the tip of the nozzle and the gantry system (X and Y axes).",
|
"description": "The height difference between the tip of the nozzle and the gantry system (X and Y axes).",
|
||||||
"default_value": 99999999999,
|
"default_value": 99999999999,
|
||||||
"value": "machine_height",
|
"value": "machine_height",
|
||||||
|
@ -488,7 +489,7 @@
|
||||||
},
|
},
|
||||||
"machine_use_extruder_offset_to_offset_coords":
|
"machine_use_extruder_offset_to_offset_coords":
|
||||||
{
|
{
|
||||||
"label": "Offset With Extruder",
|
"label": "Offset with Extruder",
|
||||||
"description": "Apply the extruder offset to the coordinate system.",
|
"description": "Apply the extruder offset to the coordinate system.",
|
||||||
"type": "bool",
|
"type": "bool",
|
||||||
"default_value": true,
|
"default_value": true,
|
||||||
|
@ -523,7 +524,7 @@
|
||||||
"description": "The maximum speed for the motor of the X-direction.",
|
"description": "The maximum speed for the motor of the X-direction.",
|
||||||
"unit": "mm/s",
|
"unit": "mm/s",
|
||||||
"type": "float",
|
"type": "float",
|
||||||
"default_value": 500,
|
"default_value": 299792458000,
|
||||||
"settable_per_mesh": false,
|
"settable_per_mesh": false,
|
||||||
"settable_per_extruder": false,
|
"settable_per_extruder": false,
|
||||||
"settable_per_meshgroup": false
|
"settable_per_meshgroup": false
|
||||||
|
@ -534,7 +535,7 @@
|
||||||
"description": "The maximum speed for the motor of the Y-direction.",
|
"description": "The maximum speed for the motor of the Y-direction.",
|
||||||
"unit": "mm/s",
|
"unit": "mm/s",
|
||||||
"type": "float",
|
"type": "float",
|
||||||
"default_value": 500,
|
"default_value": 299792458000,
|
||||||
"settable_per_mesh": false,
|
"settable_per_mesh": false,
|
||||||
"settable_per_extruder": false,
|
"settable_per_extruder": false,
|
||||||
"settable_per_meshgroup": false
|
"settable_per_meshgroup": false
|
||||||
|
@ -545,7 +546,7 @@
|
||||||
"description": "The maximum speed for the motor of the Z-direction.",
|
"description": "The maximum speed for the motor of the Z-direction.",
|
||||||
"unit": "mm/s",
|
"unit": "mm/s",
|
||||||
"type": "float",
|
"type": "float",
|
||||||
"default_value": 5,
|
"default_value": 299792458000,
|
||||||
"settable_per_mesh": false,
|
"settable_per_mesh": false,
|
||||||
"settable_per_extruder": false,
|
"settable_per_extruder": false,
|
||||||
"settable_per_meshgroup": false
|
"settable_per_meshgroup": false
|
||||||
|
@ -1429,10 +1430,11 @@
|
||||||
"type": "enum",
|
"type": "enum",
|
||||||
"options":
|
"options":
|
||||||
{
|
{
|
||||||
"z_seam_corner_none": "None",
|
"z_seam_corner_none": "None",
|
||||||
"z_seam_corner_inner": "Hide Seam",
|
"z_seam_corner_inner": "Hide Seam",
|
||||||
"z_seam_corner_outer": "Expose Seam",
|
"z_seam_corner_outer": "Expose Seam",
|
||||||
"z_seam_corner_any": "Hide or Expose Seam"
|
"z_seam_corner_any": "Hide or Expose Seam",
|
||||||
|
"z_seam_corner_weighted": "Smart Hiding"
|
||||||
},
|
},
|
||||||
"default_value": "z_seam_corner_inner",
|
"default_value": "z_seam_corner_inner",
|
||||||
"enabled": "z_seam_type != 'random'",
|
"enabled": "z_seam_type != 'random'",
|
||||||
|
@ -2055,6 +2057,21 @@
|
||||||
"settable_per_mesh": false,
|
"settable_per_mesh": false,
|
||||||
"minimum_value": "-273.15"
|
"minimum_value": "-273.15"
|
||||||
},
|
},
|
||||||
|
"build_volume_temperature":
|
||||||
|
{
|
||||||
|
"label": "Build Volume Temperature",
|
||||||
|
"description": "The temperature used for build volume. If this is 0, the build volume temperature will not be adjusted.",
|
||||||
|
"unit": "°C",
|
||||||
|
"type": "float",
|
||||||
|
"default_value": 35,
|
||||||
|
"resolve": "min(extruderValues('build_volume_temperature'))",
|
||||||
|
"minimum_value": "-273.15",
|
||||||
|
"minimum_value_warning": "0",
|
||||||
|
"maximum_value_warning": "285",
|
||||||
|
"enabled": true,
|
||||||
|
"settable_per_mesh": false,
|
||||||
|
"settable_per_extruder": true
|
||||||
|
},
|
||||||
"material_print_temperature":
|
"material_print_temperature":
|
||||||
{
|
{
|
||||||
"label": "Printing Temperature",
|
"label": "Printing Temperature",
|
||||||
|
@ -3527,6 +3544,20 @@
|
||||||
"enabled": "retraction_hop_enabled and extruders_enabled_count > 1",
|
"enabled": "retraction_hop_enabled and extruders_enabled_count > 1",
|
||||||
"settable_per_mesh": false,
|
"settable_per_mesh": false,
|
||||||
"settable_per_extruder": true
|
"settable_per_extruder": true
|
||||||
|
},
|
||||||
|
"retraction_hop_after_extruder_switch_height":
|
||||||
|
{
|
||||||
|
"label": "Z Hop After Extruder Switch Height",
|
||||||
|
"description": "The height difference when performing a Z Hop after extruder switch.",
|
||||||
|
"unit": "mm",
|
||||||
|
"type": "float",
|
||||||
|
"default_value": 1,
|
||||||
|
"value": "retraction_hop",
|
||||||
|
"minimum_value_warning": "0",
|
||||||
|
"maximum_value_warning": "10",
|
||||||
|
"enabled": "retraction_enable and retraction_hop_after_extruder_switch",
|
||||||
|
"settable_per_mesh": false,
|
||||||
|
"settable_per_extruder": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -5219,7 +5250,7 @@
|
||||||
"type": "bool",
|
"type": "bool",
|
||||||
"enabled": "extruders_enabled_count > 1",
|
"enabled": "extruders_enabled_count > 1",
|
||||||
"default_value": false,
|
"default_value": false,
|
||||||
"resolve": "any(extruderValues('prime_tower_enable'))",
|
"resolve": "(extruders_enabled_count > 1) and any(extruderValues('prime_tower_enable')) or (adhesion_type in ('none', 'skirt'))",
|
||||||
"settable_per_mesh": false,
|
"settable_per_mesh": false,
|
||||||
"settable_per_extruder": false
|
"settable_per_extruder": false
|
||||||
},
|
},
|
||||||
|
@ -6659,7 +6690,7 @@
|
||||||
},
|
},
|
||||||
"adaptive_layer_height_enabled":
|
"adaptive_layer_height_enabled":
|
||||||
{
|
{
|
||||||
"label": "Use adaptive layers",
|
"label": "Use Adaptive Layers",
|
||||||
"description": "Adaptive layers computes the layer heights depending on the shape of the model.",
|
"description": "Adaptive layers computes the layer heights depending on the shape of the model.",
|
||||||
"type": "bool",
|
"type": "bool",
|
||||||
"default_value": false,
|
"default_value": false,
|
||||||
|
@ -6669,7 +6700,7 @@
|
||||||
},
|
},
|
||||||
"adaptive_layer_height_variation":
|
"adaptive_layer_height_variation":
|
||||||
{
|
{
|
||||||
"label": "Adaptive layers maximum variation",
|
"label": "Adaptive Layers Maximum Variation",
|
||||||
"description": "The maximum allowed height different from the base layer height.",
|
"description": "The maximum allowed height different from the base layer height.",
|
||||||
"type": "float",
|
"type": "float",
|
||||||
"enabled": "adaptive_layer_height_enabled",
|
"enabled": "adaptive_layer_height_enabled",
|
||||||
|
@ -6681,7 +6712,7 @@
|
||||||
},
|
},
|
||||||
"adaptive_layer_height_variation_step":
|
"adaptive_layer_height_variation_step":
|
||||||
{
|
{
|
||||||
"label": "Adaptive layers variation step size",
|
"label": "Adaptive Layers Variation Step Size",
|
||||||
"description": "The difference in height of the next layer height compared to the previous one.",
|
"description": "The difference in height of the next layer height compared to the previous one.",
|
||||||
"type": "float",
|
"type": "float",
|
||||||
"enabled": "adaptive_layer_height_enabled",
|
"enabled": "adaptive_layer_height_enabled",
|
||||||
|
@ -6694,7 +6725,7 @@
|
||||||
},
|
},
|
||||||
"adaptive_layer_height_threshold":
|
"adaptive_layer_height_threshold":
|
||||||
{
|
{
|
||||||
"label": "Adaptive layers threshold",
|
"label": "Adaptive Layers Threshold",
|
||||||
"description": "Threshold whether to use a smaller layer or not. This number is compared to the tan of the steepest slope in a layer.",
|
"description": "Threshold whether to use a smaller layer or not. This number is compared to the tan of the steepest slope in a layer.",
|
||||||
"type": "float",
|
"type": "float",
|
||||||
"enabled": "adaptive_layer_height_enabled",
|
"enabled": "adaptive_layer_height_enabled",
|
||||||
|
@ -6966,7 +6997,7 @@
|
||||||
"enabled": "bridge_settings_enabled and bridge_enable_more_layers",
|
"enabled": "bridge_settings_enabled and bridge_enable_more_layers",
|
||||||
"settable_per_mesh": true
|
"settable_per_mesh": true
|
||||||
},
|
},
|
||||||
"clean_between_layers":
|
"clean_between_layers":
|
||||||
{
|
{
|
||||||
"label": "Wipe Nozzle Between Layers",
|
"label": "Wipe Nozzle Between Layers",
|
||||||
"description": "Whether to include nozzle wipe G-Code between layers. Enabling this setting could influence behavior of retract at layer change. Please use Wipe Retraction settings to control retraction at layers where the wipe script will be working.",
|
"description": "Whether to include nozzle wipe G-Code between layers. Enabling this setting could influence behavior of retract at layer change. Please use Wipe Retraction settings to control retraction at layers where the wipe script will be working.",
|
||||||
|
@ -6976,7 +7007,7 @@
|
||||||
"settable_per_extruder": true,
|
"settable_per_extruder": true,
|
||||||
"settable_per_meshgroup": false
|
"settable_per_meshgroup": false
|
||||||
},
|
},
|
||||||
"max_extrusion_before_wipe":
|
"max_extrusion_before_wipe":
|
||||||
{
|
{
|
||||||
"label": "Material Volume Between Wipes",
|
"label": "Material Volume Between Wipes",
|
||||||
"description": "Maximum material, that can be extruded before another nozzle wipe is initiated.",
|
"description": "Maximum material, that can be extruded before another nozzle wipe is initiated.",
|
||||||
|
@ -6988,7 +7019,7 @@
|
||||||
"settable_per_extruder": true,
|
"settable_per_extruder": true,
|
||||||
"settable_per_meshgroup": false
|
"settable_per_meshgroup": false
|
||||||
},
|
},
|
||||||
"wipe_retraction_enable":
|
"wipe_retraction_enable":
|
||||||
{
|
{
|
||||||
"label": "Wipe Retraction Enable",
|
"label": "Wipe Retraction Enable",
|
||||||
"description": "Retract the filament when the nozzle is moving over a non-printed area.",
|
"description": "Retract the filament when the nozzle is moving over a non-printed area.",
|
||||||
|
@ -6999,7 +7030,7 @@
|
||||||
"settable_per_extruder": true,
|
"settable_per_extruder": true,
|
||||||
"settable_per_meshgroup": false
|
"settable_per_meshgroup": false
|
||||||
},
|
},
|
||||||
"wipe_retraction_amount":
|
"wipe_retraction_amount":
|
||||||
{
|
{
|
||||||
"label": "Wipe Retraction Distance",
|
"label": "Wipe Retraction Distance",
|
||||||
"description": "Amount to retract the filament so it does not ooze during the wipe sequence.",
|
"description": "Amount to retract the filament so it does not ooze during the wipe sequence.",
|
||||||
|
@ -7013,7 +7044,7 @@
|
||||||
"settable_per_extruder": true,
|
"settable_per_extruder": true,
|
||||||
"settable_per_meshgroup": false
|
"settable_per_meshgroup": false
|
||||||
},
|
},
|
||||||
"wipe_retraction_extra_prime_amount":
|
"wipe_retraction_extra_prime_amount":
|
||||||
{
|
{
|
||||||
"label": "Wipe Retraction Extra Prime Amount",
|
"label": "Wipe Retraction Extra Prime Amount",
|
||||||
"description": "Some material can ooze away during a wipe travel moves, which can be compensated for here.",
|
"description": "Some material can ooze away during a wipe travel moves, which can be compensated for here.",
|
||||||
|
@ -7026,7 +7057,7 @@
|
||||||
"settable_per_mesh": false,
|
"settable_per_mesh": false,
|
||||||
"settable_per_extruder": true
|
"settable_per_extruder": true
|
||||||
},
|
},
|
||||||
"wipe_retraction_speed":
|
"wipe_retraction_speed":
|
||||||
{
|
{
|
||||||
"label": "Wipe Retraction Speed",
|
"label": "Wipe Retraction Speed",
|
||||||
"description": "The speed at which the filament is retracted and primed during a wipe retraction move.",
|
"description": "The speed at which the filament is retracted and primed during a wipe retraction move.",
|
||||||
|
@ -7040,9 +7071,9 @@
|
||||||
"enabled": "wipe_retraction_enable and clean_between_layers",
|
"enabled": "wipe_retraction_enable and clean_between_layers",
|
||||||
"settable_per_mesh": false,
|
"settable_per_mesh": false,
|
||||||
"settable_per_extruder": true,
|
"settable_per_extruder": true,
|
||||||
"children":
|
"children":
|
||||||
{
|
{
|
||||||
"wipe_retraction_retract_speed":
|
"wipe_retraction_retract_speed":
|
||||||
{
|
{
|
||||||
"label": "Wipe Retraction Retract Speed",
|
"label": "Wipe Retraction Retract Speed",
|
||||||
"description": "The speed at which the filament is retracted during a wipe retraction move.",
|
"description": "The speed at which the filament is retracted during a wipe retraction move.",
|
||||||
|
@ -7058,7 +7089,7 @@
|
||||||
"settable_per_mesh": false,
|
"settable_per_mesh": false,
|
||||||
"settable_per_extruder": true
|
"settable_per_extruder": true
|
||||||
},
|
},
|
||||||
"wipe_retraction_prime_speed":
|
"wipe_retraction_prime_speed":
|
||||||
{
|
{
|
||||||
"label": "Retraction Prime Speed",
|
"label": "Retraction Prime Speed",
|
||||||
"description": "The speed at which the filament is primed during a wipe retraction move.",
|
"description": "The speed at which the filament is primed during a wipe retraction move.",
|
||||||
|
@ -7076,7 +7107,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"wipe_pause":
|
"wipe_pause":
|
||||||
{
|
{
|
||||||
"label": "Wipe Pause",
|
"label": "Wipe Pause",
|
||||||
"description": "Pause after the unretract.",
|
"description": "Pause after the unretract.",
|
||||||
|
@ -7089,7 +7120,7 @@
|
||||||
"settable_per_extruder": true,
|
"settable_per_extruder": true,
|
||||||
"settable_per_meshgroup": false
|
"settable_per_meshgroup": false
|
||||||
},
|
},
|
||||||
"wipe_hop_enable":
|
"wipe_hop_enable":
|
||||||
{
|
{
|
||||||
"label": "Wipe Z Hop When Retracted",
|
"label": "Wipe Z Hop When Retracted",
|
||||||
"description": "Whenever a retraction is done, the build plate is lowered to create clearance between the nozzle and the print. It prevents the nozzle from hitting the print during travel moves, reducing the chance to knock the print from the build plate.",
|
"description": "Whenever a retraction is done, the build plate is lowered to create clearance between the nozzle and the print. It prevents the nozzle from hitting the print during travel moves, reducing the chance to knock the print from the build plate.",
|
||||||
|
@ -7100,7 +7131,7 @@
|
||||||
"settable_per_extruder": true,
|
"settable_per_extruder": true,
|
||||||
"settable_per_meshgroup": false
|
"settable_per_meshgroup": false
|
||||||
},
|
},
|
||||||
"wipe_hop_amount":
|
"wipe_hop_amount":
|
||||||
{
|
{
|
||||||
"label": "Wipe Z Hop Height",
|
"label": "Wipe Z Hop Height",
|
||||||
"description": "The height difference when performing a Z Hop.",
|
"description": "The height difference when performing a Z Hop.",
|
||||||
|
@ -7112,7 +7143,7 @@
|
||||||
"settable_per_extruder": true,
|
"settable_per_extruder": true,
|
||||||
"settable_per_meshgroup": false
|
"settable_per_meshgroup": false
|
||||||
},
|
},
|
||||||
"wipe_hop_speed":
|
"wipe_hop_speed":
|
||||||
{
|
{
|
||||||
"label": "Wipe Hop Speed",
|
"label": "Wipe Hop Speed",
|
||||||
"description": "Speed to move the z-axis during the hop.",
|
"description": "Speed to move the z-axis during the hop.",
|
||||||
|
@ -7126,7 +7157,7 @@
|
||||||
"settable_per_extruder": true,
|
"settable_per_extruder": true,
|
||||||
"settable_per_meshgroup": false
|
"settable_per_meshgroup": false
|
||||||
},
|
},
|
||||||
"wipe_brush_pos_x":
|
"wipe_brush_pos_x":
|
||||||
{
|
{
|
||||||
"label": "Wipe Brush X Position",
|
"label": "Wipe Brush X Position",
|
||||||
"description": "X location where wipe script will start.",
|
"description": "X location where wipe script will start.",
|
||||||
|
@ -7139,7 +7170,7 @@
|
||||||
"settable_per_extruder": true,
|
"settable_per_extruder": true,
|
||||||
"settable_per_meshgroup": false
|
"settable_per_meshgroup": false
|
||||||
},
|
},
|
||||||
"wipe_repeat_count":
|
"wipe_repeat_count":
|
||||||
{
|
{
|
||||||
"label": "Wipe Repeat Count",
|
"label": "Wipe Repeat Count",
|
||||||
"description": "Number of times to move the nozzle across the brush.",
|
"description": "Number of times to move the nozzle across the brush.",
|
||||||
|
@ -7151,7 +7182,7 @@
|
||||||
"settable_per_extruder": true,
|
"settable_per_extruder": true,
|
||||||
"settable_per_meshgroup": false
|
"settable_per_meshgroup": false
|
||||||
},
|
},
|
||||||
"wipe_move_distance":
|
"wipe_move_distance":
|
||||||
{
|
{
|
||||||
"label": "Wipe Move Distance",
|
"label": "Wipe Move Distance",
|
||||||
"description": "The distance to move the head back and forth across the brush.",
|
"description": "The distance to move the head back and forth across the brush.",
|
||||||
|
@ -7165,41 +7196,47 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"command_line_settings": {
|
"command_line_settings":
|
||||||
|
{
|
||||||
"label": "Command Line Settings",
|
"label": "Command Line Settings",
|
||||||
"description": "Settings which are only used if CuraEngine isn't called from the Cura frontend.",
|
"description": "Settings which are only used if CuraEngine isn't called from the Cura frontend.",
|
||||||
"type": "category",
|
"type": "category",
|
||||||
"enabled": false,
|
"enabled": false,
|
||||||
"children": {
|
"children": {
|
||||||
"center_object": {
|
"center_object":
|
||||||
|
{
|
||||||
"description": "Whether to center the object on the middle of the build platform (0,0), instead of using the coordinate system in which the object was saved.",
|
"description": "Whether to center the object on the middle of the build platform (0,0), instead of using the coordinate system in which the object was saved.",
|
||||||
"type": "bool",
|
"type": "bool",
|
||||||
"label": "Center Object",
|
"label": "Center Object",
|
||||||
"default_value": false,
|
"default_value": false,
|
||||||
"enabled": false
|
"enabled": false
|
||||||
},
|
},
|
||||||
"mesh_position_x": {
|
"mesh_position_x":
|
||||||
|
{
|
||||||
"description": "Offset applied to the object in the x direction.",
|
"description": "Offset applied to the object in the x direction.",
|
||||||
"type": "float",
|
"type": "float",
|
||||||
"label": "Mesh Position X",
|
"label": "Mesh Position X",
|
||||||
"default_value": 0,
|
"default_value": 0,
|
||||||
"enabled": false
|
"enabled": false
|
||||||
},
|
},
|
||||||
"mesh_position_y": {
|
"mesh_position_y":
|
||||||
|
{
|
||||||
"description": "Offset applied to the object in the y direction.",
|
"description": "Offset applied to the object in the y direction.",
|
||||||
"type": "float",
|
"type": "float",
|
||||||
"label": "Mesh Position Y",
|
"label": "Mesh Position Y",
|
||||||
"default_value": 0,
|
"default_value": 0,
|
||||||
"enabled": false
|
"enabled": false
|
||||||
},
|
},
|
||||||
"mesh_position_z": {
|
"mesh_position_z":
|
||||||
|
{
|
||||||
"description": "Offset applied to the object in the z direction. With this you can perform what was used to be called 'Object Sink'.",
|
"description": "Offset applied to the object in the z direction. With this you can perform what was used to be called 'Object Sink'.",
|
||||||
"type": "float",
|
"type": "float",
|
||||||
"label": "Mesh Position Z",
|
"label": "Mesh Position Z",
|
||||||
"default_value": 0,
|
"default_value": 0,
|
||||||
"enabled": false
|
"enabled": false
|
||||||
},
|
},
|
||||||
"mesh_rotation_matrix": {
|
"mesh_rotation_matrix":
|
||||||
|
{
|
||||||
"label": "Mesh Rotation Matrix",
|
"label": "Mesh Rotation Matrix",
|
||||||
"description": "Transformation matrix to be applied to the model when loading it from file.",
|
"description": "Transformation matrix to be applied to the model when loading it from file.",
|
||||||
"type": "str",
|
"type": "str",
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue