This commit is contained in:
fieldOfView 2016-06-23 14:08:49 +02:00
commit 19412c5bda
34 changed files with 1154 additions and 990 deletions

View file

@ -6,6 +6,12 @@ include(GNUInstallDirs)
set(URANIUM_SCRIPTS_DIR "${CMAKE_SOURCE_DIR}/../uranium/scripts" CACHE DIRECTORY "The location of the scripts directory of the Uranium repository")
# Tests
# Note that we use exit 0 here to not mark the build as a failure on test failure
add_custom_target(tests)
add_custom_command(TARGET tests POST_BUILD COMMAND "PYTHONPATH=${CMAKE_SOURCE_DIR}/../Uranium/:${CMAKE_SOURCE_DIR}" ${PYTHON_EXECUTABLE} -m pytest -r a --junitxml=${CMAKE_BINARY_DIR}/junit.xml ${CMAKE_SOURCE_DIR} || exit 0)
set(CURA_VERSION "master" CACHE STRING "Version name of Cura")
set(CURA_BUILDTYPE "" CACHE STRING "Build type of Cura, eg. 'PPA'")
configure_file(cura/CuraVersion.py.in CuraVersion.py @ONLY)

View file

@ -44,6 +44,7 @@ from . import ZOffsetDecorator
from . import CuraSplashScreen
from . import MachineManagerModel
from . import ContainerSettingsModel
from . import MachineActionManager
from PyQt5.QtCore import pyqtSlot, QUrl, pyqtSignal, pyqtProperty, QEvent, Q_ENUMS
from PyQt5.QtGui import QColor, QIcon
@ -99,6 +100,8 @@ class CuraApplication(QtApplication):
SettingDefinition.addSupportedProperty("settable_globally", DefinitionPropertyType.Any, default = True)
SettingDefinition.addSettingType("extruder", int, str, UM.Settings.Validator)
self._machine_action_manager = MachineActionManager.MachineActionManager()
super().__init__(name = "cura", version = CuraVersion, buildtype = CuraBuildType)
self.setWindowIcon(QIcon(Resources.getPath(Resources.Images, "cura-icon.png")))
@ -367,6 +370,7 @@ class CuraApplication(QtApplication):
qmlRegisterSingletonType(MachineManagerModel.MachineManagerModel, "Cura", 1, 0, "MachineManager",
MachineManagerModel.createMachineManagerModel)
qmlRegisterSingletonType(MachineActionManager.MachineActionManager, "Cura", 1, 0, "MachineActionManager", self.getMachineActionManager)
self.setMainQml(Resources.getPath(self.ResourceTypes.QmlFiles, "Cura.qml"))
self._qml_import_paths.append(Resources.getPath(self.ResourceTypes.QmlFiles))
self.initializeEngine()
@ -383,6 +387,12 @@ class CuraApplication(QtApplication):
self.exec_()
## Get the machine action manager
# We ignore any *args given to this, as we also register the machine manager as qml singleton.
# It wants to give this function an engine and script engine, but we don't care about that.
def getMachineActionManager(self, *args):
return self._machine_action_manager
## Handle Qt events
def event(self, event):
if event.type() == QEvent.FileOpen:

78
cura/MachineAction.py Normal file
View file

@ -0,0 +1,78 @@
# Copyright (c) 2016 Ultimaker B.V.
# Cura is released under the terms of the AGPLv3 or higher.
from PyQt5.QtCore import QObject, pyqtSlot, pyqtProperty, pyqtSignal, QUrl
from PyQt5.QtQml import QQmlComponent, QQmlContext
from UM.PluginObject import PluginObject
from UM.PluginRegistry import PluginRegistry
from UM.Application import Application
import os
class MachineAction(QObject, PluginObject):
def __init__(self, key, label = ""):
super().__init__()
self._key = key
self._label = label
self._qml_url = ""
self._component = None
self._context = None
self._view = None
self._finished = False
labelChanged = pyqtSignal()
onFinished = pyqtSignal()
def getKey(self):
return self._key
@pyqtProperty(str, notify = labelChanged)
def label(self):
return self._label
def setLabel(self, label):
if self._label != label:
self._label = label
self.labelChanged.emit()
## Reset the action to it's default state.
# This should not be re-implemented by child classes, instead re-implement _reset.
# /sa _reset
@pyqtSlot()
def reset(self):
self._finished = False
self._reset()
## Protected implementation of reset.
# /sa reset()
def _reset(self):
pass
@pyqtSlot()
def setFinished(self):
self._finished = True
self._reset()
self.onFinished.emit()
@pyqtProperty(bool, notify = onFinished)
def finished(self):
return self._finished
def _createViewFromQML(self):
path = QUrl.fromLocalFile(
os.path.join(PluginRegistry.getInstance().getPluginPath(self.getPluginId()), self._qml_url))
self._component = QQmlComponent(Application.getInstance()._engine, path)
self._context = QQmlContext(Application.getInstance()._engine.rootContext())
self._context.setContextProperty("manager", self)
self._view = self._component.create(self._context)
@pyqtProperty(QObject, constant = True)
def displayItem(self):
if not self._component:
self._createViewFromQML()
return self._view

View file

@ -0,0 +1,143 @@
# Copyright (c) 2016 Ultimaker B.V.
# Cura is released under the terms of the AGPLv3 or higher.
from UM.Logger import Logger
from UM.PluginRegistry import PluginRegistry # So MachineAction can be added as plugin type
from UM.Settings.ContainerRegistry import ContainerRegistry
from UM.Settings.DefinitionContainer import DefinitionContainer
from PyQt5.QtCore import QObject, pyqtSlot
## Raised when trying to add an unknown machine action as a required action
class UnknownMachineActionError(Exception):
pass
## Raised when trying to add a machine action that does not have an unique key.
class NotUniqueMachineActionError(Exception):
pass
class MachineActionManager(QObject):
def __init__(self, parent = None):
super().__init__(parent)
self._machine_actions = {} # Dict of all known machine actions
self._required_actions = {} # Dict of all required actions by definition ID
self._supported_actions = {} # Dict of all supported actions by definition ID
self._first_start_actions = {} # Dict of all actions that need to be done when first added by definition ID
# Add machine_action as plugin type
PluginRegistry.addType("machine_action", self.addMachineAction)
# Ensure that all containers that were registered before creation of this registry are also handled.
# This should not have any effect, but it makes it safer if we ever refactor the order of things.
for container in ContainerRegistry.getInstance().findDefinitionContainers():
self._onContainerAdded(container)
ContainerRegistry.getInstance().containerAdded.connect(self._onContainerAdded)
def _onContainerAdded(self, container):
## Ensure that the actions are added to this manager
if isinstance(container, DefinitionContainer):
supported_actions = container.getMetaDataEntry("supported_actions", [])
for action in supported_actions:
self.addSupportedAction(container.getId(), action)
required_actions = container.getMetaDataEntry("required_actions", [])
for action in required_actions:
self.addRequiredAction(container.getId(), action)
first_start_actions = container.getMetaDataEntry("first_start_actions", [])
for action in first_start_actions:
self.addFirstStartAction(container.getId(), action)
## Add a required action to a machine
# Raises an exception when the action is not recognised.
def addRequiredAction(self, definition_id, action_key):
if action_key in self._machine_actions:
if definition_id in self._required_actions:
self._required_actions[definition_id] |= {self._machine_actions[action_key]}
else:
self._required_actions[definition_id] = {self._machine_actions[action_key]}
else:
raise UnknownMachineActionError("Action %s, which is required for %s is not known." % (action_key, definition_id))
## Add a supported action to a machine.
def addSupportedAction(self, definition_id, action_key):
if action_key in self._machine_actions:
if definition_id in self._supported_actions:
self._supported_actions[definition_id] |= {self._machine_actions[action_key]}
else:
self._supported_actions[definition_id] = {self._machine_actions[action_key]}
else:
Logger.log("w", "Unable to add %s to %s, as the action is not recognised", action_key, definition_id)
## Add an action to the first start list of a machine.
def addFirstStartAction(self, definition_id, action_key, index = None):
if action_key in self._machine_actions:
if definition_id in self._first_start_actions:
if index is not None:
self._first_start_actions[definition_id].insert(index, self._machine_actions[action_key])
else:
self._first_start_actions[definition_id].append(self._machine_actions[action_key])
else:
self._first_start_actions[definition_id] = [self._machine_actions[action_key]]
else:
Logger.log("w", "Unable to add %s to %s, as the action is not recognised", action_key, definition_id)
## Add a (unique) MachineAction
# if the Key of the action is not unique, an exception is raised.
def addMachineAction(self, action):
if action.getKey() not in self._machine_actions:
self._machine_actions[action.getKey()] = action
else:
raise NotUniqueMachineActionError("MachineAction with key %s was already added. Actions must have unique keys.", action.getKey())
## Get all actions supported by given machine
# \param definition_id The ID of the definition you want the supported actions of
# \returns set of supported actions.
@pyqtSlot(str, result = "QVariantList")
def getSupportedActions(self, definition_id):
if definition_id in self._supported_actions:
return list(self._supported_actions[definition_id])
else:
return set()
## Get all actions required by given machine
# \param definition_id The ID of the definition you want the required actions of
# \returns set of required actions.
def getRequiredActions(self, definition_id):
if definition_id in self._required_actions:
return self._required_actions[definition_id]
else:
return set()
## Get all actions that need to be performed upon first start of a given machine.
# Note that contrary to required / supported actions a list is returned (as it could be required to run the same
# action multiple times).
# \param definition_id The ID of the definition that you want to get the "on added" actions for.
# \returns List of actions.
@pyqtSlot(str, result="QVariantList")
def getFirstStartActions(self, definition_id):
if definition_id in self._first_start_actions:
return self._first_start_actions[definition_id]
else:
return []
## Remove Machine action from manager
# \param action to remove
def removeMachineAction(self, action):
try:
del self._machine_actions[action.getKey()]
except KeyError:
Logger.log("w", "Trying to remove MachineAction (%s) that was already removed", action.getKey())
## Get MachineAction by key
# \param key String of key to select
# \return Machine action if found, None otherwise
def getMachineAction(self, key):
if key in self._machine_actions:
return self._machine_actions[key]
else:
return None

