Managers

Managers manage displaying panels and retrieving user input, so you don’t have to worry about it. Your code just needs to tell the manager what you want to display and watch for messages back from the manager containing input from the user.

thurible.queued_manager(q_to: Queue, q_from: Queue, term: Terminal | None = None, displays: dict | None = None) None[source]

Manage a terminal display by sending and receiving thurible.messages.Message objects through queue.Queue objects.

Warning

thurible.queued_manager() is intended to be run in its own thread or process. If you try to run it synchronously with the rest of your application, the loop will prevent your application from completing execution. This is why it is a “queued” manager.

Parameters:
  • q_to – A queue for messages the program sends to the manager.

  • q_from – A queue for messages the manager sends to the program.

  • term – An instance of blessed.Terminal used to interact with the terminal.

  • displays – (Optional.) Storage for the panels the program may want the manager to display.

Returns:

None.

Return type:

NoneType

Usage:
>>> from queue import Queue
>>> from threading import Thread
>>> from thurible import get_terminal, queued_manager
>>> from thurible.messages import End
>>>
>>> # Create a queue to send messages to the manager.
>>> q_in = Queue()
>>>
>>> # Create a queue to receive messages from the manager.
>>> q_out = Queue()
>>>
>>> # Get a terminal instance for the manager to use.
>>> term = get_terminal()
>>>
>>> # Run the manager in a separate thread.
>>> T = Thread(target=queued_manager, args=(q_in, q_out, term))
>>> T.start()
>>>
>>> # End the thread running the queued_manager.
>>> msg = End('Ending.')
>>> q_in.put(msg)
thurible.event_manager(event_map: Mapping[type, Callable[[Message, Queue], bool]] | None = None, initial_panel: Panel | None = None) None[source]

Manage a terminal display by mapping Response Messages to event scripts (see below).

Parameters:
  • event_map – (Optional.) A dict mapping the thurible.panel.Message types managers can send to applications (Response Messages) to functions in your application. These functions must accept a queue.Queue object and the response message as parameters. It must return a bool indicating whether the application should continue running.

  • initial_panel – (Optional.) The first panel displayed in the terminal. While this is technically optional, that’s just for testing purposes. You should really provide this to the manager. The panel passed this way will be stored as “__init”.

Returns:

None

Return type:

NoneType

Usage:

An example small application that uses the thurible.event_manager() show a splash screen then quit if a button is pressed:

from thurible import event_manager, Splash
import thurible.messages as tm

# Create the event handlers.
def data_handler(msg, q_to):
    msg = tm.End('Quitting.')
    q_to.put(msg)
    return False

def ending_handler(msg, q_to):
    if msg.exception:
        raise msg.exception
    return False

# Map the handlers to event messages.
event_map = {
    tm.Data: data_handler,
    tm.Ending: ending_handler,
}

# Create the panel to display when the manager starts.
splash = Splash('SPAM!')

# Run the event_manager.
event_manager(event_map, splash)
Event scripts:

An event script is a function that:

  • Accepts a response message it will receive from the manager and a queue.Queue for it to send command messages to the manager.

  • Returns True if the manager should continue running.

  • Returns False if the manager should end.

For example, let’s say we want an event script that will handle user input. It will:

  • Display a splash screen if the user presses x,

  • Display a different splash screen if the user presses y,

  • End the event_manager() if the user presses the space bar.

That would look like:

import thurible
from thurible import messages as msgs

def data_handler(data, q_to):
    keep_running = True

    # If the user presses `x` display the X screen.
    if data.value == 'x':
        splash = thurible.Splash('XXXXX')
        store_msg = msgs.Store('x', splash)
        q_to.put(store_msg)
        show_msg = msgs.Show('x')
        q_to.put(show_msg)

    # If the user presses `y` display the Y screen.
    if data.value == 'y':
        splash = thurible.Splash('YYYYY')
        store_msg = msgs.Store('y', splash)
        q_to.put(store_msg)
        show_msg = msgs.Show('y')
        q_to.put(show_msg)

    # If the user presses ` ` end.
    if data.value == ' ':
        keep_running = False

    return keep_running

These event scripts are intended for fairly simple use cases. They respond to a single message from the manager. They can add messages to the manager’s input queue to tell the manager to act. They can tell the manager to end.

If you need more complex behaviors like checking the manager’s state or maintaining internal state, you probably should use queued_manager() directly rather than event_manager().

How Managers Work

The following are details on the internal operations of managers to help you understand how to work with them. While they apply to both managers, these details are mostly hidden from event_manager(). It’s more important to understand them when working with queued_manager().

The Manager Loop

Once started, a manager loops through a standard set of actions until it is told to close or crashes. Those actions are:

  1. Check for a message from the application.

    1. If there was a comand message, perform the commanded action.

    2. If the commanded action should return a response to the application, send that response.

    3. If there is no action for the command message received, send that message to the Panel.update() of the currently displayed panel.

  2. Check for a key press from the user.

    1. If there was a key press, send the blessed.keyboard.Keystroke object to the Panel.action() of the currently displayed panel.

    2. If the Panel.action() returns data, send that data to the application in a thurible.messages.Data message.

    3. If the Panel.action() returns an update, print that update to the terminal.

The loop will end under the following conditions:

  • The application sends a thurible.messages.End message,

  • The manager encounters a known exception it cannot recover from.

  • The manager encounters an unknown exception.

When possible, the manager will send a thurible.messages.Ending message to announce the end of the loop. If the loop is ending due to and exception, that exception is passed to the application within the thurible.messages.Ending message.

Storing Panels

To allow your application to pre-load panels and easily switch back and forth between panels without having to recreate them, the manager stores all panels sent to it with the thurible.messages.Store message or created with the thurible.messages.Alert message. Those messages allow you to assign a name to the panel, which is then used by other messages, like thurible.messages.Show, to interact with the stored panels.

Warning

Managers store panels in a dict object, using the name given to the panel as the key. This means that if you provide the same name for two panels, one panel will overwrite the other panel. For this reason, it is recommended all panels be given unique names.

Examples

The examples directory in the thurible repository provides examples for how to use managers.

An example of using event_manager() is found in:

  • examples/eventsplash.py

Examples of using queued_manager() are found in:

  • examples/favword.py

  • examples/filereader.py

  • examples/showsplash.py

  • examples/tensecs.py