Merge pull request #819 from Ultimaker/MachineActions

Machine actions
This commit is contained in:
Aldo Hoeben 2016-06-23 12:59:03 +02:00 committed by GitHub
commit 488740c60d
29 changed files with 1078 additions and 932 deletions

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