View file

@ -3,36 +3,44 @@ from UM.Application import Application
from UM.Settings.SettingInstance import SettingInstance
from UM.Logger import Logger
import UM.Settings.Models
from cura.SettingOverrideDecorator import SettingOverrideDecorator
## The per object setting visibility handler ensures that only setting defintions that have a matching instance Container
# are returned as visible.
class PerObjectSettingVisibilityHandler(QObject):
class PerObjectSettingVisibilityHandler(UM.Settings.Models.SettingVisibilityHandler):
def __init__(self, parent = None, *args, **kwargs):
super().__init__(parent = parent, *args, **kwargs)
self._selected_object_id = None
visibilityChanged = pyqtSignal()
self._selected_object_id = None
self._node = None
self._stack = None
def setSelectedObjectId(self, id):
self._selected_object_id = id
self.visibilityChanged.emit()
if id != self._selected_object_id:
self._selected_object_id = id
self._node = Application.getInstance().getController().getScene().findObject(self._selected_object_id)
if self._node:
self._stack = self._node.callDecoration("getStack")
self.visibilityChanged.emit()
@pyqtProperty("quint64", fset = setSelectedObjectId)
def selectedObjectId(self):
pass
return self._selected_object_id
def setVisible(self, visible):
node = Application.getInstance().getController().getScene().findObject(self._selected_object_id)
if not node:
if not self._node:
return
stack = node.callDecoration("getStack")
if not stack:
node.addDecorator(SettingOverrideDecorator())
stack = node.callDecoration("getStack")
settings = stack.getTop()
all_instances = settings.findInstances(**{})
if not self._stack:
self._node.addDecorator(SettingOverrideDecorator())
self._stack = self._node.callDecoration("getStack")
settings = self._stack.getTop()
all_instances = settings.findInstances()
visibility_changed = False # Flag to check if at the end the signal needs to be emitted
# Remove all instances that are not in visibility list
@ -41,13 +49,12 @@ class PerObjectSettingVisibilityHandler(QObject):
settings.removeInstance(instance.definition.key)
visibility_changed = True
# Add all instances that are not added, but are in visiblity list
# Add all instances that are not added, but are in visibility list
for item in visible:
if not settings.getInstance(item):
definition_container = Application.getInstance().getGlobalContainerStack().getBottom()
definitions = definition_container.findDefinitions(key = item)
if definitions:
settings.addInstance(SettingInstance(definitions[0], settings))
definition = self._stack.getSettingDefinition(item)
if definition:
settings.addInstance(SettingInstance(definition, settings))
visibility_changed = True
else:
Logger.log("w", "Unable to add instance (%s) to perobject visibility because we couldn't find the matching definition", item)
@ -57,20 +64,16 @@ class PerObjectSettingVisibilityHandler(QObject):
def getVisible(self):
visible_settings = set()
node = Application.getInstance().getController().getScene().findObject(self._selected_object_id)
if not node:
if not self._node:
return visible_settings
stack = node.callDecoration("getStack")
if not stack:
if not self._stack:
return visible_settings
settings = stack.getTop()
settings = self._stack.getTop()
if not settings:
return visible_settings
all_instances = settings.findInstances(**{})
for instance in all_instances:
visible_settings.add(instance.definition.key)
visible_settings = set(map(lambda i: i.definition.key, settings.findInstances()))
return visible_settings

View file

