pypercard.core

PyperCard is a simple HyperCard inspired framework for PyScript for building graphical apps in Python.

Based on original pre-COVID work by Nicholas H.Tollervey.

Copyright (c) 2023 Anaconda Inc.

Licensed under the Apache License, Version 2.0 (the “License”); you may not use this file except in compliance with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

Module Contents

Classes

Card

Represents a card in the application. A card defines what is presented to the user.

App

Represents a card based application.

API

class pypercard.core.Card(name, template=None, on_show=None, on_hide=None, auto_advance=None, transition=None, sound=None, sound_loop=False, background=None, background_repeat=False)

Represents a card in the application. A card defines what is presented to the user.

The app ensures that only one card is ever displayed at once. Each card has a name and an HTML template that defines how it looks on the page. If a template is not passed in on instantiation, the Card will look for a template element within the DOM with the same id as the given name to use as the template instead.

Cards may also have optional auto_advance and transition attributes for transitioning to a target card after a given period of time.

Cards are rendered from the template in the show method. The first time show is called it creates a pyper-card HTML element for the app to insert into the DOM.

Bespoke behaviour for rendering can be defined by the user. This should be passed in as the optional on_show argument when initialising the card. It will be called, at the end of the card’s show function, but before the element to insert into the DOM is returned to the app. The on_show function is called with the same arguments as a transition function: a reference to the app and the current card.

The hide method hides card’s HTML element, but leaves it in the DOM.

Card’s can optionally take some action each time a card is hidden using the on_hide method. The on_hide method is called with the same arguments as a transition function: a reference to the app and the current card.

It’s also possible to use the register_transition method to register a user defined function to handle events dispatched by elements found in the card’s HTML. These contain application logic and transition the app to new cards by returning a string indicating the name of the next card to show.

The convenience functions called get_by_id, get_element and get_elements return individual or groups of matching HTML elements rendered by this card, given a valid id or CSS selector (comments attached to the functions explain the specific behaviours).

Cards can optionally define the nature of their background, via the background and background_repeat attributes. The background should either be a valid CSS color or a URL to an image. If the background is an image, the background_repeat flag will indicate if the image will fill the whole screen or repeat in a tiled fashion (the default is to fill the whole screen).

Initialization

Initialise the card with a name unique within the application in which it is used.

The following optional arguments are available to customize the card’s appearance and behaviour.

The string content of the template argument is used to render the card. If not given, the card will attempt to extract the innerHTML from a template tag with an id of the given name of the card. Otherwise, the card will raise a RuntimeError.

The on_show function is called every time the card is shown. It should take app and card arguments (just like transitions) and be used for customising the rendered card.

The on_hide function is called every time the card is hidden. It should take app and card arguments (just like transitions) and be used for stopping actions (sounds etc.).

The auto_advance is the number of seconds, as a float or int, to wait until the transition is evaluated to discern the next card to which to automatically transition.

The transition can be either a string containing the name of the card to which to automatically transition, or a transition function to call that returns a string containing the name of the next card.

Either both auto_advance and transition need to be given, or both need to be None. Otherwise, the card will raise a ValueError. If the transition is not a string or function or the auto_advance is not an integer or float, a TypeError will be raised.

The optional background argument can either contain a valid CSS color or a URL to an image to display as the background.

The optional background_repeat flag defines if the background image fills the whole screen (the default) or repeats in a tiled fashion (if the flag is set to True).

register_app(app)

Add a reference to the hosting app, of which this card is a part.

show()

Show the card to the user.

If this is the first time the card has been shown a pyper-card element will be created for it and inserted into the DOM (as a child of the app’s pyper-app element).

If the card has already been shown then we simply make it visible by setting the element’s display attribute to block.

Ensures the template is .format-ed with the self.app.datastore dictionary (so named custom values can be inserted into the template).

Rebinds any user defined transitions to the newly rendered elements created by the card.

_add_dom_event_listeners()

Add DOM event listeners for any transitions added via “app.transition”.

_start_auto_advance_timer()

Start the card’s auto-advance timer.

hide()

Hide the card from the user.

This leaves the card in the DOM but just sets display to none, and removes any DOM event listeners.

_remove_dom_event_listeners()

Remove any DOM event listeners that were hooked up when the card was shown.

register_transition(dom_event_name, element_id=None, query=None)

event_name - e.g. “click” element_id - the unique ID identifying the target element. query - a CSS selector identifying the target element(s).

get_by_id(element_id)

Convenience function for getting a child element by id. Returns None if no element is found.

get_element(selector)

Convenience function for getting a child element that matches the passed in CSS selector. Returns None if no element is found.

get_elements(selector)

Convenience function for getting a Python list of child elements that match the passed in CSS selector. Returns an empty list if no elements are found.

