Source code for schrodinger.ui.qt.standard.icons.icons
"""
Module to access all Schrodinger standard icons filepaths. Instructions on
using, adding, and editing icons can be found below.
====================
Using standard icons
====================
Standard icon filepaths are exposed as constants. To browse the available icons
and what constants they correspond to, run::
$SCHRODINGER/run catalog_gui.py
Once an icon of interest has been identified, import `icons.py` and access
the icon's path. Example::
from schrodinger.ui.qt.standard.icons import icons
from schrodinger.Qt import QtGui
ok_lb_icon = QtGui.QIcon(icons.OK_LB)
=====================
Adding standard icons
=====================
1) If the new icon belongs to an existing family (e.g. a new icon state
variation), skip to step 2.
If the new icon does not belong to an existing family, add a blank row to
the metadata csv (schrodinger/ui/qt/standard/icons/icon_data/icon_data.csv)
and fill in the columns.:
- **Action:** The action for the icon (UX should provide)
- **Keywords:** Any words not already present in `Action` that would be helpful
to search for that icon. Should be space-separated. (UX can provide,
but devs can also supplement with whatever seems helpful)
- **Directory name:** The name of the directory where all icons in this family
will be stored. This should pretty much be the same as the action in
most cases but lowercase and hyphen-separated. (UX won't provide. Use
best judgment to make most concise name possible)
- **Original File Name:** Copy/paste the file name that UX gives you. This
helps map the new filenames to the old ones that UX has in case UX
needs to know which is which.
- **Current or Proposed Symbol:** What the symbol looks like (Include if UX
provides, but not essential)
2) Rename the icon file according to the naming convention outlined below in
"File structure". If this is a completely new family of icons, also create
the icon subdirectory, as outlined in "File structure".
3) Place the icon into the appropriate icon subdirectory.
4) Run unit tests. If test_get_icon_metadata_from_csv() fails, run
test_identify_undetected_files() (skipped by default) to identify which
icon files are not being registered by the module. Most likely there is
misspelling in the metadata csv, the directory name, or the file name.
5) Confirm the icon appears as expected in the icon viewer with the desired
search terms. If not, edit the metadata csv as needed.
6) Commit changes.
======================
Editing standard icons
======================
1) Edit the metadata csv as needed. See 1) from "Adding standard icons" for
information on each of the csv columns.
2) Perform 4) - 6) from "Adding standard icons"
==============
File structure
==============
The following must be true in order for this to work properly.
1) An icon metadata csv must be present at
schrodinger/ui/qt/standard/icons/icon_data/icon_data.csv.
2) The first four columns of the CSV must be 'Variable name', 'Action',
'Keywords', and 'Directory name' in that order. Other columns are ignored,
so they can contain other any information helpful to whoever is
editing the CSV.
3) Each row of the CSV corresponds to only the default version of an icon.
4) The icons directory MUST follow a particular structure::
- icons
| - <icon-dir-name-1>
| - <icon-dir-name-1>_<icon_state_information_1>.png
| - <icon-dir-name-1>_<icon_state_information_2>.png
| - ...
| - <icon-dir-name-1>_<icon_state_information_N>.png
| - <icon-dir-name-2>
| - etc.
Note that each icon in an icon subdirectory must have a base name equal
to the directory name. Dashes are used in place of spaces for the dir name.
Each icon filename then has additional information denoting its icon state
and is separated by underscores. This naming convention helps us identify
the icon family name from just the icon's file name.
Here's an example using the collapse-chevron-vert subdirectory::
| - collapse-chevron-vert
| - collapse-chevron-vert_db_d.png
| - collapse-chevron-vert_db_h.png
| - collapse-chevron-vert_db.png
| - collapse-chevron-vert_lb_d.png
| - collapse-chevron-vert_lb_h.png
| - collapse-chevron-vert_lb.png
5) See LEGEND_TEXT below for a description of the different icon suffixes.
"""
import csv
from pathlib import Path
from schrodinger.models import parameters
LEGEND_TEXT = """
Each set of letters separated by underscores describes the icon state:
lb = light background
db = dark background
eb = either background (works for light/dark)
hvr = hover state
dis = disabled state
act = active state
clk = click state (during a click event)
Icons with just "_lb" or "_db" are the default light or dark background
state versions of the icon, whereas those with e.g. "_lb_hvr" would be the
hover state of the icon against a light background panel.
Some other icon states like "on", "off", and special cases will be written
out completely since they are uncommon, and thus don't need any
special codes like the above.
"""
_ICONS_DIR = Path(__file__).parent
_ICON_DATA_DIR = _ICONS_DIR / 'icon_data'
_ICON_DATA_CSV = _ICON_DATA_DIR / 'icon_data.csv'
# str(path_obj) should make the string appropriate to each OS
_ICON_DATA_CSV_PATH = str(_ICON_DATA_CSV)
_ICON_METADATA_MAP = {}
def _get_icon_states_metadata(action, filter_terms, icon_family_name):
"""
Get the metadata of any and all states of an icon.
:param action: The action that this icon family is meant to portray.
:type action: str
:param filter_terms: A collection of terms that one could use to filter
this particular icon family out from a larger set of icons. It is
space-separated for simplicity because ultimately filtering is done
via regular expression matching.
:type filter_terms: str
:param icon_family_name: The name of the given icon family. Also the
name of the subdirectory inside ./icons_dir where these icon files
live.
:type icon_family_name: str
:return: Dict of variable names mapped to IconMetadata objects for all
icons of the given `icon_family_name`.
:rtype: Dict(str: IconMetadata)
"""
icon_family_dir = _ICON_DATA_DIR / icon_family_name
states_metadata = {}
for icon_state_path in icon_family_dir.glob('*.png'):
var_name = _generate_var_name(icon_state_path, icon_family_name)
if var_name is None:
continue
# Convert to posix path for Qt to be happy with windows paths
filepath = icon_state_path.as_posix()
state_metadata = IconMetadata(var_name=var_name,
action=action,
filepath=filepath,
filter_terms=filter_terms)
states_metadata[var_name] = state_metadata
return states_metadata
def _generate_var_name(icon_path, family_name):
"""
Generate the variable name of a given icon path. If the icon's name is
misspelled or belongs to a different icon family entirely (e.g. file
got misplaced), return None instead of a variable name.
See unit test for examples.
:param icon_path: The absolute path to an icon
:type icon_path: pathlib.Path
:param family_name: The name of an icon family. Also the name of the
subdirectory inside ./icons_dir where `icon_path` is located.
:type family_name: str
:return: The variable name or None
:rtype: str or None
"""
var_name = icon_path.stem
fam_name_end_idx = var_name.find('_')
icon_family_name = var_name[:fam_name_end_idx]
if icon_family_name != family_name:
return None
return var_name.upper().replace('-', '_')
def __getattr__(name):
"""
Return any attribute that exists in this module.
The idea is to expose icon filepaths with the following API:
from schrodinger.ui.qt.icons import icons
ok_lb_path = icons.OK_LB
All icon filepath global variables are assigned dynamically by
_initialize(), which thus must be called the first time a
filepath variable is accessed in this module. In order to avoid repeated
access to the metadata csv and variable declaration, we cache
_initialize().
:return: The requested attribute
:raise: AttributeError when the requested attribute doesn't exist.
"""
icon_path_map = get_icon_metadata_from_csv()
if name in globals().keys():
return globals()[name]
elif name in icon_path_map.keys():
return icon_path_map[name].filepath
raise AttributeError(f"module {__name__} has no attribute {name}")