Modes¶
Modes are global state that can be used to allow esc operations to work in different ways. For instance, the trig operations in the trig plugin distributed with esc use a degrees/radians mode. Modes tend to be confusing to both users and programmers, so it’s best to avoid them when possible, but sometimes it’s difficult to do without them (do you really want to create and select a whole separate set of trig operations depending on whether you’re calculating in degrees or radians?).
Working with modes¶
Modes are created using the Mode
and ModeChange
constructors:
-
esc.commands.
Mode
(name, default_value, allowable_values=None)[source]¶ Register a new mode.
Parameters: - name – The name of the mode. This is used to refer to it in code.
If a mode with this name already exists,
a
ProgrammingError
will be raised. - default_value – The value the mode starts at.
- allowable_values – An optional sequence of possible values for the mode.
If defined, if code ever tries to set a different value,
a
ProgrammingError
will be raised.
- name – The name of the mode. This is used to refer to it in code.
If a mode with this name already exists,
a
-
esc.commands.
ModeChange
(key, description, menu, mode_name, to_value)[source]¶ Create a new mode change operation the user can select from a menu. Syntactic sugar for registering an operation.
Parameters: - key – The key to press to select the constant from the menu.
- description – A brief description to show next to the key.
- menu – A
Menu
to place this operation on. - mode_name – The name of the mode, registered with
Mode()
, to set. - to_value – The value the mode will be set to when this operation is selected.
Aside from defining ModeChange
operations,
you can view and edit the values of modes
using the modes
module:
modes.py - Manage calculator state/modes
-
class
esc.modes.
Mode
(name, value, allowable_values)[source]¶ esc modes implement basic calculator state like a degrees/radians switch. In esc, they are created and used by menus with families of related operations, where they can also be displayed. They have a name, a current value, and optionally a set of allowable values; if something ever causes the value to be set to a non-allowable value, a
ProgrammingError
will be raised, hopefully identifying the issue before it leads to wrong results.Modes are usually created by the
esc.commands.Mode()
factory function, not by calling this constructor directly.
-
esc.modes.
get
(name)[source]¶ Retrieve the value of a mode with a given name. Return None if no mode by that name has been registered.
-
esc.modes.
register
(name, default_value, allowable_values=None)[source]¶ Create a new mode. If the mode already exists, a
ProgrammingError
is raised.Modes should be registered by the
esc.commands.Mode()
factory function, not by calling this function directly.
-
esc.modes.
set
(name, val)[source]¶ Set a mode to a new value. If the mode doesn’t exist, a
KeyError
will be raised. If the value is invalid for the mode, aProgrammingError
will be raised.
You’ll probably want to display the value of your mode somewhere
– as confusing as modes can be already,
they’re even worse when they’re invisible!
Typically, this is done by supplying a mode_display
callable
to the Menu
that contains the associated operations.
This callable takes no arguments
and calls modes.get
to determine the display value:
def my_mode_display_1():
# Assumes the values of the mode are strings.
# If they're something else, you need to map them to strings here.
return modes.get('my_mode')
Example¶
Here’s a complete silly example:
from esc.commands import Operation, Mode, ModeChange, Menu, main_menu, BINOP
from esc import modes
def opposite_mode():
return "[opposite day]" if modes.get('opposite_day') else ""
bool_menu = Menu('b', 'boolean functions', parent=main_menu, doc="blah",
mode_display=opposite_mode)
Mode('opposite_day', False, (True, False))
ModeChange("o", "enable opposite day", menu=bool_menu,
mode_name='opposite_day', to_value=True)
ModeChange("O", "disable opposite day", menu=bool_menu,
mode_name='opposite_day', to_value=False)
@Operation('a', menu=bool_menu, push=1,
description="AND sos and bos",
log_as=BINOP)
def and_(x, y):
result = x and y
return int((not result) if modes.get('opposite_day') else result)
# ...insert other boolean functions here