class pypercard.core.App(name=None, datastore=None, cards=None, sounds=None)

Represents a card based application.

This encapsulates the state (as a DataStore), stack of Card instances, and registering transitions.

Other app-wide functions (such as playing or pausing sounds) are methods of this class. To add your own app-wide functions, create a sub-class of this one.

TODO: It will be possible to dump and load a declarative JSON representation of the application, once the specification and capabilities of an app are finalised.

If no default arguments given, the app will assume sensible defaults.

Initialization

Initialise a PyperCard app.

If no name is given, the page’s title value is used, otherwise the given name becomes the page title.

The datastore is an optional pre-populated DataStore instance.

The cards are an optional list of Card instances with which to initialise the app. If no cards are given, the app will look in the DOM for template tags to populate the card stack.

The sounds dict contains default name / url pairs that define the initial sounds the app may need to play.

_new_id()

Gets a likely unique id to be attached to an element that doesn’t have one (but should).

Why not UUID? MicroPython.

_harvest_cards_from_dom()

Harvest any cards defined in the DOM.

This queries the DOM for all ‘template’ tags. The contents and attributes of each template tag is used to configure a card in the app’s stack of cards.

Returns a (possibly empty) list of the Card instances.

_resolve_card(card_reference)

Given a card reference, that could be either a string containing the card’s name, or a card object, returns the correct card instance if the card is in the app’s stack.

Otherwise, raises a ValueError.

show_card(card)

Show the referenced card into the DOM via self.placeholder.

TODO: enable different types of visual transition between cards (e.g. fade, slide etc…).

hide_card(card)

Hide the specified card.

add_card(card)

Add a card to the stack.

get_next_card(card)

Get the next card sequentially in the card list.

Returns None if ‘card’ is the last card.

remove_card(card_reference)

Remove a card from the stack.

The reference to the card can be an instance of the card itself, or a string containing the card’s name.

add_sound(name, url)

Add a named Audio object to the application, to play the sound file found at the given URL.

NOTE The URL is NOT a reference to a file on the PyScript filesystem, but a file accessible to the browser via an HTTP request.

get_sound(name)

Get the sound referenced by the given name.

If the name doesn’t reference a sound, a ValueError is raised.

remove_sound(name)

Remove the Audio object referenced by the given name.

play_sound(name, loop=False, multitrack=False, restart=False)

Play the sound, added to self with the given name. If the sound was paused with the keep_place flag set to True, the sound will resume playing from the place at which it was paused. Otherwise, the sound will play from the start.

If loop is True the sound will keep repeating until paused or removed from the application.

If multitrack is True then any currently playing sounds will continue to play. The default is false, so only one track will play at any given time (the most recet track to be played).

If restart is True then, if the sound is already playing when this function is called, it will be restarted from the beginning, otherwise the sound will be allowed to continue to play as is.

pause_sound(name, keep_place=False)

If the sound, added to self with the given name, is playing, pause it. If keep_place is True the sound will pause at its current location. Otherwise, should the sound be played again, it will play from the start.

register_transition(dom_event_name, element_id=None, selector=None)

event_name - e.g. “click” element_id - the unique ID identifying the target element. query - a CSS selector identifying the target element(s).

set_background(background='')

Set the body tag’s background style attribute to the given value. If no value is given, resets it to blank.

transition(from_card_name_or_list, dom_event_name, id=None, query=None)

A decorator to create transitions for DOM events within the specified card.

This just adds a transition to the app’s state machine.

The from_card_name_or_list can be either a string of the name of the target card, or a list of target card names. If the card name is “*” the transition applies to ALL cards in.

The dom_event_name is the name of the event, as dispatched by the browser, e.g. “click”.

The id is the unique id attribute of the target element within the referenced card[s], that will dispatch the event.

Finally, query is a way to provide a valid CSS query to match elements within the referenced card[s], that will dispatch the event.

start(card_reference=None)

Start the app with the referenced card.

The reference to the card can be an instance of the card itself, or a string containing the card’s name.

If no card_reference is given, the app will start with the first card that was added to its stack.

dump()

TODO: Dump a tree (JSON) representation of the app.

load(tree)

TODO: Load a tree (JSON) representation of the app.

_create_auto_advance_transition(from_card)

Create a transition that accepts a timeout and advances to another card.

_create_dom_event_transition(from_card_name, transition_fn_or_card_name, dom_event_name, element_id=None, selector=None)

Create a transition that is triggered by a DOM event.

_create_card_state(card)

Create a state machine state for the specified card.

Returns a tuple in the form (State, [Transition])

_get_name_of_card_to_transition_to(from_card, transition_fn_or_card_name, input_)

Get the name of the card to transition to.