Base Components

Base Components are the building blocks for your Edifice application. These components may all be imported from the edifice namespace:

import edifice
from edifice import View, Label

# you can now access edifice.Button, View, etc.

All components in this module inherit from QtWidgetComponent and its props, such as style and on_click. This means that all widgets could potentially respond to clicks and are stylable using css-like stylesheets.

The components here can roughly be divided into layout components and content components.

Layout components take a list of children and function as a container for its children; it is most analogous to the <div> html tag. The two basic layout components are View and ScrollView, They take a layout prop, which controls whether children are laid out in a row, a column, or without any preset layout. A layout component without children will appear as an empty spot in the window; of course, you could still set the background color, borders, and size, making this a handy way of reserving blank spot on the screen or drawing an empty rectangle.

Content components display some information or control on the window. The basic component for displaying text is Label, which simply displays the given text (or any Python object). The font can be controlled using the style prop. The Icon component is another handy component, displaying an icon from the Font Awesome icon set. Finally, the Button and TextInput components allow you to collect input from the user.

QtWidgetComponent([style, tool_tip, cursor, …])

Base Qt Widget.

Window([title, icon, menu, on_close])

Component that displays its child as a window.

View([layout])

Basic layout widget for grouping children together

ScrollView([layout])

Scrollable layout widget for grouping children together.

TabView([labels])

Widget with multiple tabs.

GridView([layout, key_to_code])

Grid layout widget for rendering children on a 2D rectangular grid.

Label([text, selectable, editable, word_wrap])

Basic widget for displaying text.

Image([src, scale_to_fit])

An image container.

Icon(name[, size, collection, …])

Display an Icon

IconButton(name[, size, collection, …])

Display an Icon Button.

Button([title])

Basic button widget.

TextInput([text, on_change, on_edit_finish, …])

Basic widget for a one line text input

CheckBox([checked, text, on_change])

Checkbox widget.

RadioButton([checked, text, on_change])

Radio buttons.

Slider([value, min_value, max_value, dtype, …])

Slider bar widget.

Events

For every base component, user interactions generate events, and you can specify how to handle the event by passing a callback function (which is either a function or an asyncio coroutine). These callbacks can be passed into the base component as props, for example the on_click callback that can be passed to every widget, or the on_change callback for checkboxes, radio buttons, sliders, and text input. The callback function is passed an argument describing the event, for example a click object holding click information (location of cursor, etc) or the new value of the input.

These callbacks run in the same thread as the main application. This is handy, as you don’t have to worry about locking and race conditions. However, a lengthy operation will block the application from interacting with the user, which is generally a bad user experience. For such cases, you can use an asyncio coroutine.

Consider this code:

class Component(edifice.Component):

    def __init__(self):
        super().__init__()
        self.results = ""
        self.counter = 0

    def on_click(self, e):
        results = fetch_from_network()
        self.set_state(results=results)

    def render(self):
        return edifice.View()(
            edifice.Label(self.results),
            edifice.Label(self.counter),
            edifice.Button("Fetch", on_click=self.on_click),
            edifice.Button("Increment", on_click=lambda e: self.set_state(counter=self.counter + 1))
        )

When the Fetch button is clicked, the event handler will call a lengthy fetch_from_network function, blocking the application from further progress. In the mean time, if the user clicks the increment button, nothing will happen until the fetch is complete.

To allow the rest of the application to run while the fetch is happening, you can define the on_click handler as a coroutine:

class Component(edifice.Component):

    def __init__(self):
        super().__init__()
        self.results = ""
        self.counter = 0
        self.loading = False

    async def on_click(self, e):
        self.set_state(loading=True)
        results = await asyncio.to_thread(fetch_from_network)
        self.set_state(loading=False, results=results)

    def render(self):
        return edifice.View()(
            edifice.Label(self.results),
            self.loading and edifice.Label("Loading"),
            edifice.Label(self.counter),
            edifice.Button("Fetch", on_click=self.on_click),
            edifice.Button("Increment", on_click=lambda e: self.set_state(counter=self.counter + 1))
        )

While the fetch_from_network function is running, control is returned to the event loop, allowing the application to continue handling button clicks.

See docs for QtWidgetComponent for a list of supported events.

Custom Widgets

Not all widgets are currently supported by Edifice. Edifice provides CustomWidget to allow you to bind arbitrary QtWidgets to an Edifice component. The two methods to override are create_widget, which should return the Qt widget, and paint, which takes the current widget and new props, and should update the widget according to the new props.

CustomWidget()

Custom widgets that you can define.