@ -153,6 +153,8 @@ Item {
{
id: addedSettingsModel;
containerId: Cura.MachineManager.activeDefinitionId
expanded: [ "*" ]
visibilityHandler: Cura.PerObjectSettingVisibilityHandler
{
selectedObjectId: UM.ActiveTool.properties.getValue("SelectedObjectId")
@ -215,9 +217,8 @@ Item {
style: ButtonStyle
{
background: Rectangle
background: Item
{
color: control.hovered ? control.parent.style.controlHighlightColor : control.parent.style.controlColor;
UM.RecolorImage
{
anchors.verticalCenter: parent.verticalCenter
@ -340,6 +341,8 @@ Item {
"settable_per_mesh": true
}
visibilityHandler: UM.SettingPreferenceVisibilityHandler {}
expanded: [ "*" ]
exclude: [ "machine_settings" ]
}
delegate:Loader
{

View file

@ -0,0 +1,53 @@
from cura.MachineAction import MachineAction
from PyQt5.QtCore import pyqtSlot
from UM.Application import Application
from cura.PrinterOutputDevice import PrinterOutputDevice
class BedLevelMachineAction(MachineAction):
def __init__(self):
super().__init__("BedLevel", "Level bed")
self._qml_url = "BedLevelMachineAction.qml"
self._bed_level_position = 0
def _execute(self):
pass
def _reset(self):
self._bed_level_position = 0
printer_output_devices = self._getPrinterOutputDevices()
if printer_output_devices:
printer_output_devices[0].homeBed()
printer_output_devices[0].moveHead(0, 0, 3)
printer_output_devices[0].homeHead()
def _getPrinterOutputDevices(self):
return [printer_output_device for printer_output_device in Application.getInstance().getOutputDeviceManager().getOutputDevices() if isinstance(printer_output_device, PrinterOutputDevice)]
@pyqtSlot()
def moveToNextLevelPosition(self):
output_devices = self._getPrinterOutputDevices()
if output_devices: # We found at least one output device
output_device = output_devices[0]
if self._bed_level_position == 0:
output_device.moveHead(0, 0, 3)
output_device.homeHead()
output_device.moveHead(0, 0, 3)
output_device.moveHead(Application.getInstance().getGlobalContainerStack().getProperty("machine_width", "value") - 10, 0, 0)
output_device.moveHead(0, 0, -3)
self._bed_level_position += 1
elif self._bed_level_position == 1:
output_device.moveHead(0, 0, 3)
output_device.moveHead(-Application.getInstance().getGlobalContainerStack().getProperty("machine_width", "value" ) / 2, Application.getInstance().getGlobalContainerStack().getProperty("machine_depth", "value") - 10, 0)
output_device.moveHead(0, 0, -3)
self._bed_level_position += 1
elif self._bed_level_position == 2:
output_device.moveHead(0, 0, 3)
output_device.moveHead(-Application.getInstance().getGlobalContainerStack().getProperty("machine_width", "value") / 2 + 10, -(Application.getInstance().getGlobalContainerStack().getProperty("machine_depth", "value") + 10), 0)
output_device.moveHead(0, 0, -3)
self._bed_level_position += 1
elif self._bed_level_position >= 3:
self.setFinished()

View file

@ -0,0 +1,85 @@
// Copyright (c) 2016 Ultimaker B.V.
// Cura is released under the terms of the AGPLv3 or higher.
import QtQuick 2.2
import QtQuick.Controls 1.1
import QtQuick.Layouts 1.1
import QtQuick.Window 2.1
import UM 1.2 as UM
import Cura 1.0 as Cura
Cura.MachineAction
{
anchors.fill: parent;
Item
{
id: bedLevelMachineAction
anchors.fill: parent;
UM.I18nCatalog { id: catalog; name: "cura"; }
Label
{
id: pageTitle
width: parent.width
text: catalog.i18nc("@title", "Bed Leveling")
wrapMode: Text.WordWrap
font.pointSize: 18;
}
Label
{
id: pageDescription
anchors.top: pageTitle.bottom
anchors.topMargin: UM.Theme.getSize("default_margin").height
width: parent.width
wrapMode: Text.WordWrap
text: catalog.i18nc("@label", "To make sure your prints will come out great, you can now adjust your buildplate. When you click 'Move to Next Position' the nozzle will move to the different positions that can be adjusted.")
}
Label
{
id: bedlevelingText
anchors.top: pageDescription.bottom
anchors.topMargin: UM.Theme.getSize("default_margin").height
width: parent.width
wrapMode: Text.WordWrap
text: catalog.i18nc("@label", "For every position; insert a piece of paper under the nozzle and adjust the print bed height. The print bed height is right when the paper is slightly gripped by the tip of the nozzle.")
}
Item
{
id: bedlevelingWrapper
anchors.top: bedlevelingText.bottom
anchors.topMargin: UM.Theme.getSize("default_margin").height
anchors.horizontalCenter: parent.horizontalCenter
height: skipBedlevelingButton.height
width: bedlevelingButton.width + skipBedlevelingButton.width + UM.Theme.getSize("default_margin").height < bedLevelMachineAction.width ? bedlevelingButton.width + skipBedlevelingButton.width + UM.Theme.getSize("default_margin").height : bedLevelMachineAction.width
Button
{
id: bedlevelingButton
anchors.top: parent.top
anchors.left: parent.left
text: catalog.i18nc("@action:button","Move to Next Position");
onClicked:
{
manager.moveToNextLevelPosition()
}
}
Button
{
id: skipBedlevelingButton
anchors.top: parent.width < bedLevelMachineAction.width ? parent.top : bedlevelingButton.bottom
anchors.topMargin: parent.width < bedLevelMachineAction.width ? 0 : UM.Theme.getSize("default_margin").height/2
anchors.left: parent.width < bedLevelMachineAction.width ? bedlevelingButton.right : parent.left
anchors.leftMargin: parent.width < bedLevelMachineAction.width ? UM.Theme.getSize("default_margin").width : 0
text: catalog.i18nc("@action:button","Skip bed leveling");
onClicked:
{
manager.setFinished()
}
}
}
}
}

View file

@ -0,0 +1,160 @@
from cura.MachineAction import MachineAction
from cura.PrinterOutputDevice import PrinterOutputDevice
from UM.Application import Application
from PyQt5.QtCore import pyqtSlot, pyqtSignal, pyqtProperty
class UMOCheckupMachineAction(MachineAction):
def __init__(self):
super().__init__("UMOCheckup", "Checkup")
self._qml_url = "UMOCheckupMachineAction.qml"
self._hotend_target_temp = 180
self._bed_target_temp = 60
self._output_device = None
self._bed_test_completed = False
self._hotend_test_completed = False
# Endstop tests
self._x_min_endstop_test_completed = False
self._y_min_endstop_test_completed = False
self._z_min_endstop_test_completed = False
self._check_started = False
Application.getInstance().getOutputDeviceManager().outputDevicesChanged.connect(self._onOutputDevicesChanged)
onBedTestCompleted = pyqtSignal()
onHotendTestCompleted = pyqtSignal()
onXMinEndstopTestCompleted = pyqtSignal()
onYMinEndstopTestCompleted = pyqtSignal()
onZMinEndstopTestCompleted = pyqtSignal()
bedTemperatureChanged = pyqtSignal()
hotendTemperatureChanged = pyqtSignal()
def _onOutputDevicesChanged(self):
# Check if this action was started, but no output device was found the first time.
# If so, re-try now that an output device has been added/removed.
if self._output_device is None and self._check_started:
self.startCheck()
def _getPrinterOutputDevices(self):
return [printer_output_device for printer_output_device in
Application.getInstance().getOutputDeviceManager().getOutputDevices() if
isinstance(printer_output_device, PrinterOutputDevice)]
def _reset(self):
if self._output_device:
self._output_device.bedTemperatureChanged.disconnect(self.bedTemperatureChanged)
self._output_device.hotendTemperaturesChanged.disconnect(self.hotendTemperatureChanged)
self._output_device.bedTemperatureChanged.disconnect(self._onBedTemperatureChanged)
self._output_device.hotendTemperaturesChanged.disconnect(self._onHotendTemperatureChanged)
self._output_device.endstopStateChanged.disconnect(self._onEndstopStateChanged)
try:
self._output_device.stopPollEndstop()
except AttributeError: # Connection is probably not a USB connection. Something went pretty wrong if this happens.
pass
self._output_device = None
self._check_started = False
# Ensure everything is reset (and right signals are emitted again)
self._bed_test_completed = False
self.onBedTestCompleted.emit()
self._hotend_test_completed = False
self.onHotendTestCompleted.emit()
self._x_min_endstop_test_completed = False
self.onXMinEndstopTestCompleted.emit()
self._y_min_endstop_test_completed = False
self.onYMinEndstopTestCompleted.emit()
self._z_min_endstop_test_completed = False
self.onZMinEndstopTestCompleted.emit()
@pyqtProperty(bool, notify = onBedTestCompleted)
def bedTestCompleted(self):
return self._bed_test_completed
@pyqtProperty(bool, notify = onHotendTestCompleted)
def hotendTestCompleted(self):
return self._hotend_test_completed
@pyqtProperty(bool, notify = onXMinEndstopTestCompleted)
def xMinEndstopTestCompleted(self):
return self._x_min_endstop_test_completed
@pyqtProperty(bool, notify=onYMinEndstopTestCompleted)
def yMinEndstopTestCompleted(self):
return self._y_min_endstop_test_completed
@pyqtProperty(bool, notify=onZMinEndstopTestCompleted)
def zMinEndstopTestCompleted(self):
return self._z_min_endstop_test_completed
@pyqtProperty(float, notify = bedTemperatureChanged)
def bedTemperature(self):
if not self._output_device:
return 0
return self._output_device.bedTemperature
@pyqtProperty(float, notify=hotendTemperatureChanged)
def hotendTemperature(self):
if not self._output_device:
return 0
return self._output_device.hotendTemperatures[0]
def _onHotendTemperatureChanged(self):
if not self._output_device:
return
if not self._hotend_test_completed:
if self._output_device.hotendTemperatures[0] + 10 > self._hotend_target_temp and self._output_device.hotendTemperatures[0] - 10 < self._hotend_target_temp:
self._hotend_test_completed = True
self.onHotendTestCompleted.emit()
def _onBedTemperatureChanged(self):
if not self._output_device:
return
if not self._bed_test_completed:
if self._output_device.bedTemperature + 5 > self._bed_target_temp and self._output_device.bedTemperature - 5 < self._bed_target_temp:
self._bed_test_completed = True
self.onBedTestCompleted.emit()
def _onEndstopStateChanged(self, switch_type, state):
if state:
if switch_type == "x_min":
self._x_min_endstop_test_completed = True
self.onXMinEndstopTestCompleted.emit()
elif switch_type == "y_min":
self._y_min_endstop_test_completed = True
self.onYMinEndstopTestCompleted.emit()
elif switch_type == "z_min":
self._z_min_endstop_test_completed = True
self.onZMinEndstopTestCompleted.emit()
@pyqtSlot()
def startCheck(self):
self._check_started = True
output_devices = self._getPrinterOutputDevices()
if output_devices:
self._output_device = output_devices[0]
try:
self._output_device.startPollEndstop()
self._output_device.bedTemperatureChanged.connect(self.bedTemperatureChanged)
self._output_device.hotendTemperaturesChanged.connect(self.hotendTemperatureChanged)
self._output_device.bedTemperatureChanged.connect(self._onBedTemperatureChanged)
self._output_device.hotendTemperaturesChanged.connect(self._onHotendTemperatureChanged)
self._output_device.endstopStateChanged.connect(self._onEndstopStateChanged)
except AttributeError: # Connection is probably not a USB connection. Something went pretty wrong if this happens.
pass
@pyqtSlot()
def heatupHotend(self):
if self._output_device is not None:
self._output_device.setTargetHotendTemperature(0, self._hotend_target_temp)
@pyqtSlot()
def heatupBed(self):
if self._output_device is not None:
self._output_device.setTargetBedTemperature(self._bed_target_temp)

View file

@ -0,0 +1,271 @@
import UM 1.2 as UM
import Cura 1.0 as Cura
import QtQuick 2.2
import QtQuick.Controls 1.1
import QtQuick.Layouts 1.1
import QtQuick.Window 2.1
Cura.MachineAction
{
anchors.fill: parent;
Item
{
id: checkupMachineAction
anchors.fill: parent;
property int leftRow: checkupMachineAction.width * 0.40
property int rightRow: checkupMachineAction.width * 0.60
UM.I18nCatalog { id: catalog; name:"cura"}
Label
{
id: pageTitle
width: parent.width
text: catalog.i18nc("@title", "Check Printer")
wrapMode: Text.WordWrap
font.pointSize: 18;
}
Label
{
id: pageDescription
anchors.top: pageTitle.bottom
anchors.topMargin: UM.Theme.getSize("default_margin").height
width: parent.width
wrapMode: Text.WordWrap
text: catalog.i18nc("@label","It's a good idea to do a few sanity checks on your Ultimaker. You can skip this step if you know your machine is functional");
}
Item
{
id: startStopButtons
anchors.top: pageDescription.bottom
anchors.topMargin: UM.Theme.getSize("default_margin").height
anchors.horizontalCenter: parent.horizontalCenter
height: childrenRect.height
width: startCheckButton.width + skipCheckButton.width + UM.Theme.getSize("default_margin").height < checkupMachineAction.width ? startCheckButton.width + skipCheckButton.width + UM.Theme.getSize("default_margin").height : checkupMachineAction.width
Button
{
id: startCheckButton
anchors.top: parent.top
anchors.left: parent.left
text: catalog.i18nc("@action:button","Start Printer Check");
onClicked:
{
checkupContent.visible = true
startCheckButton.enabled = false
manager.startCheck()
}
}
Button
{
id: skipCheckButton
anchors.top: parent.width < checkupMachineAction.width ? parent.top : startCheckButton.bottom
anchors.topMargin: parent.width < checkupMachineAction.width ? 0 : UM.Theme.getSize("default_margin").height/2
anchors.left: parent.width < checkupMachineAction.width ? startCheckButton.right : parent.left
anchors.leftMargin: parent.width < checkupMachineAction.width ? UM.Theme.getSize("default_margin").width : 0
text: catalog.i18nc("@action:button", "Skip Printer Check");
onClicked: manager.setFinished()
}
}
Item
{
id: checkupContent
anchors.top: startStopButtons.bottom
anchors.topMargin: UM.Theme.getSize("default_margin").height
visible: false
width: parent.width
height: 250
//////////////////////////////////////////////////////////
Label
{
id: connectionLabel
width: checkupMachineAction.leftRow
anchors.left: parent.left
anchors.top: parent.top
wrapMode: Text.WordWrap
text: catalog.i18nc("@label","Connection: ")
}
Label
{
id: connectionStatus
width: checkupMachineAction.rightRow
anchors.left: connectionLabel.right
anchors.top: parent.top
wrapMode: Text.WordWrap
text: Cura.USBPrinterManager.connectedPrinterList.rowCount() > 0 || base.addOriginalProgress.checkUp[0] ? catalog.i18nc("@info:status","Done"):catalog.i18nc("@info:status","Incomplete")
}
//////////////////////////////////////////////////////////
Label
{
id: endstopXLabel
width: checkupMachineAction.leftRow
anchors.left: parent.left
anchors.top: connectionLabel.bottom
wrapMode: Text.WordWrap
text: catalog.i18nc("@label","Min endstop X: ")
}
Label
{
id: endstopXStatus
width: checkupMachineAction.rightRow
anchors.left: endstopXLabel.right
anchors.top: connectionLabel.bottom
wrapMode: Text.WordWrap
text: manager.xMinEndstopTestCompleted ? catalog.i18nc("@info:status","Works") : catalog.i18nc("@info:status","Not checked")
}
//////////////////////////////////////////////////////////////
Label
{
id: endstopYLabel
width: checkupMachineAction.leftRow
anchors.left: parent.left
anchors.top: endstopXLabel.bottom
wrapMode: Text.WordWrap
text: catalog.i18nc("@label","Min endstop Y: ")
}
Label
{
id: endstopYStatus
width: checkupMachineAction.rightRow
anchors.left: endstopYLabel.right
anchors.top: endstopXLabel.bottom
wrapMode: Text.WordWrap
text: manager.yMinEndstopTestCompleted ? catalog.i18nc("@info:status","Works") : catalog.i18nc("@info:status","Not checked")
}
/////////////////////////////////////////////////////////////////////
Label
{
id: endstopZLabel
width: checkupMachineAction.leftRow
anchors.left: parent.left
anchors.top: endstopYLabel.bottom
wrapMode: Text.WordWrap
text: catalog.i18nc("@label","Min endstop Z: ")
}
Label
{
id: endstopZStatus
width: checkupMachineAction.rightRow
anchors.left: endstopZLabel.right
anchors.top: endstopYLabel.bottom
wrapMode: Text.WordWrap
text: manager.zMinEndstopTestCompleted ? catalog.i18nc("@info:status","Works") : catalog.i18nc("@info:status","Not checked")
}
////////////////////////////////////////////////////////////
Label
{
id: nozzleTempLabel
width: checkupMachineAction.leftRow
anchors.left: parent.left
anchors.top: endstopZLabel.bottom
wrapMode: Text.WordWrap
text: catalog.i18nc("@label","Nozzle temperature check: ")
}
Label
{
id: nozzleTempStatus
width: checkupMachineAction.rightRow * 0.4
anchors.top: nozzleTempLabel.top
anchors.left: nozzleTempLabel.right
wrapMode: Text.WordWrap
text: catalog.i18nc("@info:status","Not checked")
}
Item
{
id: nozzleTempButton
width: checkupMachineAction.rightRow * 0.3
height: nozzleTemp.height
anchors.top: nozzleTempLabel.top
anchors.left: bedTempStatus.right
anchors.leftMargin: UM.Theme.getSize("default_margin").width/2
Button
{
height: nozzleTemp.height - 2
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
text: catalog.i18nc("@action:button","Start Heating")
onClicked:
{
manager.heatupHotend()
nozzleTempStatus.text = catalog.i18nc("@info:progress","Checking")
}
}
}
Label
{
id: nozzleTemp
anchors.top: nozzleTempLabel.top
anchors.left: nozzleTempButton.right
anchors.leftMargin: UM.Theme.getSize("default_margin").width
width: checkupMachineAction.rightRow * 0.2
wrapMode: Text.WordWrap
text: manager.hotendTemperature + "°C"
font.bold: true
}
/////////////////////////////////////////////////////////////////////////////
Label
{
id: bedTempLabel
width: checkupMachineAction.leftRow
anchors.left: parent.left
anchors.top: nozzleTempLabel.bottom
wrapMode: Text.WordWrap
text: catalog.i18nc("@label","bed temperature check:")
}
Label
{
id: bedTempStatus
width: checkupMachineAction.rightRow * 0.4
anchors.top: bedTempLabel.top
anchors.left: bedTempLabel.right
wrapMode: Text.WordWrap
text: manager.bedTestCompleted ? catalog.i18nc("@info:status","Not checked"): catalog.i18nc("@info:status","Checked")
}
Item
{
id: bedTempButton
width: checkupMachineAction.rightRow * 0.3
height: bedTemp.height
anchors.top: bedTempLabel.top
anchors.left: bedTempStatus.right
anchors.leftMargin: UM.Theme.getSize("default_margin").width/2
Button
{
height: bedTemp.height - 2
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
text: catalog.i18nc("@action:button","Start Heating")
onClicked:
{
manager.heatupBed()
}
}
}
Label
{
id: bedTemp
width: checkupMachineAction.rightRow * 0.2
anchors.top: bedTempLabel.top
anchors.left: bedTempButton.right
anchors.leftMargin: UM.Theme.getSize("default_margin").width
wrapMode: Text.WordWrap
text: manager.bedTemperature + "°C"
font.bold: true
}
Label
{
id: resultText
visible: false
anchors.top: bedTemp.bottom
anchors.topMargin: UM.Theme.getSize("default_margin").height
anchors.left: parent.left
width: parent.width
wrapMode: Text.WordWrap
text: catalog.i18nc("@label", "Everything is in order! You're done with your CheckUp.")
}
}
}
}

View file

@ -0,0 +1,6 @@
from cura.MachineAction import MachineAction
class UpgradeFirmwareMachineAction(MachineAction):
def __init__(self):
super().__init__("UpgradeFirmware", "Upgrade Firmware")
self._qml_url = "UpgradeFirmwareMachineAction.qml"

View file

@ -0,0 +1,85 @@
// Copyright (c) 2016 Ultimaker B.V.
// Cura is released under the terms of the AGPLv3 or higher.
import QtQuick 2.2
import QtQuick.Controls 1.1
import QtQuick.Layouts 1.1
import QtQuick.Window 2.1
import UM 1.2 as UM
import Cura 1.0 as Cura
Cura.MachineAction
{
anchors.fill: parent;
Item
{
id: upgradeFirmwareMachineAction
anchors.fill: parent;
UM.I18nCatalog { id: catalog; name:"cura"}
Label
{
id: pageTitle
width: parent.width
text: catalog.i18nc("@title", "Upgrade Firmware")
wrapMode: Text.WordWrap
font.pointSize: 18
}
Label
{
id: pageDescription
anchors.top: pageTitle.bottom
anchors.topMargin: UM.Theme.getSize("default_margin").height
width: parent.width
wrapMode: Text.WordWrap
text: catalog.i18nc("@label", "Firmware is the piece of software running directly on your 3D printer. This firmware controls the step motors, regulates the temperature and ultimately makes your printer work.")
}
Label
{
id: upgradeText1
anchors.top: pageDescription.bottom
anchors.topMargin: UM.Theme.getSize("default_margin").height
width: parent.width
wrapMode: Text.WordWrap
text: catalog.i18nc("@label", "The firmware shipping with new Ultimakers works, but upgrades have been made to make better prints, and make calibration easier.");
}
Label
{
id: upgradeText2
anchors.top: upgradeText1.bottom
anchors.topMargin: UM.Theme.getSize("default_margin").height
width: parent.width
wrapMode: Text.WordWrap
text: catalog.i18nc("@label", "Cura requires these new features and thus your firmware will most likely need to be upgraded. You can do so now.");
}
Item
{
anchors.top: upgradeText2.bottom
anchors.topMargin: UM.Theme.getSize("default_margin").height
anchors.horizontalCenter: parent.horizontalCenter
width: upgradeButton.width + skipUpgradeButton.width + UM.Theme.getSize("default_margin").height < upgradeFirmwareMachineAction.width ? upgradeButton.width + skipUpgradeButton.width + UM.Theme.getSize("default_margin").height : upgradeFirmwareMachineAction.width
Button
{
id: upgradeButton
anchors.top: parent.top
anchors.left: parent.left
text: catalog.i18nc("@action:button","Upgrade to Marlin Firmware");
onClicked: Cura.USBPrinterManager.updateAllFirmware()
}
Button
{
id: skipUpgradeButton
anchors.top: parent.width < upgradeFirmwareMachineAction.width ? parent.top : upgradeButton.bottom
anchors.topMargin: parent.width < upgradeFirmwareMachineAction.width ? 0 : UM.Theme.getSize("default_margin").height / 2
anchors.left: parent.width < upgradeFirmwareMachineAction.width ? upgradeButton.right : parent.left
anchors.leftMargin: parent.width < upgradeFirmwareMachineAction.width ? UM.Theme.getSize("default_margin").width : 0
text: catalog.i18nc("@action:button", "Skip Upgrade");
onClicked: manager.setFinished()
}
}
}
}

View file

@ -0,0 +1,23 @@
# Copyright (c) 2016 Ultimaker B.V.
# Cura is released under the terms of the AGPLv3 or higher.
from . import BedLevelMachineAction
from . import UpgradeFirmwareMachineAction
from . import UMOCheckupMachineAction
from UM.i18n import i18nCatalog
catalog = i18nCatalog("cura")
def getMetaData():
return {
"plugin": {
"name": catalog.i18nc("@label", "Ultimaker machine actions"),
"author": "Ultimaker",
"version": "1.0",
"description": catalog.i18nc("@info:whatsthis", "Provides machine actions for Ultimaker machines (such as bed leveling wizard, selecting upgrades, etc)"),
"api": 3
}
}
def register(app):
return { "machine_action": [BedLevelMachineAction.BedLevelMachineAction(), UpgradeFirmwareMachineAction.UpgradeFirmwareMachineAction(), UMOCheckupMachineAction.UMOCheckupMachineAction()]}

4
pytest.ini Normal file
View file

@ -0,0 +1,4 @@
[pytest]
testpaths = tests
python_files = Test*.py
python_classes = Test

View file

@ -12,7 +12,8 @@
"icon": "icon_ultimaker2.png",
"platform": "ultimaker2_platform.obj",
"platform_texture": "Ultimaker2backplate.png",
"platform_offset": [9, 0, 0]
"platform_offset": [9, 0, 0],
"supported_actions":["UpgradeFirmware"]
},
"overrides": {
"machine_start_gcode" : {

View file

@ -10,7 +10,8 @@
"file_formats": "text/x-gcode",
"icon": "icon_ultimaker2.png",
"platform": "ultimaker2_platform.obj",
"platform_texture": "Ultimaker2Extendedbackplate.png"
"platform_texture": "Ultimaker2Extendedbackplate.png",
"supported_actions": ["UpgradeFirmware"]
},
"overrides": {

View file

@ -9,7 +9,8 @@
"category": "Ultimaker",
"file_formats": "text/x-gcode",
"platform": "ultimaker2_platform.obj",
"platform_texture": "Ultimaker2ExtendedPlusbackplate.png"
"platform_texture": "Ultimaker2ExtendedPlusbackplate.png",
"supported_actions":["UpgradeFirmware"]
},
"overrides": {

View file

@ -11,7 +11,8 @@
"icon": "icon_ultimaker2.png",
"platform": "ultimaker2go_platform.obj",
"platform_texture": "Ultimaker2Gobackplate.png",
"platform_offset": [0, 0, 0]
"platform_offset": [0, 0, 0],
"supported_actions":["UpgradeFirmware"]
},
"overrides": {

View file

@ -16,7 +16,8 @@
"has_variants": true,
"has_materials": true,
"has_machine_materials": true,
"has_machine_quality": true
"has_machine_quality": true,
"supported_actions":["UpgradeFirmware"]
},
"overrides": {

View file

@ -14,12 +14,7 @@
"has_materials": true,
"preferred_material": "*pla*",
"preferred_quality": "*normal*",
"pages": [
"SelectUpgradedParts",
"UpgradeFirmware",
"UltimakerCheckup",
"BedLeveling"
]
"supported_actions":["UMOCheckup", "UpgradeFirmware", "BedLevel"]
},
"overrides": {

View file

@ -11,11 +11,7 @@
"icon": "icon_ultimaker.png",
"platform": "ultimaker2_platform.obj",
"platform_texture": "UltimakerPlusbackplate.png",
"pages": [
"UpgradeFirmware",
"UltimakerCheckup",
"BedLeveling"
]
"supported_actions":["UMOCheckup", "UpgradeFirmware", "BedLevel"]
},
"overrides": {

View file

@ -18,6 +18,7 @@ UM.Dialog
title: catalog.i18nc("@title:window", "Add Printer")
property string activeManufacturer: "Ultimaker";
signal machineAdded(string id)
function getMachineName()
{
var name = machineList.model.getItem(machineList.currentIndex).name
@ -162,6 +163,7 @@ UM.Dialog
base.visible = false
var item = machineList.model.getItem(machineList.currentIndex);
Cura.MachineManager.addMachine(machineName.text, item.id)
base.machineAdded(item.id) // Emit signal that the user added a machine.
}
}

View file

@ -771,6 +771,38 @@ UM.MainWindow
AddMachineDialog
{
id: addMachineDialog
onMachineAdded:
{
machineActionsWizard.start(id)
}
}
// Dialog to handle first run machine actions
UM.Wizard
{
id: machineActionsWizard;
title: catalog.i18nc("@title:window", "Add Printer")
property var machine;
function start(id)
{
var actions = Cura.MachineActionManager.getFirstStartActions(id)
resetPages() // Remove previous pages
for (var i = 0; i < actions.length; i++)
{
actions[i].displayItem.reset()
machineActionsWizard.appendPage(actions[i].displayItem, catalog.i18nc("@title", actions[i].label));
}
//Only start if there are actions to perform.
if (actions.length > 0)
{
machineActionsWizard.currentPage = 0;
show()
}
}
}
Connections

View file

@ -0,0 +1,15 @@
import QtQuick 2.2
Item
{
id: contentItem
// Connect the finished property change to completed signal.
property var finished: manager.finished
onFinishedChanged: if(manager.finished) {completed()}
signal completed()
function reset()
{
manager.reset()
}
}

View file

@ -41,6 +41,37 @@ UM.ManagementPage
anchors.fill: parent;
spacing: UM.Theme.getSize("default_margin").height;
Row
{
Repeater
{
id: machineActionRepeater
model: Cura.MachineActionManager.getSupportedActions(Cura.MachineManager.activeDefinitionId)
Button
{
text: machineActionRepeater.model[index].label;
onClicked:
{
actionDialog.content = machineActionRepeater.model[index].displayItem
machineActionRepeater.model[index].displayItem.reset()
actionDialog.show()
}
}
}
}
UM.Dialog
{
id: actionDialog
property var content
onContentChanged:
{
contents = content;
content.onCompleted.connect(hide)
}
}
Label
{
text: base.currentItem && base.currentItem.name ? base.currentItem.name : ""

View file

@ -34,34 +34,34 @@ Item {
property string tooltipText:
{
var affects = settingDefinitionsModel.getRequiredBy(definition.key, "value")
var affected_by = settingDefinitionsModel.getRequires(definition.key, "value")
var affects = settingDefinitionsModel.getRequiredBy(definition.key, "value")
var affected_by = settingDefinitionsModel.getRequires(definition.key, "value")
var affected_by_list = ""
for(var i in affected_by)
{
affected_by_list += "<li>%1</li>\n".arg(affected_by[i].label)
}
var affected_by_list = ""
for(var i in affected_by)
{
affected_by_list += "<li>%1</li>\n".arg(affected_by[i].label)
}
var affects_list = ""
for(var i in affects)
{
affects_list += "<li>%1</li>\n".arg(affects[i].label)
}
var affects_list = ""
for(var i in affects)
{
affects_list += "<li>%1</li>\n".arg(affects[i].label)
}
var tooltip = "<b>%1</b>\n<p>%2</p>".arg(definition.label).arg(definition.description)
var tooltip = "<b>%1</b>\n<p>%2</p>".arg(definition.label).arg(definition.description)
if(affects_list != "")
{
tooltip += "<br/><b>%1</b>\n<ul>\n%2</ul>".arg(catalog.i18nc("@label", "Affects")).arg(affects_list)
}
if(affects_list != "")
{
tooltip += "<br/><b>%1</b>\n<ul>\n%2</ul>".arg(catalog.i18nc("@label", "Affects")).arg(affects_list)
}
if(affected_by_list != "")
{
tooltip += "<br/><b>%1</b>\n<ul>\n%2</ul>".arg(catalog.i18nc("@label", "Affected By")).arg(affected_by_list)
}
if(affected_by_list != "")
{
tooltip += "<br/><b>%1</b>\n<ul>\n%2</ul>".arg(catalog.i18nc("@label", "Affected By")).arg(affected_by_list)
}
return tooltip
return tooltip
}
MouseArea
@ -236,7 +236,7 @@ Item {
{
id: controlContainer;
enabled: provider.isValueUsed
enabled: propertyProvider.isValueUsed
anchors.right: parent.right;
anchors.rightMargin: UM.Theme.getSize("default_margin").width

View file

@ -19,11 +19,6 @@ SettingItem
border.width: UM.Theme.getSize("default_lining").width
border.color: !enabled ? UM.Theme.getColor("setting_control_disabled_border") : hovered ? UM.Theme.getColor("setting_control_border_highlight") : UM.Theme.getColor("setting_control_border")
property variant parentValue: value //From parent loader
function notifyReset() {
input.text = format(parentValue)
}
color: {
if (!enabled)
{

View file

@ -29,8 +29,10 @@ ScrollView
model: UM.SettingDefinitionsModel {
id: definitionsModel;
containerId: Cura.MachineManager.activeDefinitionId
exclude: ["machine_settings"]
visibilityHandler: UM.SettingPreferenceVisibilityHandler { }
exclude: ["machine_settings"]
expanded: Printer.expandedCategories
onExpandedChanged: Printer.setExpandedCategories(expanded)
filter:
{

View file

@ -1,243 +0,0 @@
// Copyright (c) 2015 Ultimaker B.V.
// Cura is released under the terms of the AGPLv3 or higher.
import QtQuick 2.2
import QtQuick.Controls 1.1
import QtQuick.Window 2.1
import QtQuick.Controls.Styles 1.1
import UM 1.1 as UM
Item
{
id: base
property string activeManufacturer: "Ultimaker";
property variant wizard: null;
property bool visibility: base.wizard.visible
onVisibilityChanged:
{
machineName.text = getMachineName()
}
function getMachineName()
{
var name = machineList.model.getItem(machineList.currentIndex).name
return name
}
Connections
{
target: base.wizard
onNextClicked: //You can add functions here that get triggered when the final button is clicked in the wizard-element
{
base.wizard.resetPages()
saveMachine()
}
onBackClicked: base.wizard.resetPages()
}
Label
{
id: title
anchors.left: parent.left
anchors.top: parent.top
text: catalog.i18nc("@title", "Add Printer")
font.pointSize: 18;
}
Label
{
id: subTitle
anchors.left: parent.left
anchors.top: title.bottom
text: catalog.i18nc("@label", "Please select the type of printer:");
}
ScrollView
{
id: machinesHolder
anchors
{
left: parent.left;
top: subTitle.bottom;
right: parent.right;
bottom: machineNameHolder.top;
}
ListView
{
id: machineList
model: UM.MachineDefinitionsModel { id: machineDefinitionsModel; showVariants: false; }
focus: true
section.property: "manufacturer"
section.delegate: Button {
text: section
style: ButtonStyle {
background: Rectangle {
border.width: 0
color: "transparent";
height: UM.Theme.getSize("standard_list_lineheight").height
width: machineList.width
}
label: Label {
anchors.left: parent.left
anchors.leftMargin: UM.Theme.getSize("standard_arrow").width + UM.Theme.getSize("default_margin").width
text: control.text
color: palette.windowText
font.bold: true
UM.RecolorImage {
id: downArrow
anchors.verticalCenter: parent.verticalCenter
anchors.right: parent.left
anchors.rightMargin: UM.Theme.getSize("default_margin").width
width: UM.Theme.getSize("standard_arrow").width
height: UM.Theme.getSize("standard_arrow").height
sourceSize.width: width
sourceSize.height: width
color: palette.windowText
source: base.activeManufacturer == section ? UM.Theme.getIcon("arrow_bottom") : UM.Theme.getIcon("arrow_right")
}
}
}
onClicked: {
base.activeManufacturer = section;
machineList.currentIndex = machineList.model.find("manufacturer", section)
machineName.text = getMachineName()
}
}
delegate: RadioButton {
id: machineButton
anchors.left: parent.left
anchors.leftMargin: UM.Theme.getSize("standard_list_lineheight").width
opacity: 1;
height: UM.Theme.getSize("standard_list_lineheight").height;
checked: ListView.isCurrentItem;
exclusiveGroup: printerGroup;
text: model.name
onClicked: {
ListView.view.currentIndex = index;
machineName.text = getMachineName()
}
states: State {
name: "collapsed";
when: base.activeManufacturer != model.manufacturer;
PropertyChanges { target: machineButton; opacity: 0; height: 0; }
}
transitions: [
Transition {
to: "collapsed";
SequentialAnimation {
NumberAnimation { property: "opacity"; duration: 75; }
NumberAnimation { property: "height"; duration: 75; }
}
},
Transition {
from: "collapsed";
SequentialAnimation {
NumberAnimation { property: "height"; duration: 75; }
NumberAnimation { property: "opacity"; duration: 75; }
}
}
]
}
}
}
Column
{
id: machineNameHolder
anchors.bottom: parent.bottom;
Item
{
height: errorMessage.lineHeight
anchors.bottom: insertNameLabel.top
anchors.bottomMargin: insertNameLabel.height * errorMessage.lineCount
Label
{
id: errorMessage
property bool show: false
width: base.width
height: errorMessage.show ? errorMessage.lineHeight : 0
visible: errorMessage.show
text: catalog.i18nc("@label", "This printer name has already been used. Please choose a different printer name.");
wrapMode: Text.WordWrap
Behavior on height {NumberAnimation {duration: 75; }}
color: UM.Theme.getColor("error")
}
}
Label
{
id: insertNameLabel
text: catalog.i18nc("@label:textbox", "Printer Name:");
}
TextField
{
id: machineName;
text: getMachineName()
implicitWidth: UM.Theme.getSize("standard_list_input").width
maximumLength: 40
}
}
function saveMachine()
{
if(machineList.currentIndex != -1)
{
var item = machineList.model.getItem(machineList.currentIndex);
machineList.model.createInstance(machineName.text, item.id)
var pages = machineList.model.getItem(machineList.currentIndex).pages
// Insert new pages (if any)
for(var i = 0; i < pages.length; i++)
{
switch(pages[i]) {
case "SelectUpgradedParts":
base.wizard.appendPage(Qt.resolvedUrl("SelectUpgradedParts.qml"), catalog.i18nc("@title", "Select Upgraded Parts"));
break;
case "SelectUpgradedPartsUM2":
base.wizard.appendPage(Qt.resolvedUrl("SelectUpgradedPartsUM2.qml"), catalog.i18nc("@title", "Select Upgraded Parts"));
break;
case "UpgradeFirmware":
base.wizard.appendPage(Qt.resolvedUrl("UpgradeFirmware.qml"), catalog.i18nc("@title", "Upgrade Firmware"));
break;
case "UltimakerCheckup":
base.wizard.appendPage(Qt.resolvedUrl("UltimakerCheckup.qml"), catalog.i18nc("@title", "Check Printer"));
break;
case "BedLeveling":
base.wizard.appendPage(Qt.resolvedUrl("Bedleveling.qml"), catalog.i18nc("@title", "Bed Levelling"));
break;
default:
base.wizard.appendPage(Qt.resolvedUrl("%1.qml".arg(pages[i])), pages[i])
break;
}
}
}
}
ExclusiveGroup { id: printerGroup; }
UM.I18nCatalog { id: catalog; name: "cura"; }
SystemPalette { id: palette }
}

View file

@ -1,132 +0,0 @@
// Copyright (c) 2015 Ultimaker B.V.
// Cura is released under the terms of the AGPLv3 or higher.
import QtQuick 2.2
import QtQuick.Controls 1.1
import QtQuick.Layouts 1.1
import QtQuick.Window 2.1
import UM 1.1 as UM
import Cura 1.0 as Cura
import ".."
Item
{
id: wizardPage
property int leveling_state: 0
property bool three_point_leveling: true
property int platform_width: UM.MachineManager.getSettingValue("machine_width")
property int platform_height: UM.MachineManager.getSettingValue("machine_depth")
anchors.fill: parent;
property variant printer_connection: Cura.USBPrinterManager.connectedPrinterList.getItem(0).printer
Component.onCompleted:
{
printer_connection.homeBed()
printer_connection.moveHead(0, 0, 3)
printer_connection.homeHead()
}
UM.I18nCatalog { id: catalog; name:"cura"}
property variant wizard: null;
Label
{
id: pageTitle
width: parent.width
text: catalog.i18nc("@title", "Bed Leveling")
wrapMode: Text.WordWrap
font.pointSize: 18;
}
Label
{
id: pageDescription
anchors.top: pageTitle.bottom
anchors.topMargin: UM.Theme.getSize("default_margin").height
width: parent.width
wrapMode: Text.WordWrap
text: catalog.i18nc("@label","To make sure your prints will come out great, you can now adjust your buildplate. When you click 'Move to Next Position' the nozzle will move to the different positions that can be adjusted.")
}
Label
{
id: bedlevelingText
anchors.top: pageDescription.bottom
anchors.topMargin: UM.Theme.getSize("default_margin").height
width: parent.width
wrapMode: Text.WordWrap
text: catalog.i18nc("@label", "For every postition; insert a piece of paper under the nozzle and adjust the print bed height. The print bed height is right when the paper is slightly gripped by the tip of the nozzle.")
}
Item{
id: bedlevelingWrapper
anchors.top: bedlevelingText.bottom
anchors.topMargin: UM.Theme.getSize("default_margin").height
anchors.horizontalCenter: parent.horizontalCenter
height: skipBedlevelingButton.height
width: bedlevelingButton.width + skipBedlevelingButton.width + UM.Theme.getSize("default_margin").height < wizardPage.width ? bedlevelingButton.width + skipBedlevelingButton.width + UM.Theme.getSize("default_margin").height : wizardPage.width
Button
{
id: bedlevelingButton
anchors.top: parent.top
anchors.left: parent.left
text: catalog.i18nc("@action:button","Move to Next Position");
onClicked:
{
if(wizardPage.leveling_state == 0)
{
printer_connection.moveHead(0, 0, 3)
printer_connection.homeHead()
printer_connection.moveHead(0, 0, 3)
printer_connection.moveHead(platform_width - 10, 0, 0)
printer_connection.moveHead(0, 0, -3)
}
if(wizardPage.leveling_state == 1)
{
printer_connection.moveHead(0, 0, 3)
printer_connection.moveHead(-platform_width/2, platform_height - 10, 0)
printer_connection.moveHead(0, 0, -3)
}
if(wizardPage.leveling_state == 2)
{
printer_connection.moveHead(0, 0, 3)
printer_connection.moveHead(-platform_width/2 + 10, -(platform_height + 10), 0)
printer_connection.moveHead(0, 0, -3)
}
wizardPage.leveling_state++
if (wizardPage.leveling_state >= 3){
resultText.visible = true
skipBedlevelingButton.enabled = false
bedlevelingButton.enabled = false
wizardPage.leveling_state = 0
}
}
}
Button
{
id: skipBedlevelingButton
anchors.top: parent.width < wizardPage.width ? parent.top : bedlevelingButton.bottom
anchors.topMargin: parent.width < wizardPage.width ? 0 : UM.Theme.getSize("default_margin").height/2
anchors.left: parent.width < wizardPage.width ? bedlevelingButton.right : parent.left
anchors.leftMargin: parent.width < wizardPage.width ? UM.Theme.getSize("default_margin").width : 0
text: catalog.i18nc("@action:button","Skip Bedleveling");
onClicked: base.nextPage()
}
}
Label
{
id: resultText
visible: false
anchors.top: bedlevelingWrapper.bottom
anchors.topMargin: UM.Theme.getSize("default_margin").height
anchors.left: parent.left
width: parent.width
wrapMode: Text.WordWrap
text: catalog.i18nc("@label", "Everything is in order! You're done with bedleveling.")
}
function threePointLeveling(width, height)
{
}
}

View file

@ -1,85 +0,0 @@
// Copyright (c) 2015 Ultimaker B.V.
// Cura is released under the terms of the AGPLv3 or higher.
import QtQuick 2.2
import QtQuick.Controls 1.1
import QtQuick.Window 2.1
import UM 1.1 as UM
Item
{
id: wizardPage
property string title
SystemPalette{id: palette}
UM.I18nCatalog { id: catalog; name:"cura"}
Component.onDestruction:
{
if (heatedBedCheckBox1.checked == true || heatedBedCheckBox2.checked == true){
UM.MachineManager.setMachineSettingValue("machine_heated_bed", true)
}
}
Label
{
id: pageTitle
width: parent.width
text: catalog.i18nc("@title", "Select Upgraded Parts")
wrapMode: Text.WordWrap
font.pointSize: 18
}
Label
{
id: pageDescription
anchors.top: pageTitle.bottom
anchors.topMargin: UM.Theme.getSize("default_margin").height
width: parent.width
wrapMode: Text.WordWrap
text: catalog.i18nc("@label","To assist you in having better default settings for your Ultimaker. Cura would like to know which upgrades you have in your machine:")
}
Item
{
id: pageCheckboxes
height: childrenRect.height
anchors.left: parent.left
anchors.leftMargin: UM.Theme.getSize("default_margin").width
anchors.top: pageDescription.bottom
anchors.topMargin: UM.Theme.getSize("default_margin").height
width: parent.width - UM.Theme.getSize("default_margin").width
CheckBox
{
id: heatedBedCheckBox1
text: catalog.i18nc("@option:check","Heated printer bed")
y: extruderCheckBox.height * 1
checked: false
onClicked: {
if (heatedBedCheckBox2.checked == true)
heatedBedCheckBox2.checked = false
}
}
CheckBox
{
id: heatedBedCheckBox2
text: catalog.i18nc("@option:check","Heated printer bed (self built)")
y: extruderCheckBox.height * 2
checked: false
onClicked: {
if (heatedBedCheckBox1.checked == true)
heatedBedCheckBox1.checked = false
}
}
}
Label
{
width: parent.width
anchors.top: pageCheckboxes.bottom
anchors.topMargin: UM.Theme.getSize("default_margin").height
wrapMode: Text.WordWrap
text: catalog.i18nc("@label","If you bought your Ultimaker after october 2012 you will have the Extruder drive upgrade. If you do not have this upgrade, it is highly recommended to improve reliability. This upgrade can be bought from the Ultimaker webshop or found on thingiverse as thing:26094");
}
ExclusiveGroup { id: printerGroup; }
}

View file

@ -1,378 +0,0 @@
// Copyright (c) 2015 Ultimaker B.V.
// Cura is released under the terms of the AGPLv3 or higher.
import QtQuick 2.2
import QtQuick.Controls 1.1
import QtQuick.Window 2.1
import UM 1.1 as UM
Item
{
id: wizardPage
property string title
property int leftRow: wizardPage.width*0.40
property int rightRow: wizardPage.width*0.60
anchors.fill: parent;
property bool x_min_pressed: false
property bool y_min_pressed: false
property bool z_min_pressed: false
property bool heater_works: false
property int extruder_target_temp: 0
property int bed_target_temp: 0
UM.I18nCatalog { id: catalog; name:"cura"}
property var checkupProgress: {
"connection": false,
"endstopX": wizardPage.x_min_pressed,
"endstopY": wizardPage.y_min_pressed,
"endstopZ": wizardPage.z_min_pressed,
"nozzleTemp": false,
"bedTemp": false
}
property variant printer_connection: {
if (Cura.USBPrinterManager.connectedPrinterList.rowCount() != 0){
wizardPage.checkupProgress.connection = true
return Cura.USBPrinterManager.connectedPrinterList.getItem(0).printer
}
else {
return null
}
}
function checkTotalCheckUp(){
var allDone = true
for(var property in checkupProgress){
if (checkupProgress[property] == false){
allDone = false
}
}
if (allDone == true){
skipCheckButton.enabled = false
resultText.visible = true
}
}
Component.onCompleted:
{
if (printer_connection != null){
printer_connection.startPollEndstop()
}
}
Component.onDestruction:
{
if (printer_connection != null){
printer_connection.stopPollEndstop()
}
}
Label
{
id: pageTitle
width: parent.width
text: catalog.i18nc("@title", "Check Printer")
wrapMode: Text.WordWrap
font.pointSize: 18;
}
Label
{
id: pageDescription
anchors.top: pageTitle.bottom
anchors.topMargin: UM.Theme.getSize("default_margin").height
width: parent.width
wrapMode: Text.WordWrap
text: catalog.i18nc("@label","It's a good idea to do a few sanity checks on your Ultimaker. You can skip this step if you know your machine is functional");
}
Item{
id: startStopButtons
anchors.top: pageDescription.bottom
anchors.topMargin: UM.Theme.getSize("default_margin").height
anchors.horizontalCenter: parent.horizontalCenter
height: childrenRect.height
width: startCheckButton.width + skipCheckButton.width + UM.Theme.getSize("default_margin").height < wizardPage.width ? startCheckButton.width + skipCheckButton.width + UM.Theme.getSize("default_margin").height : wizardPage.width
Button
{
id: startCheckButton
anchors.top: parent.top
anchors.left: parent.left
//enabled: !alreadyTested
text: catalog.i18nc("@action:button","Start Printer Check");
onClicked: {
checkupContent.visible = true
startCheckButton.enabled = false
printer_connection.homeHead()
}
}
Button
{
id: skipCheckButton
anchors.top: parent.width < wizardPage.width ? parent.top : startCheckButton.bottom
anchors.topMargin: parent.width < wizardPage.width ? 0 : UM.Theme.getSize("default_margin").height/2
anchors.left: parent.width < wizardPage.width ? startCheckButton.right : parent.left
anchors.leftMargin: parent.width < wizardPage.width ? UM.Theme.getSize("default_margin").width : 0
//enabled: !alreadyTested
text: catalog.i18nc("@action:button","Skip Printer Check");
onClicked: base.nextPage()
}
}
Item{
id: checkupContent
anchors.top: startStopButtons.bottom
anchors.topMargin: UM.Theme.getSize("default_margin").height
visible: false
//////////////////////////////////////////////////////////
Label
{
id: connectionLabel
width: wizardPage.leftRow
anchors.left: parent.left
anchors.top: parent.top
wrapMode: Text.WordWrap
text: catalog.i18nc("@label","Connection: ")
}
Label
{
id: connectionStatus
width: wizardPage.rightRow
anchors.left: connectionLabel.right
anchors.top: parent.top
wrapMode: Text.WordWrap
text: Cura.USBPrinterManager.connectedPrinterList.rowCount() > 0 || base.addOriginalProgress.checkUp[0] ? catalog.i18nc("@info:status","Done"):catalog.i18nc("@info:status","Incomplete")
}
//////////////////////////////////////////////////////////
Label
{
id: endstopXLabel
width: wizardPage.leftRow
anchors.left: parent.left
anchors.top: connectionLabel.bottom
wrapMode: Text.WordWrap
text: catalog.i18nc("@label","Min endstop X: ")
}
Label
{
id: endstopXStatus
width: wizardPage.rightRow
anchors.left: endstopXLabel.right
anchors.top: connectionLabel.bottom
wrapMode: Text.WordWrap
text: x_min_pressed ? catalog.i18nc("@info:status","Works") : catalog.i18nc("@info:status","Not checked")
}
//////////////////////////////////////////////////////////////
Label
{
id: endstopYLabel
width: wizardPage.leftRow
anchors.left: parent.left
anchors.top: endstopXLabel.bottom
wrapMode: Text.WordWrap
text: catalog.i18nc("@label","Min endstop Y: ")
}
Label
{
id: endstopYStatus
width: wizardPage.rightRow
anchors.left: endstopYLabel.right
anchors.top: endstopXLabel.bottom
wrapMode: Text.WordWrap
text: y_min_pressed ? catalog.i18nc("@info:status","Works") : catalog.i18nc("@info:status","Not checked")
}
/////////////////////////////////////////////////////////////////////
Label
{
id: endstopZLabel
width: wizardPage.leftRow
anchors.left: parent.left
anchors.top: endstopYLabel.bottom
wrapMode: Text.WordWrap
text: catalog.i18nc("@label","Min endstop Z: ")
}
Label
{
id: endstopZStatus
width: wizardPage.rightRow
anchors.left: endstopZLabel.right
anchors.top: endstopYLabel.bottom
wrapMode: Text.WordWrap
text: z_min_pressed ? catalog.i18nc("@info:status","Works") : catalog.i18nc("@info:status","Not checked")
}
////////////////////////////////////////////////////////////
Label
{
id: nozzleTempLabel
width: wizardPage.leftRow
anchors.left: parent.left
anchors.top: endstopZLabel.bottom
wrapMode: Text.WordWrap
text: catalog.i18nc("@label","Nozzle temperature check: ")
}
Label
{
id: nozzleTempStatus
width: wizardPage.rightRow * 0.4
anchors.top: nozzleTempLabel.top
anchors.left: nozzleTempLabel.right
wrapMode: Text.WordWrap
text: catalog.i18nc("@info:status","Not checked")
}
Item
{
id: nozzleTempButton
width: wizardPage.rightRow * 0.3
height: nozzleTemp.height
anchors.top: nozzleTempLabel.top
anchors.left: bedTempStatus.right
anchors.leftMargin: UM.Theme.getSize("default_margin").width/2
Button
{
height: nozzleTemp.height - 2
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
text: catalog.i18nc("@action:button","Start Heating")
onClicked:
{
if(printer_connection != null)
{
nozzleTempStatus.text = catalog.i18nc("@info:progress","Checking")
printer_connection.setTargetHotendTemperature(0, 190)
wizardPage.extruder_target_temp = 190
}
}
}
}
Label
{
id: nozzleTemp
anchors.top: nozzleTempLabel.top
anchors.left: nozzleTempButton.right
anchors.leftMargin: UM.Theme.getSize("default_margin").width
width: wizardPage.rightRow * 0.2
wrapMode: Text.WordWrap
text: printer_connection != null ? printer_connection.hotendTemperatures[0] + "°C" : "0°C"
font.bold: true
}
/////////////////////////////////////////////////////////////////////////////
Label
{
id: bedTempLabel
width: wizardPage.leftRow
anchors.left: parent.left
anchors.top: nozzleTempLabel.bottom
wrapMode: Text.WordWrap
text: catalog.i18nc("@label","bed temperature check:")
}
Label
{
id: bedTempStatus
width: wizardPage.rightRow * 0.4
anchors.top: bedTempLabel.top
anchors.left: bedTempLabel.right
wrapMode: Text.WordWrap
text: catalog.i18nc("@info:status","Not checked")
}
Item
{
id: bedTempButton
width: wizardPage.rightRow * 0.3
height: bedTemp.height
anchors.top: bedTempLabel.top
anchors.left: bedTempStatus.right
anchors.leftMargin: UM.Theme.getSize("default_margin").width/2
Button
{
height: bedTemp.height - 2
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
text: catalog.i18nc("@action:button","Start Heating")
onClicked:
{
if(printer_connection != null)
{
bedTempStatus.text = catalog.i18nc("@info:progress","Checking")
printer_connection.setTargetBedTemperature(60)
wizardPage.bed_target_temp = 60
}
}
}
}
Label
{
id: bedTemp
width: wizardPage.rightRow * 0.2
anchors.top: bedTempLabel.top
anchors.left: bedTempButton.right
anchors.leftMargin: UM.Theme.getSize("default_margin").width
wrapMode: Text.WordWrap
text: printer_connection != null ? printer_connection.bedTemperature + "°C": "0°C"
font.bold: true
}
Label
{
id: resultText
visible: false
anchors.top: bedTemp.bottom
anchors.topMargin: UM.Theme.getSize("default_margin").height
anchors.left: parent.left
width: parent.width
wrapMode: Text.WordWrap
text: catalog.i18nc("@label", "Everything is in order! You're done with your CheckUp.")
}
}
Connections
{
target: printer_connection
onEndstopStateChanged:
{
if(key == "x_min")
{
x_min_pressed = true
checkTotalCheckUp()
}
if(key == "y_min")
{
y_min_pressed = true
checkTotalCheckUp()
}
if(key == "z_min")
{
z_min_pressed = true
checkTotalCheckUp()
}
}
onHotendTemperaturesChanged:
{
if(printer_connection.hotendTemperatures[0] > wizardPage.extruder_target_temp - 10 && printer_connection.hotendTemperatures[0] < wizardPage.extruder_target_temp + 10)
{
if(printer_connection != null)
{
nozzleTempStatus.text = catalog.i18nc("@info:status","Works")
wizardPage.checkupProgress.nozzleTemp = true
checkTotalCheckUp()
printer_connection.setTargetHotendTemperature(0, 0)
}
}
}
onBedTemperatureChanged:
{
if(printer_connection.bedTemperature > wizardPage.bed_target_temp - 5 && printer_connection.bedTemperature < wizardPage.bed_target_temp + 5)
{
bedTempStatus.text = catalog.i18nc("@info:status","Works")
wizardPage.checkupProgress.bedTemp = true
checkTotalCheckUp()
printer_connection.setTargetBedTemperature(0)
}
}
}
ExclusiveGroup
{
id: printerGroup;
}
}

View file

@ -1,78 +0,0 @@
// Copyright (c) 2015 Ultimaker B.V.
// Cura is released under the terms of the AGPLv3 or higher.
import QtQuick 2.2
import QtQuick.Controls 1.1
import QtQuick.Window 2.1
import UM 1.1 as UM
Item
{
id: wizardPage
property string title
SystemPalette{id: palette}
UM.I18nCatalog { id: catalog; name:"cura"}
property variant printer_connection: Cura.USBPrinterManager.connectedPrinterList.rowCount() != 0 ? Cura.USBPrinterManager.connectedPrinterList.getItem(0).printer : null
Label
{
id: pageTitle
width: parent.width
text: catalog.i18nc("@title", "Upgrade Firmware")
wrapMode: Text.WordWrap
font.pointSize: 18
}
Label
{
id: pageDescription
anchors.top: pageTitle.bottom
anchors.topMargin: UM.Theme.getSize("default_margin").height
width: parent.width
wrapMode: Text.WordWrap
text: catalog.i18nc("@label","Firmware is the piece of software running directly on your 3D printer. This firmware controls the step motors, regulates the temperature and ultimately makes your printer work.")
}
Label
{
id: upgradeText1
anchors.top: pageDescription.bottom
anchors.topMargin: UM.Theme.getSize("default_margin").height
width: parent.width
wrapMode: Text.WordWrap
text: catalog.i18nc("@label","The firmware shipping with new Ultimakers works, but upgrades have been made to make better prints, and make calibration easier.");
}
Label
{
id: upgradeText2
anchors.top: upgradeText1.bottom
anchors.topMargin: UM.Theme.getSize("default_margin").height
width: parent.width
wrapMode: Text.WordWrap
text: catalog.i18nc("@label","Cura requires these new features and thus your firmware will most likely need to be upgraded. You can do so now.");
}
Item{
anchors.top: upgradeText2.bottom
anchors.topMargin: UM.Theme.getSize("default_margin").height
anchors.horizontalCenter: parent.horizontalCenter
width: upgradeButton.width + skipUpgradeButton.width + UM.Theme.getSize("default_margin").height < wizardPage.width ? upgradeButton.width + skipUpgradeButton.width + UM.Theme.getSize("default_margin").height : wizardPage.width
Button {
id: upgradeButton
anchors.top: parent.top
anchors.left: parent.left
text: catalog.i18nc("@action:button","Upgrade to Marlin Firmware");
onClicked: Cura.USBPrinterManager.updateAllFirmware()
}
Button {
id: skipUpgradeButton
anchors.top: parent.width < wizardPage.width ? parent.top : upgradeButton.bottom
anchors.topMargin: parent.width < wizardPage.width ? 0 : UM.Theme.getSize("default_margin").height/2
anchors.left: parent.width < wizardPage.width ? upgradeButton.right : parent.left
anchors.leftMargin: parent.width < wizardPage.width ? UM.Theme.getSize("default_margin").width : 0
text: catalog.i18nc("@action:button","Skip Upgrade");
onClicked: base.nextPage()
}
}
ExclusiveGroup { id: printerGroup; }
}

View file

@ -0,0 +1,77 @@
#Todo: Write tests
import pytest
from cura.MachineAction import MachineAction
from cura.MachineActionManager import MachineActionManager, NotUniqueMachineActionError, UnknownMachineActionError
class Machine:
def __init__(self, key = ""):
self._key = key
def getKey(self):
return self._key
def test_addMachineAction():
machine_manager = MachineActionManager()
test_action = MachineAction(key = "test_action")
test_action_2 = MachineAction(key = "test_action_2")
test_machine = Machine("test_machine")
machine_manager.addMachineAction(test_action)
machine_manager.addMachineAction(test_action_2)
assert machine_manager.getMachineAction("test_action") == test_action
assert machine_manager.getMachineAction("key_that_doesnt_exist") is None
# Adding the same machine action is not allowed.
with pytest.raises(NotUniqueMachineActionError):
machine_manager.addMachineAction(test_action)
# Check that the machine has no supported actions yet.
assert machine_manager.getSupportedActions(test_machine) == set()
# Check if adding a supported action works.
machine_manager.addSupportedAction(test_machine, "test_action")
assert machine_manager.getSupportedActions(test_machine) == {test_action}
# Check that adding a unknown action doesn't change anything.
machine_manager.addSupportedAction(test_machine, "key_that_doesnt_exist")
assert machine_manager.getSupportedActions(test_machine) == {test_action}
# Check if adding multiple supported actions works.
machine_manager.addSupportedAction(test_machine, "test_action_2")
assert machine_manager.getSupportedActions(test_machine) == {test_action, test_action_2}
# Check that the machine has no required actions yet.
assert machine_manager.getRequiredActions(test_machine) == set()
## Ensure that only known actions can be added.
with pytest.raises(UnknownMachineActionError):
machine_manager.addRequiredAction(test_machine, "key_that_doesnt_exist")
## Check if adding single required action works
machine_manager.addRequiredAction(test_machine, "test_action")
assert machine_manager.getRequiredActions(test_machine) == {test_action}
# Check if adding multiple required actions works.
machine_manager.addRequiredAction(test_machine, "test_action_2")
assert machine_manager.getRequiredActions(test_machine) == {test_action, test_action_2}
# Ensure that firstStart actions are empty by default.
assert machine_manager.getFirstStartActions(test_machine) == []
# Check if adding multiple (the same) actions to first start actions work.
machine_manager.addFirstStartAction(test_machine, "test_action")
machine_manager.addFirstStartAction(test_machine, "test_action")
assert machine_manager.getFirstStartActions(test_machine) == [test_action, test_action]
# Check if inserting an action works
machine_manager.addFirstStartAction(test_machine, "test_action_2", index = 1)
assert machine_manager.getFirstStartActions(test_machine) == [test_action, test_action_2, test_action]
# Check that adding a unknown action doesn't change anything.
machine_manager.addFirstStartAction(test_machine, "key_that_doesnt_exist", index = 1)
assert machine_manager.getFirstStartActions(test_machine) == [test_action, test_action_2, test_action]