diff --git a/README.md b/README.md index 6e53e73..303d87a 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,2 @@ -# LUCID +# Lucid **L**CLS **U**ser **C**ontrol and **I**nterface **D**esign diff --git a/docs/source/_static/lucid_grid.png b/docs/source/_static/lucid_grid.png new file mode 100644 index 0000000..a915f7f Binary files /dev/null and b/docs/source/_static/lucid_grid.png differ diff --git a/docs/source/_static/lucid_toolbar.png b/docs/source/_static/lucid_toolbar.png new file mode 100644 index 0000000..132a31f Binary files /dev/null and b/docs/source/_static/lucid_toolbar.png differ diff --git a/docs/source/grid.rst b/docs/source/grid.rst new file mode 100644 index 0000000..34488cf --- /dev/null +++ b/docs/source/grid.rst @@ -0,0 +1,88 @@ +Lucid Grid Configuration +======================== + +``lucid``'s main grid area is an auto-generated series of buttons and indicators, +where each indicator represents a device +loaded from a ``happi`` database. + +.. figure:: /_static/lucid_grid.png + :scale: 50 % + :alt: Lucid display with the grid highlighted. + + +Which Devices are Included? +--------------------------- + +When ``lucid`` is invoked, the only required argument is the positional ``beamline`` argument. +This argument is used as a ``happi`` search term to fill the grid. + +This is equivalent to the following search using the ``happi`` command line, +assuming your beamline is named BEAMLINE: + +.. code-block:: bash + + happi search beamline=BEAMLINE active=True + + +All devices that match this search and load without errors will be included in the grid. +All other devices will be excluded. +The search is case-sensitive. + +Note that inactive devices are not included, +and that this only targets happi entries that have a beamline associated with them at all. +This requires the ``happi`` containers defined in ``pcdsdevices``, +which define the ``beamline`` field. + +Note: if you prefer not to load any devices, you can skip this step by passing +a command-line parameter: + +.. code-block:: bash + + lucid --skip_happi MY_BEAMLINE + + +How are the Devices Arranged in the Grid? +----------------------------------------- + +The grid is defined by the various metadata values found in the ``happi`` database. +The default metadata keys in use are: + +- ``location_group``, to select which column to put a device into +- ``functional_group``, to select with row to put a device into + +That is to say, by default we expect the row headers on the left to be functional names like +"imager" or "vacuum" and column headers on the top to be location names like "Section 01" or "Section 02". + +You can change which metadata keys are used by passing the +``--row_group_key`` and ``--col_group_key`` command-line parameters +to select the row grouping and column groupings respectively. + +Each unique string (case-sensitive) found in the group key metadata for the +active devices will create an additional row or column in the grid +with the corresponding header text. +The rows and the columns will each be sorted in alphabetical order. + +Each device indicator will be added to a single square cell in the grid that +corresponds with its metadata sorting. + +This provides the following features: + +- An alarm indicator that represents the device's worst alarm state, + with mouseover text that will show more precise information +- The option to click the cell and select the device name to open the device's + ``typhos`` screen in the right-hand dock area +- The option to click the row or column header to select the device name for the + same purpose as above, where the devices can be from any cell in the corresponding + row or column. +- The ability to search for devices within the grid using the search bar, + which will highlight their indicators in the grid view. + + +Limitations +----------- + +- There are currently no configuration options for row/column ordering or + customization of the happi search used to accumulate devices. +- The only way to adjust the grid is to carefully manipulate the ``happi`` database. +- There are no options for changing the sizes of cells or the sizes of the alarm + indicators held inside the cells. diff --git a/docs/source/index.rst b/docs/source/index.rst index 034edcb..dbc5c3e 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -1,4 +1,4 @@ -LUCID +Lucid ===== LCLS User Control and Interface Design @@ -8,6 +8,8 @@ LCLS User Control and Interface Design :caption: User Documentation launcher.rst + grid.rst + toolbar.rst .. toctree:: :maxdepth: 2 diff --git a/docs/source/launcher.rst b/docs/source/launcher.rst index 2ab3761..24b398a 100644 --- a/docs/source/launcher.rst +++ b/docs/source/launcher.rst @@ -1,4 +1,4 @@ -LUCID Launcher Options +Lucid Launcher Options ====================== .. argparse:: diff --git a/docs/source/toolbar.rst b/docs/source/toolbar.rst new file mode 100644 index 0000000..0477057 --- /dev/null +++ b/docs/source/toolbar.rst @@ -0,0 +1,110 @@ +Lucid Toolbar Configuration +=========================== + +``lucid`` can be launched with a toolbar file that specifies +the tabs that are displayed below the device grid. +These tabs can be configured to hold buttons +that open other screens. + +.. figure:: /_static/lucid_toolbar.png + :scale: 50 % + :alt: Lucid display with the toolbar highlighted. + +Invocation +---------- + +The invocation to include this toolbar file is: + +.. code-block:: bash + + lucid --toolbar /path/to/toolbar/file.yaml HUTCHNAME + +Options and Limitations +----------------------- + +- The user can specify an arbitrary number of tabs. +- Each tab can have an arbitrary number of columns. +- The columns are filled from left to right, then from top to bottom. +- Only ``PyDMShellCommand`` and ``PyDMRelatedDisplayButton`` widgets are supported. +- Any available pyqt property or general python attribute can be specified on either + of these button types. + +Guidelines +---------- + +- The related display option (``display``) should be used whenever it is possible to do so, + because the screen will open more quickly compared to a shell script, which will need to + initialize an entire new python shell with new imports. + +File Format +----------- + +The toolbar configuration is stored in a yaml file that encodes a dictionary. +Each key in this top-level dictionary is the name of a tab to include. +These tabs will be arranged in file order from left to right. + +Each of these tab names should contain another dictionary with the following top-level keys: + +- ``config`` (optional) +- ``buttons`` + +The only ``config`` option right now is ``cols``, which defaults to 4. + +Here's an example config file structure, omitting only the ``buttons`` sections: + +.. code-block:: yaml + + tab1_name_with_3_cols: + config: + cols: 3 + buttons: see below + tab2_name_with_2_cols: + config: + cols: 2 + buttons: see below + tab3_name_with_4_cols: + buttons: see below + + +The ``buttons`` key should itself point to a dictionary that maps each +button text to a button config dictionary, in order. +LUCID will iterate through these dictionaries and create buttons, +arranging them from left to right across each column before moving to the next row. + +The button config dictionary has one special key: ``type``. +The ``type`` key can either be ``shell`` to create a ``PyDMShellCommand`` button, +or it can be ``display`` to create a ``PyDMRelatedDisplayButton``. +If ``type`` is omitted, or is not one of these options, +an inactive ``QPushButton`` will be created. + +All other keys in the config dictionary should be mappings from qt property names +to each property's desired value. +This is forward-compatible with any new properties that may be implemented in the future +for these button types. + +Here's an example buttons config snippit that shows the structure and some of the +useful properties. This will create two buttons, one with button text "Cool PyDM Display" +and another with button text "Run Neat Script." + +.. code-block:: yaml + + buttons: + Cool PyDM Display: + type: display + filenames: + - /path/to/some/ui/file.ui + macros: + - "{'PREFIX': 'COOL'}" + Run Neat Script: + type: shell + commands: + - "echo 'neat'" + - /path/to/some/neat/shell/script.sh + redirectCommandOutput: true + +Examples +-------- + +The LCLS lucid toolbar configurations are backed up in +`this github repo `_. +These are all good examples for how to set these files up. diff --git a/lucid/dock_shim.py b/lucid/dock_shim.py new file mode 100644 index 0000000..1e45c93 --- /dev/null +++ b/lucid/dock_shim.py @@ -0,0 +1,20 @@ +""" +Shim module for dealing with the repeatedly changing import API for +the conda builds of PyQtAds. + +The pypi builds do not seem to have the same issue. +""" +try: + # pre-v4.0.0 + from PyQtAds import QtAds as ads +except ImportError: + try: + # from v4.0.0 to v4.1.1 + from PyQtAds import ads + except ImportError: + # starting at v4.2.0 (latest, for now) + import PyQtAds as ads + +if not hasattr(ads, "CDockWidget"): + import PyQtAds + raise ImportError(f"Submodule name for PyQtAds changed (again): {dir(PyQtAds)}") diff --git a/lucid/launcher.py b/lucid/launcher.py index 6a467d6..79f92fd 100644 --- a/lucid/launcher.py +++ b/lucid/launcher.py @@ -12,12 +12,12 @@ import typhos.utils from ophyd.signal import EpicsSignalBase from pydm import exception -from PyQtAds import ads from qtpy import QtCore, QtWidgets import lucid from . import utils +from .dock_shim import ads MODULE_PATH = pathlib.Path(__file__).parent diff --git a/lucid/main_window.py b/lucid/main_window.py index 3419f2d..19f6d1f 100644 --- a/lucid/main_window.py +++ b/lucid/main_window.py @@ -4,7 +4,6 @@ import typhos from pydm import exception -from PyQtAds import ads from qtpy import QtCore, QtGui, QtWidgets from qtpy.QtCore import Qt, Signal from qtpy.QtWidgets import QMainWindow, QSizePolicy, QStyle @@ -12,6 +11,7 @@ import lucid from . import utils +from .dock_shim import ads logger = logging.getLogger(__name__)