artiq/artiq/dashboard/applets_ccb.py

274 lines
11 KiB
Python

import asyncio
import logging
from PyQt5 import QtCore, QtWidgets
from artiq.gui import applets
logger = logging.getLogger(__name__)
class AppletsCCBDock(applets.AppletsDock):
def __init__(self, *args, **kwargs):
applets.AppletsDock.__init__(self, *args, **kwargs)
sep = QtWidgets.QAction(self.table)
sep.setSeparator(True)
self.table.addAction(sep)
ccbp_group_menu = QtWidgets.QMenu()
actiongroup = QtWidgets.QActionGroup(self.table)
actiongroup.setExclusive(True)
self.ccbp_group_none = QtWidgets.QAction("No policy", self.table)
self.ccbp_group_none.setCheckable(True)
self.ccbp_group_none.triggered.connect(lambda: self.set_ccbp(""))
ccbp_group_menu.addAction(self.ccbp_group_none)
actiongroup.addAction(self.ccbp_group_none)
self.ccbp_group_ignore = QtWidgets.QAction("Ignore requests", self.table)
self.ccbp_group_ignore.setCheckable(True)
self.ccbp_group_ignore.triggered.connect(lambda: self.set_ccbp("ignore"))
ccbp_group_menu.addAction(self.ccbp_group_ignore)
actiongroup.addAction(self.ccbp_group_ignore)
self.ccbp_group_create = QtWidgets.QAction("Create applets", self.table)
self.ccbp_group_create.setCheckable(True)
self.ccbp_group_create.triggered.connect(lambda: self.set_ccbp("create"))
ccbp_group_menu.addAction(self.ccbp_group_create)
actiongroup.addAction(self.ccbp_group_create)
self.ccbp_group_enable = QtWidgets.QAction("Create and enable/disable applets",
self.table)
self.ccbp_group_enable.setCheckable(True)
self.ccbp_group_enable.triggered.connect(lambda: self.set_ccbp("enable"))
ccbp_group_menu.addAction(self.ccbp_group_enable)
actiongroup.addAction(self.ccbp_group_enable)
self.ccbp_group_action = QtWidgets.QAction("Group CCB policy", self.table)
self.ccbp_group_action.setMenu(ccbp_group_menu)
self.table.addAction(self.ccbp_group_action)
self.table.itemSelectionChanged.connect(self.update_group_ccbp_menu)
self.update_group_ccbp_menu()
ccbp_global_menu = QtWidgets.QMenu()
actiongroup = QtWidgets.QActionGroup(self.table)
actiongroup.setExclusive(True)
self.ccbp_global_ignore = QtWidgets.QAction("Ignore requests", self.table)
self.ccbp_global_ignore.setCheckable(True)
ccbp_global_menu.addAction(self.ccbp_global_ignore)
actiongroup.addAction(self.ccbp_global_ignore)
self.ccbp_global_create = QtWidgets.QAction("Create applets", self.table)
self.ccbp_global_create.setCheckable(True)
self.ccbp_global_create.setChecked(True)
ccbp_global_menu.addAction(self.ccbp_global_create)
actiongroup.addAction(self.ccbp_global_create)
self.ccbp_global_enable = QtWidgets.QAction("Create and enable/disable applets",
self.table)
self.ccbp_global_enable.setCheckable(True)
ccbp_global_menu.addAction(self.ccbp_global_enable)
actiongroup.addAction(self.ccbp_global_enable)
ccbp_global_action = QtWidgets.QAction("Global CCB policy", self.table)
ccbp_global_action.setMenu(ccbp_global_menu)
self.table.addAction(ccbp_global_action)
def update_group_ccbp_menu(self):
selection = self.table.selectedItems()
if selection:
item = selection[0]
if item.ty == "applet":
item = item.parent()
if item is None:
self.ccbp_group_action.setEnabled(False)
else:
self.ccbp_group_action.setEnabled(True)
ccbp = item.text(1)
if ccbp == "":
self.ccbp_group_none.setChecked(True)
else:
getattr(self, "ccbp_group_" + ccbp).setChecked(True)
else:
self.ccbp_group_action.setEnabled(False)
def set_ccbp(self, ccbp):
item = self.table.selectedItems()[0]
if item.ty == "applet":
item = item.parent()
item.setText(1, ccbp)
def get_ccpb_global(self):
if self.ccbp_global_ignore.isChecked():
return "ignore"
if self.ccbp_global_create.isChecked():
return "create"
if self.ccbp_global_enable.isChecked():
return "enable"
def get_ccpb(self, group):
ccbp = self.get_ccpb_global()
parent = self.table.invisibleRootItem()
for g in group:
new_parent = None
for i in range(parent.childCount()):
child = parent.child(i)
if child.ty == "group" and child.text(0) == g:
c_ccbp = child.text(1)
if c_ccbp:
ccbp = c_ccbp
new_parent = child
break
if new_parent is None:
return ccbp
else:
parent = new_parent
return ccbp
def locate_applet(self, name, group, create_groups):
parent = self.table.invisibleRootItem()
for g in group:
new_parent = None
for i in range(parent.childCount()):
child = parent.child(i)
if child.ty == "group" and child.text(0) == g:
new_parent = child
break
if new_parent is None:
if create_groups:
new_parent = self.new_group(g, parent=parent)
else:
return None, None
parent = new_parent
applet = None
for i in range(parent.childCount()):
child = parent.child(i)
if child.ty == "applet" and child.text(0) == name:
applet = child
break
return parent, applet
def ccb_create_applet(self, name, command, group=None, code=None):
"""Requests the creation of a new applet.
An applet is identified by its name and an optional list of groups that
represent a path (nested groups). If ``group`` is a string, it
corresponds to a single group. If ``group`` is ``None`` or an empty
list, it corresponds to the root.
``command`` gives the command line used to run the applet, as if it was
started from a shell. The dashboard substitutes variables such as
``$python`` that gives the complete file name of the Python interpreter
running the dashboard.
If the name already exists (after following any specified groups), the
command or code of the existing applet with that name is replaced, and
the applet is restarted and shown at its previous position. If not, a
new applet entry is created and the applet is shown at any position on
the screen.
If the group(s) do not exist, they are created.
If ``code`` is not ``None``, it should be a string that contains the
full source code of the applet. In this case, ``command`` is used to
specify (optional) command-line arguments to the applet.
This function is called when a CCB ``create_applet`` is issued.
"""
if group is None:
group = []
elif isinstance(group, str):
group = [group]
ccbp = self.get_ccpb(group)
if ccbp == "ignore":
return
parent, applet = self.locate_applet(name, group, True)
if code is None:
spec = {"ty": "command", "command": command}
else:
spec = {"ty": "code", "code": code, "command": command}
if applet is None:
logger.debug("Applet %s does not exist: creating", name)
applet = self.new(name=name, spec=spec, parent=parent)
else:
if spec != self.get_spec(applet):
logger.debug("Applet %s already exists: updating existing spec", name)
self.set_spec(applet, spec)
if applet.applet_dock:
asyncio.ensure_future(applet.applet_dock.restart())
else:
logger.debug("Applet %s already exists and no update required", name)
if ccbp == "enable":
applet.setCheckState(0, QtCore.Qt.Checked)
def ccb_disable_applet(self, name, group=None):
"""Disables an applet.
The applet is identified by its name, after following any specified
groups.
This function is called when a CCB ``disable_applet`` is issued.
"""
if group is None:
group = []
elif isinstance(group, str):
group = [group]
ccbp = self.get_ccpb(group)
if ccbp != "enable":
return
parent, applet = self.locate_applet(name, group, False)
if applet is not None:
applet.setCheckState(0, QtCore.Qt.Unchecked)
def ccb_disable_applet_group(self, group):
"""Disables all the applets in a group.
If the group is nested, ``group`` should be a list, with the names
of the parents preceding the name of the group to disable.
This function is called when a CCB ``disable_applet_group`` is issued.
"""
if isinstance(group, str):
group = [group]
ccbp = self.get_ccpb(group)
if ccbp != "enable":
return
if not group:
return
wi = self.table.invisibleRootItem()
for g in group:
nwi = None
for i in range(wi.childCount()):
child = wi.child(i)
if child.ty == "group" and child.text(0) == g:
nwi = child
break
if nwi is None:
return
else:
wi = nwi
wi.setCheckState(0, QtCore.Qt.Unchecked)
def ccb_notify(self, message):
try:
service = message["service"]
args = message["args"]
kwargs = message["kwargs"]
if service == "create_applet":
self.ccb_create_applet(*args, **kwargs)
elif service == "disable_applet":
self.ccb_disable_applet(*args, **kwargs)
elif service == "disable_applet_group":
self.ccb_disable_applet_group(*args, **kwargs)
except:
logger.error("failed to process CCB", exc_info=True)
def save_state(self):
return {
"applets": applets.AppletsDock.save_state(self),
"ccbp_global": self.get_ccpb_global()
}
def restore_state(self, state):
applets.AppletsDock.restore_state(self, state["applets"])
getattr(self, "ccbp_global_" + state["ccbp_global"]).setChecked(True)