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
.
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.
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
.
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()
|