Examples

A library of current examples can be found in the examples/ folder. You can run them as python modules, or if you want to modify them:

$> pip install watchgod
$> watchgod examples.[example_name].main

Some notes are also included below.

Basic Example

This example shows how to create a custom component that renders static UI. Note that by using extra_qt.renderers.qt_renderer, extra_qt makes a window for us. You can also pass a node (in the case of Qt rendering, a QWidget) to mount the render tree onto. This allows you to use extra_qt to provide the UI only in part of your application, or onto a window’s .centralWidget.

_images/examples.basic.main.png
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
from datetime import datetime

from extra_qt.component import Component
from extra_qt.renderers.qt_renderer import render_window
from extra_qt.dom.qt_dom import group, label, create_element


class MyComponent(Component):
    def render(self):
        return group(dict(title='My Boring Component',), [
            label(f'When I started it was {self.props.get("start_time")}'),
        ])


def main():
    render_window(create_element(MyComponent, dict(start_time=datetime.now().isoformat())))


if __name__ == '__main__':
    main()

Nesting Components

You can nest a Component inside another Component, and they will be instantiated with their own state and properties. In order to render a component into the tree, use MyComponent.c (do not use the constructor, MyComponent.Render returns a representation of the UI and not the QWidget itself!). MyComponent.c is just shorthand for create_element(MyComponent, ...) so you can use that too.

_images/examples.nested_components.main.gif
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
from dataclasses import dataclass

from PyQt5.QtCore import QTimer

from extra_qt.component import Component
from extra_qt.renderers.qt_renderer import render_window
from extra_qt.dom.qt_dom import *


@dataclass
class State:
    counter: int = 0

    def update(self):
        self.counter += 5


class ComponentB(Component):
    initial_state_cls = State
    update = Component.updates_state(State.update)

    def render(self):
        return group(dict(title='Inner Component',), [
            label(f'My count is: {self.state.counter}'),
            label(f'My received prop is: {self.props.get("counter")}'),
            button(text='Increment', on_click=self.update),
        ])


class ComponentA(Component):
    initial_state_cls = State
    update = Component.updates_state(State.update)

    def after_mount(self):
        self.timer = QTimer()
        self.timer.timeout.connect(self.update)
        self.timer.start(100)

    def before_unmount(self):
        self.timer.stop()

    def render(self):
        return group(dict(title='Outer Component',), [
            label(f'My count is: {self.state.counter}'),
            ComponentB.c(dict(counter=self.state.counter)),
            ComponentB.c(dict(counter=2 * self.state.counter)),
        ])


def main():
    render_window(create_element(ComponentA))


if __name__ == '__main__':
    main()

UI Elements Library

Showcases some of the UI elements available as primitives in extra_qt.

_images/examples.ui_elements.main.gif
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
from dataclasses import dataclass

from PyQt5.QtCore import QTimer

from extra_qt import Component
from extra_qt.dom.qt_dom import *
from extra_qt.renderers.qt_renderer import render_window


@dataclass
class SimpleComponentState:
    counter: int = 0
    color: str = 'red'
    functions_swapped: bool = False

    def increment(self):
        self.counter += 1

    def double(self):
        self.counter *= 2

    def swap(self):
        self.functions_swapped = not self.functions_swapped

    def reset(self):
        self.counter = 0
        self.color = 'red'


class SimpleComponent(Component):
    @classmethod
    def get_initial_state(cls):
        return SimpleComponentState()

    def after_mount(self):
        self.timer = QTimer()
        self.timer.timeout.connect(self.increment)
        self.timer.start(100)

    def before_unmount(self):
        self.timer.stop()

    increment = Component.updates_state(SimpleComponentState.increment)
    double = Component.updates_state(SimpleComponentState.double)
    reset = Component.updates_state(SimpleComponentState.reset)
    swap = Component.updates_state(SimpleComponentState.swap)

    def print(self, *args, **kwargs):
        print('!', *args, **kwargs)

    def render(self):
        first_set = group(dict(title='Grouped',), [
            button(text='Double', on_click=self.reset if self.state.functions_swapped else self.double),
            button(text='Zero', on_click=self.double if self.state.functions_swapped else self.reset),
            button(text='Swap', on_click=self.swap),
            *self.children,
            label(str(self.state.counter), dict(style=f'color: {self.state.color};')),
            label('Full State: ' + str(self.state)),

            check_box(on_change=self.print),
            spin_box(on_change=self.print),
            combo_box(keys=['A', 'B'], values=[1, 2], on_change=self.print),
            line_edit(on_change=self.print),
            dial(on_change=self.print),
            slider(on_change=self.print),
            text_edit(on_change=self.print),
        ])

        return tabs(
            props=dict(labels=['A', 'B'], selected='A'),
            children=[
                first_set,
                label(f'I am also a tab! {self.state.counter % 2}'),
            ],
        )


def main():
    render_window(create_element(SimpleComponent, children=[label('a')]))


if __name__ == '__main__':
    main()