Welcome to Flexx’s documentation!¶
Flexx is a pure Python toolkit for creating graphical user interfaces (GUI’s), that uses web technology for its rendering. Apps are written purely in Python; The PScript transpiler generates the necessary JavaScript on the fly.
You can use Flexx to create (cross platform) desktop applications, web applications, and export an app to a standalone HTML document. It also works in the Jupyter notebook.
The docs are on Readthedocs, the code is on Github, and there is a demo server. Once you’ve got started, the most important page is probably the Widget reference.
Contents¶
Getting started¶
Installation¶
pip install flexx
conda install flexx -c conda-forge
- Old school:
python setup.py install
- Clone the repo and add the root dir to your PYTHONPATH (developer mode)
When using Pip or Conda, the dependencies will be installed automatically.
Dependencies¶
Being pure Python and cross platform, Flexx should work (almost) anywhere where there’s Python and a browser. Flexx is written for Python 3.5+ and also works on Pypy.
Flexx further depends on the following packages (all are pure Python, and the latter three are projects under the flexxui umbrella):
Supported browsers¶
Flexx aims to support all modern browsers, including Firefox, Chrome and Edge. Internet Explorer version 10 and up should work, but some things may be flaky.
To run apps that look like desktop apps, we recommend having Firefox or nw.js installed.
Current status¶
Since version 0.5 (September 2018), Flexx is in beta status; some parts of the API may change, but we do care about backwards compatibility.
Guide¶
Widgets basics¶
If you’re interested in Flexx, the first thing that you probably want to do is create a UI. So let’s see how that works, and talk about components, events and reactions later.
Your first widget¶
The Widget
class is the base class of all
other ui classes. On itself it does not do or show much. What you’ll
typically do, is subclass it to create a new widget that contains ui
elements:
from flexx import flx
class Example(flx.Widget):
def init(self):
flx.Button(text='hello')
flx.Button(text='world')
The above is usually not the layout that you want. Therefore there are layout widgets
which distribute the space among its children in a more sensible manner. Like the
HBox
:
from flexx import flx
class Example(flx.Widget):
def init(self):
with flx.HBox():
flx.Button(text='hello', flex=1)
flx.Button(text='world', flex=2)
The HBox
and Button
are all widgets too. The example widgets that we
created above are also refered to as “compound widgets”; widgets that contain
other widgets. This is the most used way to create new UI elements.
The init method¶
In the above example one can see the use of the init()
method, which
is a common use in Flexx. It is generally better to use it instead of __init__()
,
because Flexx calls it at a very approproate time in the initialization process.
For example, when init()
is called, the corresponding widget is the default parent.
Further, the init()
gets the positional instantiation arguments: creating
a component Person("john", 32)
matches def init(self, name, age)
.
Structuring widgets¶
Flexx comes with it’s own layout system. (Therefore you should generally not use CSS for widget layout, though you can very well use CSS inside a widget).
Any widget class can also be used as a context manager. Within the context, that widget is the default parent; any widget that is created in that context and that does not specify a parent will have that widget as a parent. (This mechanism is thread-safe.) This allows for a style of writing that clearly shows the structure of your app:
from flexx import flx
class Example(flx.Widget):
def init(self):
with flx.HSplit():
flx.Button(text='foo')
with flx.VBox():
flx.Widget(style='background:red;', flex=1)
flx.Widget(style='background:blue;', flex=1)
Turning a widget into an app¶
To create an actual app from a widget, simply wrap it into an App
.
You can then launch()
it as a desktop app, serve()
it as a web app,
dump()
the assets, export()
it as a standalone HTML document, or
even publish()
it online (experimental). Later in this guide we dive deeper
into the different ways that you can run your app.
from flexx import flx
class Example(flx.Widget):
def init(self):
flx.Label(text='hello world')
app = flx.App(Example)
app.export('example.html', link=0) # Export to single file
To actually show the app, use launch:
app.launch('browser') # show it now in a browser
flx.run() # enter the mainloop
Using widgets the Python way¶
In the above examples, we’ve used the “classic” way to build applications from basic components. Flexx provides a variety of layout widgets as well as leaf widgets (i.e. controls), see the list of widget classes.
Further, we’ve created high-level widgets by subclassing the flx.Widget
class.
These classes operate in JavaScript, because they are what we call JsComponent
’s,
more on that later. Effectively, we are able to show the widgets live inside the guide itself.
However, if you are developing a desktop app, consider subclassing from PyWidget
instead: this will make that your widgets operatate in Python instead of JavaScript.
We talk more about this in the next page of the guide.
Using widgets the web way¶
An approach that might be more familiar for web developers, and which is inspired by frameworks such as React is to build custom widgets using html elements. If you’re used to Python and the below looks odd to you, don’t worry, you don’t need it:
from flexx import flx
class Example(flx.Widget):
name = flx.StringProp('John Doe', settable=True)
age = flx.IntProp(22, settable=True)
@flx.action
def increase_age(self):
self._mutate_age(self.age + 1)
def _create_dom(self):
# Use this method to create a root element for this widget.
# If you just want a <div> you don't have to implement this.
return flx.create_element('div') # the default is <div>
def _render_dom(self):
# Use this to determine the content. This method may return a
# string, a list of virtual nodes, or a single virtual node
# (which must match the type produced in _create_dom()).
return [flx.create_element('span', {},
'Hello', flx.create_element('b', {}, self.name), '! '),
flx.create_element('span', {},
'I happen to know that your age is %i.' % self.age),
flx.create_element('br'),
flx.create_element('button', {'onclick': self.increase_age},
'Next year ...')
]
The _render_dom()
method is called from an implicit reaction. This means
that when any properties that are accessed during this function change,
the function is automatically called again. This thus provides a declerative
way to define the appearance of a widget using HTML elements.
Above, the third argument in create_element()
is a string, but this may
also be a list of dicts (create_element()
returns a dict).
Next¶
Next up: Widgets are components.
Widgets are components¶
Widgets are what we call “components”, which are a central part of the event system. They are what allows widgets to have properties and react to things happening in other parts of the application. But let’s not get ahead of ourselves; the event system is dicsussed in the next chapter.
For the moment, it’s enough to know that the Widget
class is kind of JsComponent
.
This means that these widgets live in JavaScript: when their actions and methods
are called, they run in JavaScript. But the cool
thing is that you can still use these objects in Python, by setting their properties,
invoking their actions, and reacting to their state. This is possible because
of so-called proxy objects.
We mentioned earlier that the PyWidget
can be used to
create widgets that live in Python: these are a kind of PyComponent
:
their actions and methods are executed in Python. And yes, they can be used
from JS by setting properties, invoking action and reacting to state.
Being able to write components (and widgets) that operate either in Python or JavaScript is a very powerful feature of Flexx. However, it can easily be a source of confusion. Therefore it’s good to understand the difference between these kinds of classes.
PyComponent and JsComponent¶
A Flexx application consists of components that exist in either Python or JavaScript, and which can communicate with each-other in a variety of ways.
The PyComponent
and
JsComponent
classes derive from the
Component
class (which is a topic of the next chapter).
The most important things to know about PyComponent
and JsComponent
:
- They are both associated with a
Session
. - They have an
id
attribute that is unique within their session, and auid
attribute that is globally unique. - They live (i.e. their methods run) in Python and JavaScript, respectively.
- A
PyComponent
can only be instantiated in Python, aJsComponent
can be instantiated in both Python and JavaScript. - A
PyComponent
always has a corresponding proxy object in JavaScript. - A
JsComponent
may have a proxy object in Python; these proxy objects are created automatically when Python references the component.
In practice, you’ll use PyComponents
to implement Python-side behavior,
and JsComponents
(e.g. Widgets) for the behavior in JavaScript. Flexx
allows a variety of ways by which you can tie Python and JS together, but
this can be a pitfall. It’s important to think well about what parts of your
app operate in JavaScript and what in Python. Patterns which help you do this
are discussed later in the guide.
(And the plain Component
class? It can be
used (both in Python and JS), but is unaware of anything “on the other side”.
It’s use in Flexx is therefore limited.)
Proxy components¶
The proxy components allow the “other side” to inspect properties, invoke actions and connect to events. The real component is aware of what events the proxy reacts to, and will only communicate these events.
The example below may be a bit much to digest. Don’t worry about that. In most cases things should just work.
from flexx import flx
class Person(flx.JsComponent): # Lives in Js
name = flx.StringProp(settable=True)
age = flx.IntProp(settable=True)
@flx.action
def increase_age(self):
self._mutate_age(self.age + 1)
class PersonDatabase(flx.PyComponent): # Lives in Python
persons = flx.ListProp()
@flx.action
def add_person(self, name, age):
with self: # new components need a session
p = Person(name=name, age=age)
self._mutate_persons([p], 'insert', 99999)
@flx.action
def new_year(self):
for p in self.persons:
p.increase_age()
In the above code, the Person
objects live in JavaScript, while a
database object that keeps a list of them lives in Python. In practice,
the Person
components will e.g. have a visual representation in the
browser. The database could also have been a JsComponent
, but let’s
assume that we need it in Python because it synchronizes to a mysql
database or something.
We can observe that the add_person
action (which executes in Python)
instantiates new Person
objects. Actually, it instantiates proxy objects that
automatically get corresponding (real) Person
objects in JavaScript.
The new_year
action executes in Python, which in turn invokes the increase_age
action of each person, which execute in JavaScript.
Actions and events cross the boundary¶
It’s important to realize that actions of a component can be invoked from anywhere.
In the above example, Person.set_name("Guido")
can be called from
Python (e.g. by the PersonDatabase
).
Similarly, you can use reactions to listen for changes on components, no matter whether these components live in Python or JavaScript. As an example, let’s implement a personcounter (reactions are covered later in this guide):
class PersonCounter(flx.JsComponent):
def init(self, db):
self.db = db
# now we can call self.db.add_person() from JavaScript!
@flx.reaction
def _count(self):
print("There are", len(self.db.persons), "persons")
# To instantiate (e.g. inside PersonDatabase.init())
PersonCounter(database)
# Note that we can also invoke the db's actions from here!
The root component and active components¶
Another useful feature is that each component has a root
attribute that
holds a reference to the component representing the root of the application.
E.g. if the root is a PersonDatabase
, all JsComponent
objects have a
reference to (a proxy of) this database.
Further, when a component is used as a context manager, it becomes an
“active component”. We’ve already seen how this is used to structure
child widgets. Sometimes you may want to know which components are active,
which you can do with loop.get_active_component
and loop.get_active_components
.
Next¶
Next up: The event system.
The event system¶
The event system consists of components, properties, events, and reactions. They let different components of an application react to each-other and to user input.
In short:
- The
components
(e.g. widgets) form the units of which an application is build. - Each component has
properties
to reflect the state of the component. - Properties can only be mutated by
actions
. Calling (i.e. invoking) an action will not apply the action at once; actions are processed in batches. - When properties are modified (i.e. the state is changed),
corresponding
reactions
will be invoked. The reactions are processed when all pending actions are done. This means that during processing reactions, the state never changes, which is a great thing to rely on! - Reactions can also react to events generated by
emitters
, such as mouse events. - The
event loop
object is responsible for scheduling actions and reactions. In Python it integrates with Python’s own asyncio loop. In JavaScript it makes use of the JavaScript scheduling mechanics.
The asynchronous nature of actions combined with the fact that the state does not change during processing reactions, makes it easy to reason about cause and effect. The information flows in one direction. This concept was gratefully taken from modern frameworks such as React/Flux and Veux.
One might argue that the information flow is still circular, because there is an arrow going from reactions to actions. This is true, but note that actions invoked from reactions are not directly executed; they are pended and will be executed only after all reactions are done.
Relation to other parts of Flexx¶
This event system and its Component
class
form the basis for app.PyComponent
,
app.JsComponent
and the UI system
in flexx.ui
. It can be used in both Python and JavaScript and works exactly
the same in both languages.
Other than that, this is a generic event system that could drive any system that is based on asyncio.
Event object¶
An event is something that has occurred at a certain moment in time,
such as the mouse being pressed down or a property changing its value.
In Flexx, events are represented with dictionary objects that
provide information about the event (such as what button was pressed,
or the old and new value of a property). A custom Dict
class is used that inherits from dict
but allows attribute access,
e.g. ev.button
as an alternative to ev['button']
.
Each event object has at least two attributes: source
,
a reference to the component object emitting the event, and type
, a string
indicating the type of the event.
The Component class¶
The Component
class provides a base
class for objects that have properties, actions, reactions and emitters.
You can create your own components like so:
class MyObject(flx.Component):
... # attributes/properties/actions/reactions/emitters go here
def init(self):
super().init()
...
It is common to implement the init()
method of the component class. It gets
automatically called by the component, at a moment when all properties have
been initialized, but no events have been emitted yet. This is a good time
to further initialize the component, and/or to instantiate sub components.
One rarely needs to implement the __init__()
method.
When the init()
is called, the component is the currently “active”
component, which can be used to e.g. describe a hierarchy of objects, as is
done with widgets. It also implies that mutations are allowed and that actions
on the component itself have a direct effect (invoking actions of other
components is still asynchronous though).
Let’s look at a real working widget example and break it down. It contains a property, an action, and a few reactions:
from flexx import flx
class Example(flx.Widget):
counter = flx.IntProp(3, settable=True)
def init(self):
super().init()
with flx.HBox():
self.but1 = flx.Button(text='reset')
self.but2 = flx.Button(text='increase')
self.label = flx.Label(text='', flex=1) # take all remaining space
@flx.action
def increase(self):
self._mutate_counter(self.counter + 1)
@flx.reaction('but1.pointer_click')
def but1_clicked(self, *events):
self.set_counter(0)
@flx.reaction('but2.pointer_click')
def but2_clicked(self, *events):
self.increase(0)
@flx.reaction
def update_label(self, *events):
self.label.set_text('count is ' + str(self.counter))
We will now take a closer look at properties and actions. Reactions are so cool that they’ve got their own chapter :)
Properties represent state¶
In the widget example above, we can see an int property. There are a handful
of different property types
. For example:
class MyObject(flx.Component):
foo = flx.AnyProp(8, settable=True, doc='can have any value')
bar = flx.IntProp()
Properties accept one positional arguments to set the default value. If not
given, a sensible default value is used that depends on the type of property.
Docs can be added using the doc
argument. Note that properties are
readonly: they can can only be mutated by actions. The foo
property
(as well as the counter
property) is marked as settable, which will
automatically create a set_foo()
action.
Property values can be initialized when a component is created (also non-settable properties):
c = MyComponent(foo=42)
One can also set the initial value of a property to a function object. This creates an auto-reaction that sets the property, and makes it possible to hook things up in a very concise manner. In the example below, the label text will be automatically updated when the username property changes:
flx.Label(flex=1, text=lambda: 'count is ' + str(self.counter))
An event is emitted every time that a property changes. This event has attributes
old_value
and new_value
(except for in-place array mutations, as
explained below). At initialization, a component sends out an event for each property,
in which old_value
and new_value
will be the same.
Attributes¶
Component classes can also have Attributes
,
which are read-only (usually static) non-observable values (e.g. JsComponent.id
).
Local properties¶
Regular methods of a JsComponent
are only available in JavaScript. On the
other hand, all properties are available on the proxy object as well. This may
not always be useful. It is possible to create properties that are local
to JavaScript (or to Python in a PyComponent
) using
LocalProperty
. An alternative may be to use
Attribute
; these are also local to JavaScript/Python.
Actions can mutate properties¶
In the widget example above, we can see the definition of the increase()
action.
Actions
are needed because they are the
only place where properties can be mutated.
class Example(flx.Widget):
counter = flx.IntProp(3, settable=True)
...
@flx.action
def increase(self):
self._mutate_counter(self.counter + 1)
You may wonder why the example’s reaction does not simply do self.set_counter(self.counter + 1)
.
The reason is that actions are asynchronous; invoking an action does not perform
it directly. Therefore invoking set_counter()
twice will simply apply the
last value. Note though, that when an action is called from another action, it
is performed directly.
Actions can have any number of (positional) arguments, and always
returns the component itself, which allows chaining action invocations,
e.g. t.scale(3).translate(3, 4)
.
Mutations are done via the _mutate
method,
or by the auto-generated _mutate_xx()
methods.
Mutations can only be done from an action. Trying
to do so otherwise will result in an error. This may seem limiting at first,
but it greatly helps keeping it easy to reason about information flowing
through your application, even as it scales.
Mutations to array-like properties¶
The above shows the simple and most common use of mutations. For
list properties
, mutations can also be done in-place:
from flexx import flx
class Example(flx.Widget):
items = flx.ListProp(settable=True)
def init(self):
super().init()
with flx.HBox():
self.but1 = flx.Button(text='reset')
self.but2 = flx.Button(text='add')
flx.Label(flex=1, wrap=2, text=lambda: repr(self.items))
@flx.action
def add_item(self, item):
self._mutate_items([item], 'insert', len(self.items))
@flx.reaction('but1.pointer_click')
def but1_clicked(self, *events):
self.set_items([])
@flx.reaction('but2.pointer_click')
def but2_clicked(self, *events):
self.add_item(int(time()))
This allows more fine-grained control over state updates, which can also be handled by reactions in much more efficient ways. The types of mutations are ‘set’ (the default), ‘insert’, ‘replace’, and ‘remove’. In the latter, the provided value is the number of elements to remove. For the others it must be a list of elements to set/insert/replace at the specified index.
Emitters create events¶
Emitters
make it easy to generate events.
Similar to actions, they are created with a decorator.
# Somewhere in the Flexx codebase:
class Widget(JsComponent):
...
@flx.emitter
def key_down(self, e):
""" Event emitted when a key is pressed down while this
widget has focus.
...
"""
return self._create_key_event(e)
Emitters can have any number of arguments and should return a dictionary, which will get emitted as an event, with the event type matching the name of the emitter.
Note that strictly speaking emitters are not necessary as
Component.emit()
can be used to generate an event. However, they provide a mechanism to
generate an event based on certain input data, and also document the
events that a component may emit.
Next¶
Reactions¶
Reactions
are used to react to events and
changes in properties, using an underlying handler function:
from flexx import flx
class Example(flx.Widget):
def init(self):
super().init()
with flx.VBox():
with flx.HBox():
self.firstname = flx.LineEdit(placeholder_text='First name')
self.lastname = flx.LineEdit(placeholder_text='Last name')
with flx.HBox():
self.but = flx.Button(text='Reset')
self.label = flx.Label(flex=1)
@flx.reaction('firstname.text', 'lastname.text')
def greet(self, *events):
self.label.set_text('hi ' + self.firstname.text + ' ' + self.lastname.text)
@flx.reaction('but.pointer_click')
def reset(self, *events):
self.label.set_text('')
This example demonstrates multiple concepts. Firstly, the reactions are
connected via connection-strings that specify the types of the event;
in this case the greet()
reaction is connected to “firstname.text”
and “lastname.text”, and reset()
is connected to the event-type
“pointer_click” event of the button. One can see how the
connection-string is a path, e.g. “sub.subsub.event_type”. This allows
for some powerful mechanics, as discussed in the section on dynamism.
One can also see that the reaction-function accepts *events
argument.
This is because reactions can be passed zero or more events. If a reaction
is called manually (e.g. ob.handle_foo()
) it will have zero events.
When called by the event system, it will have at least 1 event. When
e.g. a property is set twice, the function will be called
just once, but with multiple events. If all events need to be processed
individually, use:
@flx.reaction('foo')
def handler(self, *events):
for ev in events:
...
In most cases, you will connect to events that are known beforehand,
like those corresponding to properties and emitters.
If you connect to an event that is not known Flexx will display a warning.
Prepend an exclamation mark (e.g. '!foo'
) to suppress such warnings.
Greedy and automatic reactions¶
Each reaction operates in a certain “mode”. In mode “normal” (the default), the event system ensures that all events are handled in the order that they were emitted. This is often the most sensible approach, but this implies that a reaction can be called multiple times during a single event loop iteration, with other reactions called in between to ensure the consisten event order.
If it is preferred that all events targeted at a reaction are handled with a single call to that reaction, it can be set to mode “greedy”. Cases where this makes sense is when all related events must be processed simultenously, or simply when performance matters a lot and order matters less.
@flx.reaction('foo', mode='greedy')
def handler(self, *events):
...
Reactions with mode “auto” are automatically triggered when any of the
properties that the reaction uses is changed. Such reactions can be
created by specifying the mode
argument, or simply by creating a
reaction with zero connections strings. We refer to such reactions as
“auto reactions” or “implicit reactions”.
This is a very convenient feature, but it has more overhead than a normal reaction, and should therefore probably be avoided when a lot of properties are accessed, or when the used properties change very often. It’s hard to tell exactly when it starts to significantly hurt performance, but “often” is probably around hundreds and “often around 100 times per second. Just keep this in mind and do your own benchmarks when needed.
from flexx import flx
class Example(flx.Widget):
def init(self):
super().init()
with flx.VBox():
with flx.HBox():
self.slider1 = flx.Slider(flex=1)
self.slider2 = flx.Slider(flex=1)
self.label = flx.Label(flex=1)
@flx.reaction
def slders_combined(self):
self.label.set_text('{:.2f}'.format(self.slider1.value + self.slider2.value))
A similar useful feature is to assign a property (at initialization) using a function. In such a case, the function is turned into an implicit reaction. This can be convenient to easily connect different parts of an app.
from flexx import flx
class Example(flx.Widget):
def init(self):
super().init()
with flx.VBox():
with flx.HBox():
self.slider1 = flx.Slider(flex=1)
self.slider2 = flx.Slider(flex=1)
self.label = flx.Label(flex=1, text=lambda:'{:.2f}'.format(self.slider1.value * self.slider2.value))
Reacting to in-place mutations¶
In-place mutations to lists or arrays can be reacted to by processing the events one by one:
class MyComponent(flx.Component):
@flx.reaction('other.items')
def track_array(self, *events):
for ev in events:
if ev.mutation == 'set':
self.items[:] = ev.objects
elif ev.mutation == 'insert':
self.items[ev.index:ev.index] = ev.objects
elif ev.mutation == 'remove':
self.items[ev.index:ev.index+ev.objects] = [] # objects is int here
elif ev.mutation == 'replace':
self.items[ev.index:ev.index+len(ev.objects)] = ev.objects
else:
assert False, 'we cover all mutations'
For convenience, the mutation can also be “replicated” using the
flx.mutate_array()
and flx.mutate_dict()
functions.
Connection string labels¶
Connection strings can have labels to infuence the order by which reactions are called, and provide a means to disconnect specific (groups of) handlers at once.
class MyObject(flx.Component):
@flx.reaction('foo')
def given_foo_handler(*events):
...
@flx.reaction('foo:aa')
def my_foo_handler(*events):
# This one is called first: 'aa' < 'given_f...'
...
When an event is emitted, any connected reactions are scheduled in the order of a key, which is the label if present, and otherwise the name of the name of the reaction.
The label can also be used in the
disconnect()
method:
@h.reaction('foo:mylabel')
def handle_foo(*events):
...
...
h.disconnect('foo:mylabel') # don't need reference to handle_foo
Dynamism¶
Dynamism is a concept that allows one to connect to events for which the source can change. In the example below, we connect to the click event of a list of buttons, which keeps working even as that list changes.
from flexx import flx
class Example(flx.Widget):
def init(self):
super().init()
with flx.VBox():
with flx.HBox():
self.but = flx.Button(text='add')
self.label = flx.Label(flex=1)
with flx.HBox() as self.box:
flx.Button(text='x')
@flx.reaction('but.pointer_click')
def add_widget(self, *events):
flx.Button(parent=self.box, text='x')
@flx.reaction('box.children*.pointer_click')
def a_button_was_pressed(self, *events):
ev = events[-1] # only care about last event
self.label.set_text(ev.source.id + ' was pressed')
The a_button_was_pressed
gets invoked when any of the buttons inside
box
is clicked. When the box’s children changes, the reaction is
automatically reconnected. Note that in some cases you might also want
to connect to changes of the box.children
property itself.
The above works because box.children
is a property. The reaction
would still work if it would connect to widgets in a regular list, but
it would not be dynamic.
Implicit dynamism¶
Implicit reactions are also dynamic, maybe even more so! In the example below,
the reaction accesses the children
property, thus it will be called whenever
that property changes. It also connects to the visible
event of
all children, and to the foo
event of all children that are visible.
from flexx import flx
class Example(flx.Widget):
def init(self):
super().init()
with flx.VBox():
with flx.HBox():
self.but = flx.Button(text='add')
self.label = flx.Label(flex=1)
with flx.HBox() as self.box:
flx.CheckBox()
@flx.reaction('but.pointer_click')
def add_widget(self, *events):
flx.CheckBox(parent=self.box)
@flx.reaction
def a_button_was_pressed(self):
ids = []
for checkbox in self.box.children:
if checkbox.checked:
ids.append(checkbox.id)
self.label.set_text('checked: ' + ', '.join(ids))
This mechanism is powerful, but one can see how it can potentially access (and thus connect to) many properties, especially if the reaction calls other functions that access more properties. As mentioned before, keep in mind that implicit reactions have more overhead, which scales with the number of properties that are accessed.
Next¶
Next up: PScript, modules and scope.
PScript, modules, and scope¶
In this guide, we’ve already seen several examples where we write Python code that runs in JavaScript. This is done by transpiling the Python code to JavaScript using a tool called PScript, which is a spin-off of the Flexx project.
The compilation of Python code to JavaScript happens automatically, at the
moment that a JsComponent
gets defined (i.e. imported). Flexx is aware
of the classes that the browsers needs to know about and sends the corresponding
code when needed. Therefore it’s possible to dynamically import or
create new classes and use them.
PScript is almost Python¶
PScript is syntactically compatible with Python, so you can write it inside any Python module. PScript also feels a lot like Python, and it will probably get better in the future, but sometimes the JavaScript shines through. Thinks to watch out for:
- Accessing a nonexisting attribute will return
undefined
instead of raising an AttributeError. - Keys in a dictionary are implicitly converted to strings.
- Classes must start with a captial letter, functions must not. This is simply good practice in Python, but PScript needs it to tell classes apart from functions.
- A function can accept keyword arguments if it has a **kwargs parameter or named arguments after *args. Passing keywords to a function that does not handle keyword arguments might result in confusing errors.
Things you can do, which you cannot do in Python:
- Access elements in a dict as attributes (e.g. d.foo instead of d[“foo”]).
- Implicitly convert values to sting by adding them to a string.
- Divide by zero (results in inf).
Read more on http://pscript.readthedocs.io/.
Scope¶
In Flexx, it’s easy to possible to define PyComponents and JsComponents in the same module. For the purpose of clarity, it’s probably good to avoid this for larger applications.
Inside the methods of a JsComponent you can make use of plain Python functions and classes that are defined in the same module, provided that these (and their dependencies) can be transpiled by PScript. Similarly you can make use of objects defined or imported in the module. These can be integers, lists, dicts (and any combination thereof), as long as it can be JSON-serialized.
For every Python module that defines code that is used in JS, a corresponding JS module is created. Flexx detects what variable names are used in the JS code, but not declared in it, and tries to find the corresponding object in the module. You can even import functions/classes from other modules.
from flexx import flx
from foo import func1
def func2():
...
info = {'x': 1, 'y': 2}
class MyComponent(flx.JsComponent):
@flx.reaction('some.event')
def handler(self, *events):
func1(info)
func2()
In the code above, Flexx will include the definition of func2
and
info
in the same module that defines MyComponent
, and include
func1
in the JS module foo
. If MyComponent
would not use these
functions, neither definition would be included in the JavaScript module.
A useful feature is that the RawJS
class from PScript can be used
in modules to define objects in JS:
from flexx import flx
my_js_object = RawJS('window.something.get_some_object()')
class MyComponent(flx.JsComponent):
@flx.reaction('some.event')
def handler(self, *events):
my_js_object.bar()
One can also assign __pscript__ = True
to a module to make Flexx
transpile a module as a whole. A downside is that (at the moment) such
modules cannot use import.
Next¶
Next up: Handling assets and data.
Handling assets and data¶
Asset management¶
When writing code that relies on a certain JS or CSS library, that library can be loaded in the client by associating it with the module that needs it. Flexx will then automatically (and only) load it when code from that module is used in JS. Flexx itself uses this mechanism is some widgets e.g. for Leaflet maps or the CodeMirror editor.
# Associate asset
flx.assets.associate_asset(__name__, 'mydep.js', js_code)
# Sometimes a more lightweight *remote* asset is prefered
flx.assets.associate_asset(__name__, 'http://some.cdn/lib.css')
# Create component (or Widget) that needs the asset at the client
class MyComponent(flx.JsComponent):
....
It is also possible to provide assets that are not automatically loaded on the main app page, e.g. for sub-pages or web workers:
# Register asset
asset_url = flx.assets.add_shared_asset('mydep.js', js_code)
Data management¶
Data can be provided per session or shared between sessions:
# Add session-specific data. You need to call this inside a PyComponent
# and use the link in the JS component that needs the data.
link = my_component.session.add_data('some_name.png', binary_blob)
# Add shared data. This can be called at the module level.
link = flx.assets.add_shared_data('some_name.png', binary_blob)
Note that it is also possible to send data from Python to JS via an action invokation (the data is send over the websocket in this case). The latter also works for numpy arrays.
Next¶
Next up: Sensible usage patterns.
Sensible usage patterns¶
This chapter discusses some patterns that can be adopted to structure your applications. Which kind of pattern(s) make sense depends on the use-case and personal preference. Also, don’t be dogmatic, these are only intended to give a sensible direction.
The observer pattern¶
The idea of the observer pattern is that observers keep track of (the state of) other objects, and that these objects themselves are agnostic about what it’s tracked by. For example, in a music player, instead of writing code to update the window-title inside the function that starts a song, there would be a concept of a “current song”, and the window would listen for changes to the current song to update the title when it changes.
This is a pattern worth following in almost all sceneario’s. Flexx reaction system was designed to make this as natural as possible. This idea is also at the core of the next pattern.
Use of a central data store¶
When one part of your application needs to react to something in another part of your application, it is possible to create a reaction that connects to the event in question directly. However, as your app grows, this can start to feel like spaghetti.
It might be better to define a central place that represents the state of the application, e.g. on the root component, or on a separate object that can easily be accessed from the root component. In this way, all parts of your application stay self-contained and can be updated/replaced without the need for changes in other places of the application.
from flexx import flx
class UserInput(flx.Widget):
def init(self):
with flx.VBox():
self.edit = flx.LineEdit(placeholder_text='Your name')
flx.Widget(flex=1)
@flx.reaction('edit.user_done')
def update_user(self, *events):
self.root.store.set_username(self.edit.text)
class SomeInfoWidget(flx.Widget):
def init(self):
with flx.FormLayout():
flx.Label(title='name:', text=lambda: self.root.store.username)
flx.Widget(flex=1)
class Store(flx.JsComponent):
username = flx.StringProp(settable=True)
class Example(flx.Widget):
store = flx.ComponentProp()
def init(self):
# Create our store instance
self._mutate_store(Store())
# Imagine this being a large application with many sub-widgets,
# and the UserInput and SomeInfoWidget being used somewhere inside it.
with flx.HSplit():
UserInput()
flx.Widget(style='background:#eee;')
SomeInfoWidget()
Lean towards Python¶
If your application is a Python app that just happens to use Flexx instead
of Qt, you may try to stay in Python-land as much as possible by making
use of PyWidget
and PyComponent
.
We repeat the above example, but now most of the logic will happen in Python. (The result will be nearly the same, but if we’d display it on this page it would not be interactive, because there is no Python.)
from flexx import flx
class UserInput(flx.PyWidget):
def init(self):
with flx.VBox():
self.edit = flx.LineEdit(placeholder_text='Your name')
flx.Widget(flex=1)
@flx.reaction('edit.user_done')
def update_user(self, *events):
self.root.store.set_username(self.edit.text)
class SomeInfoWidget(flx.PyWidget):
def init(self):
with flx.FormLayout():
self.label = flx.Label(title='name:')
flx.Widget(flex=1)
@flx.reaction
def update_label(self):
self.label.set_text(self.root.store.username)
class Store(flx.PyComponent):
username = flx.StringProp(settable=True)
class Example(flx.PyWidget):
store = flx.ComponentProp()
def init(self):
# Create our store instance
self._mutate_store(Store())
# Imagine this being a large application with many sub-widgets,
# and the UserInput and SomeInfoWidget being used somewhere inside it.
with flx.HSplit():
UserInput()
flx.Widget(style='background:#eee;')
SomeInfoWidget()
Only JS¶
If you want to be able to publish your app to the web as a static page, you will need to base it completely on JsComponents.
For web apps that serve many users and/or is a long-running process, it is recommended to use Flexx to build the JS-only front-end, and implement the back-end using a classic http framework (such as aiohttp, flask, asgineer, etc.). The next chapter goes into detail how to do this.
Clear separation¶
If your use-case falls somewhere in between the two above patterns, you’ll use more of a mix of PyComponents and JsComponents.
In general, it is good to clearly separate the Python logic from the JavaScript logic. E.g. by implementing the whole UI using widgets and JsComponents, and implementing the “business logic” using one or more PyComponents.
Next¶
Next up: Different ways to run a Flexx app.
Ways to run a Flexx app¶
Run as a desktop app¶
During development, and when creating a web app, you will want to use
launch()
:
app = flx.App(MainComponent)
app.launch('app') # to run as a desktop app
# app.launch('browser') # to open in the browser
flx.run() # mainloop will exit when the app is closed
Flexx in Jupyter¶
Flexx can be used interactively from the Jupyter notebook.
Use flx.init_notebook()
which will inject the necessary JS and CSS.
Also use %gui asyncio
to enable the Flexx event system.
Simple widgets (e.g. buttons) will display just fine, but for other
widgets you might want to use SomeWidget(minsize=300)
to
specify a minimum size.
As of yet, Flexx does not work in JupyterLab.
Serve as a web app¶
It is possible to serve Flexx apps and allow multiple people to connect. Flexx provides ways to have all connected clients interact with each-other, see e.g. the chatroom and colab-painting examples.
app = flx.App(MainComponent)
app.serve('foo') # Serve at http://domain.com/foo
app.serve('') # Serve at http://domain.com/
flx.start() # Keep serving "forever"
Some details:
Each server process hosts on a single URL (domain+port), but can serve multiple applications (via different paths). Each process uses one tornado IOLoop, and exactly one Tornado Application object. Flexx’ event loop is based on asyncio (Tornado is set up to integrate with asyncio).
When a client connects to the server, it is served an HTML page, which contains the information needed to connect to a websocket. From there, all communication happens over this websocket, including the definition of CSS and JavaScript modules.
The overhead for each connection is larger than that of classic http frameworks, and the complexity of the Python-JS interaction are a potential risk for security issues and memory leaks. For the Flexx demo page we run the server in an auto-restarting Docker container with applied memory limits.
Export to a static web page¶
Any app can be exported to its raw assets. However, apps that rely on a PyComponent won’t work correctly, obviously. Exporting to a single file won’t work for apps that use session/shared data.
app = flx.App(MainComponent)
app.export('~/myapp/index.html') # creates a few files
app.export('~/myapp.html', link=0) # creates a single file
Serve as a proper web app¶
When creating an app that will run on a long-running server and/or will be accessed by many clients, you may want to implement the server using an http framework such as aiohttp, flask, etc.
You can first dump the assets to a dictionary, which you can then serve
using your library of choice. See the serve_with_
examples for
complete implementations.
It’s worth noting that if App.serve()
, flx.run()
and
flx.start()
are not called, Flexx won’t even import Tornado (and
we have a test to make sure that it stays that way). This makes it
feasible to generate the client-side of a website with Flexx when the
server starts up.
app = flx.App(MainComponent)
assets = app.dump('index.html')
... # serve assets with flask/aiohttp/tornado/vibora/django/...
Debugging¶
Debugging can be hard. Especially if your app runs partly in Python and partly in JavaScript. Here are some tips that may help.
Be clear about where the offending code is running¶
This may sound obvious, but it’s important to do this before moving on. Sometimes the bug presents itself due to the interaction between Python and JavaScript. The same rules apply, but you’d have to dig into both ends.
Digging in the Python side¶
All the normal Python debugging tips apply here. GUI applications run in
an event loop, which makes debugging harder. E.g. using breakpoints is not
always possible. A strategically placed print()
can sometimes help a lot.
It can be worthwhile to run the app in an IDE that can integrate the event loop, so that you can use a Python REPL to inspect an application while it is running. E.g. with Pyzo with asyncio GUI integration.
Digging in the JavaScript side¶
People unfamiliar with web technology might be hesitant to try and debug using the browser, but you’ll be amazed by how awesome the debugging tools of Firefox and Chrome are!
Firstly, hit the F12 key to pop up the developer console. From here, there are a few things that you can do:
You can run JavaScript commands to inspect and control your app. There
is a global flexx
object, and you can get access to all components
using flexx.s1.instances.xxx
. Use autocompletion to select the
component you need. You can inspect the component’s properties and
invoke its actions.
If the problem is related to appearance, you can activate the element selector and then click on the element on the page to select it. This will allow you to inspect the HTML DOM structure and inspect the CSS of all elements.
End¶
This concluses the Flexx guide. Have fun!
Reference¶
Widgets reference¶
This is a list of all widget classes provided by flexx.ui
. The Widget
class is the base class of all widgets.
Base widget:
Layouts:
FormLayout
GridLayout
HBox
HFix
HSplit
HVLayout
Layout
PinboardLayout
StackLayout
TabLayout
VBox
VFix
VSplit
Widgets:
BaseButton
BokehWidget
Button
CanvasWidget
CheckBox
ColorSelectWidget
ComboBox
DropdownContainer
FileBrowserWidget
GroupWidget
IFrame
ImageWidget
Label
LineEdit
MultiLineEdit
PlotWidget
PlotlyWidget
ProgressBar
PyWidget
RadioButton
RangeSlider
Slider
ToggleButton
TreeItem
TreeWidget
VideoWidget
Widget
YoutubeWidget
Widget¶
Provides the base Widget
and PyWidget
classes.
When subclassing a Widget to create a compound widget (i.e. a widget
that contains other widgets), initialize the child widgets inside the
init()
method. That method is called while the widget is the
current widget; any widgets instantiated inside it will automatically
become children.
from flexx import flx
class Example(flx.Widget):
def init(self):
super().init()
flx.Button(text='foo')
flx.Button(text='bar')
One can also use a widget as a context manager (i.e. using the with
statement) to create child widgets. This is particularly useful for
layout widgets (like HBox
).
from flexx import flx
class Example(flx.Widget):
def init(self):
super().init()
with flx.HBox():
flx.Button(flex=1, text='foo')
flx.Button(flex=2, text='bar')
In the above two examples, the newly created classes subclass from
Widget
and are thus a JsComponent
(i.e. operate in JS). This
may be what you want if you are aiming for a UI that can be exported
for the web. If, however, you are developing a desktop application,
consider subclassing from PyWidget
instead, which will make your
widget operate in Python.
It is also possible to create custom low-level widgets by implementing
_render_dom()
, resulting in a declarative “react-like” (but less
Pythonic) approach. It returns a virtual DOM that is used to update/replace
the real browser DOM.
from flexx import flx
class Example(flx.Widget):
count = flx.IntProp()
def _render_dom(self):
# This method automatically gets called when any of the used
# properties (only count, in this case) changes.
return flx.create_element('div', {},
flx.create_element('button',
{'onclick': self.increase_count},
'+'),
flx.create_element('span',
{'style.background': '#afa'},
str(self.count)),
)
@flx.action
def increase_count(self):
self._mutate_count(self.count + 1)
-
flexx.ui.
create_element
(type, props=None, *children)¶ Convenience function to create a dictionary to represent a virtual DOM node. Intended for use inside
Widget._render_dom()
.The content of the widget may be given as a series/list of child nodes (virtual or real), and strings. Strings are converted to text nodes. To insert raw HTML, use the
innerHTML
prop, but be careful not to include user-defined text, as this may introduce openings for XSS attacks.The returned dictionary has three fields: type, props, children.
-
class
flexx.ui.
PyWidget
(*args, **kwargs)¶ Inherits from:
PyComponent
A base class that can be used to create compound widgets that operate in Python. This enables an approach for building GUI’s in a Pythonic way: by only using JS components (actual widgets) all code that you write can be Python code.
Internally, objects of this class create a sub-widget (a
flx.Widget
instance). When the object is used as a context manager, the sub-widget will also become active. Further, this class gets attributes for all the sub-widget’s properties, actions, and emitters. In effect, this class can be used like a normalflx.Widget
(but in Python).
-
class
flexx.ui.
Widget
(*init_args, **kwargs)¶ Inherits from:
JsComponent
Base widget class (a
Component
in JS wrapping an HTML element).When subclassing a Widget, it is recommended to not implement the
__init__()
method, but instead implementinit()
for compound (higher-level) widgets, and_create_dom()
for low-level widgets.Widgets can be styled using CSS by implementing a string class attribute named
CSS
. A widget’s node has a CSS-class-name corresponding to its Python class (and its base classes), following the schemeflx-WidgetClassName
.All widgets have a
node
andouternode
attribute (only accessible in JavaScript), representing the DOM element(s) that represent the widget. For most types of widgets,node
is equal toouternode
. For theWidget
class, this is simply a <div> element. If you don’t understand what this is about, don’t worry; you won’t need it unless you are creating your own low-level widgets. See_create_dom()
for details.When implementing your own widget class, the class attribute
DEFAULT_MIN_SIZE
can be set to specify a sensible minimum size.properties: capture_mouse, children, container, css_class, flex, icon, maxsize, minsize, minsize_from_children, parent, size, tabindex, title
emitters: key_down, key_press, key_up, pointer_cancel, pointer_click, pointer_double_click, pointer_down, pointer_move, pointer_up, pointer_wheel
actions: apply_style, check_real_size, set_capture_mouse, set_container, set_css_class, set_flex, set_icon, set_maxsize, set_minsize, set_minsize_from_children, set_parent, set_tabindex, set_title
methods: _create_dom, _render_dom, dispose, init
-
_create_dom
()¶ Create DOM node(s) for this widget.
This method must return two (real or virtual) DOM nodes which will be available as
self.outernode
andself.node
respectively. If a single node is given, it is used for both values. These attributes must remain unchanged throughout the lifetime of a widget. This method can be overloaded in subclasses.Most widgets have the same value for
node
andouternode
. However, in some cases it helps to distinguish between the semantic “actual node” and a wrapper. E.g. Flexx uses it to properly layout theCanvasWidget
andTreeItem
. Internally, Flexx uses thenode
attribute for tab-index, and binding to mouse/touch/scroll/key events. If yourouternode
already semantically represents your widget, you should probably just use that.
-
_render_dom
()¶ Update the content of the DOM for this widget.
This method must return a DOM structure consisting of (a mix of) virtual nodes, real nodes and strings. The widget will use this structure to update the real DOM in a relatively efficient manner (new nodes are only (re)created if needed). The root element must match the type of this widget’s outernode. This method may also return a list to apply as the root node’s children.
Note that this method is called from an implicit reaction: it will auto-connect to any properties that are accessed. Combined with the above, this allows for a very declarative way to write widgets.
Virtual nodes are represented as dicts with fields “type”, “props” and “children”. Children is a list consisting of real dom nodes, virtual nodes, and strings. Strings are converted to TextNode (XSS safe). The
create_element()
function makes it easy to create virtual nodes.The default
_render_dom()
method simply places the outer node of the child widgets as the content of this DOM node, while preserving nodes that do not represent a widget. Overload as needed.
-
apply_style
(style)¶ action – Apply CSS style to this widget object. e.g.
"background: #f00; color: #0f0;"
. If the given value is a dict, its key-value pairs are converted to a CSS style string.Initial styling can also be given in a property-like fashion:
MyWidget(style='background:red;')
For static styling it is often better to define a CSS class attribute and/or use
css_class
.
-
capture_mouse
¶ IntProp – To what extend the mouse is “captured”.
- If 0, the mouse is not captured, and move events are only emitted when the mouse is pressed down (not recommended).
- If 1 (default) the mouse is captured when pressed down, so move and up events are received also when the mouse is outside the widget.
- If 2, move events are also emitted when the mouse is not pressed down and inside the widget.
-
check_real_size
()¶ action – Check whether the current size has changed. It should usually not be necessary to invoke this action, since a widget does so by itself, but it some situations the widget may not be aware of possible size changes.
-
children
¶ LocalProperty – The child widgets of this widget. This property is not settable and only present in JavaScript.
-
container
¶ StringProp – The id of the DOM element that contains this widget if parent is None. Use ‘body’ to make this widget the root.
-
css_class
¶ StringProp – The extra CSS class name to asign to the DOM element. Spaces can be used to delimit multiple names. Note that the DOM element already has a css class-name corresponding to its class (e.g. ‘flx-Widget) and all its superclasses.
-
dispose
()¶ Overloaded version of dispose() that disposes any child widgets.
-
flex
¶ FloatPairProp – How much space this widget takes (relative to the other widgets) when contained in a flexible layout such as HBox, HFix, HSplit or FormLayout. A flex of 0 means to take the minimum size. Flex is a two-element tuple, but both values can be specified at once by specifying a scalar.
-
icon
¶ LocalProperty – The icon for this widget. This is used is some widgets classes, and is used as the app’s icon if this is the main widget. It is settable from Python, but only present in JavaScript.
-
init
()¶ Overload this to initialize a custom widget. It’s preferred to use this instead of
__init__()
, because it gets called at a better moment in the instantiation of the widget.This method receives any positional arguments that were passed to the constructor. When called, this widget is the current parent.
-
key_down
(e)¶ emitter – Event emitted when a key is pressed down while this widget has focus. A key event has the following attributes:
- key: the character corresponding to the key being pressed, or
- a key name like “Escape”, “Alt”, “Enter”.
- modifiers: list of strings “Alt”, “Shift”, “Ctrl”, “Meta” for
- modifier keys pressed down at the time of the event.
A browser may associate certain actions with certain key presses. If this browser action is unwanted, it can be disabled by overloading this emitter:
@event.emitter def key_down(self, e): # Prevent browser's default reaction to function keys ev = super().key_press(e) if ev.key.startswith('F'): e.preventDefault() return ev
-
key_press
(e)¶ emitter – Event emitted when a key is released after pressing down, in theory. In contast to key_down, this event does not fire for the pressing of modifier keys, and some browsers will also not fire for the arrow keys, backspace, etc. See key_down for details.
-
key_up
(e)¶ emitter – Event emitted when a key is released while this widget has focus. See key_down for details.
-
maxsize
¶ FloatPairProp – The user-defined maximum size (width, height) of this widget in pixels. Note that using “max-width” or “max-height” in
apply_style()
. (and in thestyle
kwarg) also set this property. Maximum sizes set in CSS are ignored.
-
minsize
¶ FloatPairProp – The user-defined minimum size (width, height) of this widget in pixels. The default value differs per widget (
Widget.DEFAULT_MIN_SIZE
). Note that using “min-width” or “min-height” inapply_style()
. (and in thestyle
kwarg) also set this property. Minimum sizes set in CSS are ignored.
-
minsize_from_children
¶ BoolProp – Whether the children are taken into account to calculate this widget’s size constraints. Default True: both the
minsize
of this widget and the size constraints of its children (plus spacing and padding for layout widgets) are used to calculate the size constraints for this widget.Set to False to prevent the content in this widget to affect the parent’s layout, e.g. to allow fully collapsing this widget when the parent is a splitter. If this widget has a lot of content, you may want to combine with
style='overflow-y: auto'
.
-
parent
¶ ComponentProp – The parent widget, or None if it has no parent. Setting this property will update the “children” property of the old and new parent.
-
pointer_cancel
(e)¶ emitter – Event emitted when the mouse/touch is lost, e.g. the window becomes inactive during a drag. This only seem to work well for touch events in most browsers.
See pointer_down() for a description of the event object.
-
pointer_click
(e)¶ emitter – Event emitted when mouse-button/touchpad/screen is clicked.
See pointer_down() for a description of the event object.
-
pointer_double_click
(e)¶ emitter – Event emitted when mouse-button/touchpad/screen is double-clicked.
See pointer_down() for a description of the event object.
-
pointer_down
(e)¶ emitter – Event emitted when mouse-button/touchpad/screen is pressed.
All pointer events have the following attributes:
- pos: the pointer position, in pixels, relative to this widget
- page_pos: the pointer position relative to the page
- button: what mouse button the event is about, 1, 2, 3 are left, right,
- middle, respectively. 0 indicates no button.
- buttons: what buttons were pressed at the time of the event.
- modifiers: list of strings “Alt”, “Shift”, “Ctrl”, “Meta” for
- modifier keys pressed down at the time of the event.
- touches: a dictionary that maps touch_id’s to (x, y, force) tuples.
- For mouse events touch_id is -1 and force is 1.
A note about the relation with JavaScript events: although the name might suggest that this makes use of JS pointer events, this is not the case; Flexx captures both mouse events and touch events and exposes both as its own “pointer event”. In effect, it works better on mobile devices, and has multi-touch support.
-
pointer_move
(e)¶ emitter – Event fired when the mouse or a touch is moved.
See pointer_down for details.
-
pointer_up
(e)¶ emitter – Event emitted when mouse-button/touchpad/screen is released.
See pointer_down() for a description of the event object.
-
pointer_wheel
(e)¶ emitter – Event emitted when the mouse wheel is used.
See pointer_down() for a description of the event object. Additional event attributes:
- hscroll: amount of scrolling in horizontal direction
- vscroll: amount of scrolling in vertical direction
-
set_capture_mouse
(*val)¶ action – Setter for the ‘capture_mouse’ property.
-
set_container
(*val)¶ action – Setter for the ‘container’ property.
-
set_css_class
(*val)¶ action – Setter for the ‘css_class’ property.
-
set_flex
(*val)¶ action – Setter for the ‘flex’ property.
-
set_icon
(val)¶ action – Set the icon for this widget. This is used is some widgets classes, and is used as the app’s icon if this is the main widget. It is settable from Python, but the property is not available in Python.
Can be a url, a relative url to a shared asset, or a base64 encoded image. In the future this may also support names in icon packs like FontAwesome.
-
set_maxsize
(*val)¶ action – Setter for the ‘maxsize’ property.
-
set_minsize
(*val)¶ action – Setter for the ‘minsize’ property.
-
set_minsize_from_children
(*val)¶ action – Setter for the ‘minsize_from_children’ property.
-
set_parent
(parent, pos=None)¶ action – Set the parent widget (can be None). This action also mutates the childen of the old and new parent.
-
set_tabindex
(*val)¶ action – Setter for the ‘tabindex’ property.
-
set_title
(*val)¶ action – Setter for the ‘title’ property.
-
size
¶ FloatPairProp – The actual size of the widget (readonly). Flexx tries to keep this value up-to-date, but in e.g. a box layout, a change in a Button’s text can change the size of sibling widgets.
-
tabindex
¶ IntProp – The index used to determine widget order when the user iterates through the widgets using tab. This also determines whether a widget is able to receive key events. Flexx automatically sets this property when it should emit key events. Effect of possible values on underlying DOM element:
- -2: element cannot have focus unless its a special element like
- a link or form control (default).
- -1: element can have focus, but is not reachable via tab.
- 0: element can have focus, and is reachable via tab in the order
- at which the element is defined.
- 1 and up: element can have focus, and the tab-order is determined
- by the value of tabindex.
-
title
¶ StringProp – The string title of this widget. This is used to mark the widget in e.g. a tab layout or form layout, and is used as the app’s title if this is the main widget.
-
FormLayout¶
Layout a series of (input) widgets in a form. Example:
from flexx import ui
class Example(ui.Widget):
def init(self):
with ui.FormLayout():
self.b1 = ui.LineEdit(title='Name:')
self.b2 = ui.LineEdit(title="Age:")
self.b3 = ui.LineEdit(title="Favorite color:")
ui.Widget(flex=1) # Spacing
Also see examples: themed_form.py.
-
class
flexx.ui.
FormLayout
(*init_args, **kwargs)¶ Inherits from:
Layout
A layout widget that vertically alligns its child widgets in a form. A label is placed to the left of each widget (based on the widget’s title).
The
node
of this widget is a <div>, which lays out it’s child widgets and their labels using CSS grid.-
JS
¶ alias of
FormLayout
-
Grid layout.¶
Layout a series of widgets in a grid. The grid has a specified number of columns. Example:
from flexx import flx
class Example(flx.Widget):
def init(self):
with flx.HSplit():
with flx.GridLayout(ncolumns=3):
flx.Button(text='A')
flx.Button(text='B')
flx.Button(text='C')
flx.Button(text='D')
flx.Button(text='E')
flx.Button(text='F')
with flx.GridLayout(ncolumns=2):
flx.Button(text='A', flex=(1, 1)) # Set flex for 1st row and col
flx.Button(text='B', flex=(2, 1)) # Set flex for 2nd col
flx.Button(text='C', flex=(1, 1)) # Set flex for 2nd row
flx.Button(text='D')
flx.Button(text='E', flex=(1, 2)) # Set flex for 3d row
flx.Button(text='F')
-
class
flexx.ui.
GridLayout
(*init_args, **kwargs)¶ Inherits from:
Layout
A layout widget that places its children in a grid with a certain number of columns. The flex values of the children in the first row determine the sizing of the columns. The flex values of the first child of each row determine the sizing of the rows.
The
node
of this widget is a <div>, which lays out it’s child widgets and their labels using CSS grid.properties: ncolumns
actions: set_ncolumns
-
ncolumns
¶ IntProp – The number of columns of the grid.
-
set_ncolumns
(*val)¶ action – Setter for the ‘ncolumns’ property.
-
HVLayout¶
The HVLayout and its subclasses provide a simple mechanism to horizontally or vertically stack child widgets. This can be done in different modes: box mode is suited for aligning content where natural size matters. The fix mode and split mode are more suited for high-level layout. See the HVLayout class for details.
Interactive Box layout example:
from flexx import app, event, ui
class Example(ui.HBox):
def init(self):
self.b1 = ui.Button(text='Horizontal', flex=0)
self.b2 = ui.Button(text='Vertical', flex=1)
self.b3 = ui.Button(text='Horizontal reversed', flex=2)
self.b4 = ui.Button(text='Vertical reversed', flex=3)
@event.reaction('b1.pointer_down')
def _to_horizontal(self, *events):
self.set_orientation('h')
@event.reaction('b2.pointer_down')
def _to_vertical(self, *events):
self.set_orientation('v')
@event.reaction('b3.pointer_down')
def _to_horizontal_rev(self, *events):
self.set_orientation('hr')
@event.reaction('b4.pointer_down')
def _to_vertical_r(self, *events):
self.set_orientation('vr')
Also see examples: app_layout.py, splitters.py, box_vs_fix_layout.py, mondriaan.py.
-
class
flexx.ui.
HVLayout
(*init_args, **kwargs)¶ Inherits from:
Layout
A layout widget to distribute child widgets horizontally or vertically.
This is a versatile layout class which can operate in different orientations (horizontal, vertical, reversed), and in different modes:
In ‘fix’ mode, all available space is simply distributed corresponding to the children’s flex values. This can be convenient to e.g. split a layout in two halves.
In ‘box’ mode, each widget gets at least its natural size (if available), and any additional space is distributed corresponding to the children’s flex values. This is convenient for low-level layout of widgets, e.g. to align one or more buttons. It is common to use flex values of zero to give widgets just the size that they needs and use an empty widget with a flex of 1 to fill up any remaining space. This mode is based on CSS flexbox.
In ‘split’ mode, all available space is initially distributed corresponding to the children’s flex values. The splitters between the child widgets can be dragged by the user and positioned via an action. This is useful to give the user more control over the (high-level) layout.
In all modes, the layout is constrained by the minimum and maximum size of the child widgets (as set via style/CSS). Note that flexbox (and thus box mode) may not honour min/max sizes of widgets in child layouts.
Note that widgets with a flex value of zero may collapse if used inside a fix/split layout, or in a box layout but lacking a natural size. This can be resolved by assigning a minimum width/height to the widget. The exception is if all child widgets have a flex value of zero, in which case the available space is divided equally.
The
node
of this widget is a <div>. The outer nodes of the child widgets are layed-out using JavaScript of CSS, depending on the mode.Also see the convenience classes: HFix, VFix, HBox, VBox, HSplit, VSplit.
properties: mode, orientation, padding, spacing, splitter_positions
emitters: user_splitter_positions
actions: set_from_flex_values, set_mode, set_orientation, set_padding, set_spacing, set_splitter_positions
-
mode
¶ EnumProp – The mode in which this layout operates:
- ‘BOX’: (default) each widget gets at least its natural size, and additional space is distributed corresponding to the flex values.
- ‘FIX’: all available space is distributed corresponding to the flex values.
- ‘SPLIT’: available space is initially distributed correspondong to the flex values, and can be modified by the user by dragging the splitters.
-
orientation
¶ OrientationProp – The orientation of the child widgets. ‘h’ or ‘v’ for horizontal and vertical, or their reversed variants ‘hr’ and ‘vr’. Settable with values: 0, 1, ‘h’, ‘v’, ‘hr’, ‘vr’, ‘horizontal’, ‘vertical’, ‘left-to-right’, ‘right-to-left’, ‘top-to-bottom’, ‘bottom-to-top’ (insensitive to case and use of dashes).
-
padding
¶ FloatProp – The empty space around the layout (in pixels).
-
set_from_flex_values
()¶ action – Set the divider positions corresponding to the children’s flex values. Only has a visual effect in split-mode.
-
set_mode
(*val)¶ action – Setter for the ‘mode’ property.
-
set_orientation
(*val)¶ action – Setter for the ‘orientation’ property.
-
set_padding
(*val)¶ action – Setter for the ‘padding’ property.
-
set_spacing
(*val)¶ action – Setter for the ‘spacing’ property.
-
set_splitter_positions
(*positions)¶ action – Set relative splitter posisions (None or values between 0 and 1). Only usable in split-mode.
-
spacing
¶ FloatProp – The space between two child elements (in pixels).
-
splitter_positions
¶ LocalProperty – The preferred relative positions of the splitters. The actual positions are subject to minsize and maxsize constraints (and natural sizes for box-mode).
-
user_splitter_positions
(*positions)¶ emitter – Event emitted when the splitter is positioned by the user. The event has a
positions
attribute.
-
-
class
flexx.ui.
HBox
(*init_args, **kwargs)¶ Inherits from:
HVLayout
Horizontal layout that tries to give each widget its natural size and distributes any remaining space corresponding to the widget’s flex values. (I.e. an HVLayout with orientation ‘h’ and mode ‘box’.)
-
class
flexx.ui.
HFix
(*init_args, **kwargs)¶ Inherits from:
HVLayout
Horizontal layout that distributes the available space corresponding to the widget’s flex values. (I.e. an HVLayout with orientation ‘h’ and mode ‘fix’.)
-
class
flexx.ui.
HSplit
(*init_args, **kwargs)¶ Inherits from:
HVLayout
Horizontal layout that initially distributes the available space corresponding to the widget’s flex values, and has draggable splitters. By default, this layout has a slightly larger spacing between the widgets. (I.e. an HVLayout with orientation ‘h’ and mode ‘split’.)
-
class
flexx.ui.
VBox
(*init_args, **kwargs)¶ Inherits from:
HVLayout
Vertical layout that tries to give each widget its natural size and distributes any remaining space corresponding to the widget’s flex values. (I.e. an HVLayout with orientation ‘v’ and mode ‘box’.)
-
class
flexx.ui.
VFix
(*init_args, **kwargs)¶ Inherits from:
HVLayout
Vertical layout that distributes the available space corresponding to the widget’s flex values. (I.e. an HVLayout with orientation ‘v’ and mode ‘fix’.)
-
class
flexx.ui.
VSplit
(*init_args, **kwargs)¶ Inherits from:
HVLayout
Vertical layout that initially distributes the available space corresponding to the widget’s flex values, and has draggable splitters. By default, this layout has a slightly larger spacing between the widgets. (I.e. an HVLayout with orientation ‘v’ and mode ‘split’.)
Layout¶
PinboardLayout¶
Free positioning (absolute or relative) of child widgets. Example:
from flexx import app, ui
class Example(ui.Widget):
def init(self):
with ui.PinboardLayout():
self.b1 = ui.Button(text='Stuck at (20, 20)',
style='left:20px; top:20px;')
self.b2 = ui.Button(text='Dynamic at (30%, 30%)',
style='left:30%; top:30%; height:100px;')
self.b3 = ui.Button(text='Dynamic at (50%, 70%)',
style='left:50%; top:70%;')
-
class
flexx.ui.
PinboardLayout
(*init_args, **kwargs)¶ Inherits from:
Layout
Unconstrained absolute and relative positioning of child widgets.
This simply places child widgets using CSS “position: absolute”. Use CSS “left” and “top” to position the widget (using a “px” or “%” suffix). Optionally “width”, “height”, “right” and “bottom” can also be used.
The
node
of this widget is a <div>.-
JS
¶ alias of
PinboardLayout
-
StackLayout¶
Show only one child at any time. Example:
from flexx import app, event, ui
class Example(ui.Widget):
def init(self):
with ui.HBox():
with ui.VBox():
self.buta = ui.Button(text='red')
self.butb = ui.Button(text='green')
self.butc = ui.Button(text='blue')
ui.Widget(flex=1) # space filler
with ui.StackLayout(flex=1) as self.stack:
self.buta.w = ui.Widget(style='background:#a00;')
self.butb.w = ui.Widget(style='background:#0a0;')
self.butc.w = ui.Widget(style='background:#00a;')
@event.reaction('buta.pointer_down', 'butb.pointer_down', 'butc.pointer_down')
def _stacked_current(self, *events):
button = events[-1].source
self.stack.set_current(button.w)
-
class
flexx.ui.
StackLayout
(*init_args, **kwargs)¶ Inherits from:
Layout
A layout widget which shows only one of its children at a time.
The
node
of this widget is a <div>.properties: current
actions: set_current
-
current
¶ ComponentProp – The currently shown widget (or None).
-
set_current
(current)¶ action – Setter for current widget. Can also set using an integer index.
-
TabLayout¶
A StackLayout
subclass that uses tabs to let the user select a child widget.
Example:
from flexx import app, ui
class Example(ui.Widget):
def init(self):
with ui.TabLayout() as self.t:
self.a = ui.Widget(title='red', style='background:#a00;')
self.b = ui.Widget(title='green', style='background:#0a0;')
self.c = ui.Widget(title='blue', style='background:#00a;')
Also see examples: demo.py.
-
class
flexx.ui.
TabLayout
(*init_args, **kwargs)¶ Inherits from:
StackLayout
A StackLayout which provides a tabbar for selecting the current widget. The title of each child widget is used for the tab label.
The
node
of this widget is a <div>. The visible child widget fills the entire area of this element, except for a small area at the top where the tab-bar is shown.emitters: user_current
-
user_current
(current)¶ emitter – Event emitted when the user selects a tab. Can be used to distinguish user-invoked from programatically-invoked tab changes. Has
old_value
andnew_value
attributes.
-
Filebrowser¶
-
class
flexx.ui.
FileBrowserWidget
(*args, **kwargs)¶ Inherits from:
PyWidget
A PyWidget to browse the file system. Experimental. This could be the basis for a file open/save dialog.
properties: path
-
path
¶ StringProp – The currectly shown directory (settable). Defaults to the user directory.
-
selected
¶ emitter – Emitter that fires when the user selects a file. The emitted event has a “filename” attribute.
-
set_path
¶ action – Set the current path. If an invalid directory is given, the path is not changed. The given path can be absolute, or relative to the current path.
-
BokehWidget¶
Show Bokeh plots in Flexx. Example:
import numpy as np
from bokeh.plotting import figure
from flexx import app, event, ui
x = np.linspace(0, 6, 50)
p1 = figure()
p1.line(x, np.sin(x))
p2 = figure()
p2.line(x, np.cos(x))
class Example(app.PyComponent):
def init(self):
with ui.HSplit():
ui.BokehWidget.from_plot(p1)
ui.BokehWidget.from_plot(p2)
Also see examples: bokehdemo.py.
-
class
flexx.ui.
BokehWidget
(*init_args, **kwargs)¶ Inherits from:
Widget
A widget that shows a Bokeh plot object.
For Bokeh 0.12 and up. The plot’s
sizing_mode
property is set tostretch_both
unless it was set to something other thanfixed
. Other responsive modes are ‘scale_width’, ‘scale_height’ and ‘scale_both`, which all keep aspect ratio while being responsive in a certain direction.This widget is, like all widgets, a JsComponent; it lives in the browser, while the Bokeh plot is a Python object. Therefore we cannot simply use a property to set the plot. Use
ui.BokehWidget.from_plot(plot)
to instantiate the widget from Python.attributes: plot
actions: set_plot_components
-
plot
¶ attribute – The JS-side of the Bokeh plot object.
-
set_plot_components
(d)¶ action – Set the plot using its script/html components.
-
Button classes¶
Simple example:
b = ui.Button(text="Push me")
Also see examples: buttons.py.
-
class
flexx.ui.
BaseButton
(*init_args, **kwargs)¶ Inherits from:
Widget
Abstract button class.
properties: checked, disabled, text
emitters: user_checked
actions: set_checked, set_disabled, set_text
-
checked
¶ BoolProp – Whether the button is checked.
-
disabled
¶ BoolProp – Whether the button is disabled.
-
set_checked
(*val)¶ action – Setter for the ‘checked’ property.
-
set_disabled
(*val)¶ action – Setter for the ‘disabled’ property.
-
set_text
(*val)¶ action – Setter for the ‘text’ property.
-
text
¶ StringProp – The text on the button.
-
user_checked
(checked)¶ emitter – Event emitted when the user (un)checks this button. Has
old_value
andnew_value
attributes.
-
-
class
flexx.ui.
Button
(*init_args, **kwargs)¶ Inherits from:
BaseButton
A push button.
The
node
of this widget is a <button>.
-
class
flexx.ui.
CheckBox
(*init_args, **kwargs)¶ Inherits from:
BaseButton
A checkbox button.
The
outernode
of this widget is a <label>, and thenode
a checkbox <input>.
-
class
flexx.ui.
RadioButton
(*init_args, **kwargs)¶ Inherits from:
BaseButton
A radio button. Of any group of radio buttons that share the same parent, only one can be active.
The
outernode
of this widget is a <label>, and thenode
a radio <input>.emitters: pointer_click
-
pointer_click
(e)¶ emitter – This method is called on JS a click event. We first update the checked properties, and then emit the Flexx click event. That way, one can connect to the click event and have an up-to-date checked props (even on Py).
-
-
class
flexx.ui.
ToggleButton
(*init_args, **kwargs)¶ Inherits from:
BaseButton
A button that can be toggled. It behaves like a checkbox, while looking more like a regular button.
The
node
of this widget is a <button>.-
JS
¶ alias of
ToggleButton
-
CanvasWidget¶
The canvas can be used for specialized graphics of many sorts. It can provide either a WebGL context or a 2d context as in the example below:
from flexx import app, event, ui
class Example(ui.CanvasWidget):
def init(self):
super().init()
self.ctx = self.node.getContext('2d')
self.set_capture_mouse(1)
self._last_pos = (0, 0)
@event.reaction('pointer_move')
def on_move(self, *events):
for ev in events:
self.ctx.beginPath()
self.ctx.strokeStyle = '#080'
self.ctx.lineWidth = 3
self.ctx.lineCap = 'round'
self.ctx.moveTo(*self._last_pos)
self.ctx.lineTo(*ev.pos)
self.ctx.stroke()
self._last_pos = ev.pos
@event.reaction('pointer_down')
def on_down(self, *events):
self._last_pos = events[-1].pos
Also see example: drawing.py, splines.py.
-
class
flexx.ui.
CanvasWidget
(*init_args, **kwargs)¶ Inherits from:
Widget
A widget that provides an HTML5 canvas. The canvas is scaled with the available space. Use
self.node.getContext('2d')
orself.node.getContext('webgl')
in theinit()
method to get a contex to perform the actual drawing.The
node
of this widget is a <canvas> wrapped in a <div> (theouternode
) to handle sizing.properties: capture_wheel
actions: set_capture_wheel
-
capture_wheel
¶ BoolProp – Whether the wheel event is “captured”, i.e. not propagated to result into scrolling of the parent widget (or page). If True, if no scrolling must have been performed outside of the widget for about half a second in order for the widget to capture scroll events.
-
set_capture_wheel
(*val)¶ action – Setter for the ‘capture_wheel’ property.
-
ColorSelectWidget¶
from flexx import event, ui
class Example(ui.Widget):
def init(self):
self.c = ui.ColorSelectWidget()
@event.reaction
def _color_changed(self):
self.node.style.background = self.c.color.hex
-
class
flexx.ui.
ColorSelectWidget
(*init_args, **kwargs)¶ Inherits from:
Widget
A widget used to select a color.
The
node
of this widget is an <input> element of typecolor
. This is supported at least on Firefox and Chrome, but not on IE.emitters: user_color
actions: set_color, set_disabled
-
color
¶ ColorProp – The currently selected color.
-
disabled
¶ BoolProp – Whether the color select is disabled.
-
set_color
(*val)¶ action – Setter for the ‘color’ property.
-
set_disabled
(*val)¶ action – Setter for the ‘disabled’ property.
-
user_color
(color)¶ emitter – Event emitted when the user changes the color. Has
old_value
andnew_value
attributes.
-
Dropdown widgets¶
from flexx import app, event, ui
class Example(ui.Widget):
def init(self):
self.combo = ui.ComboBox(editable=True,
options=('foo', 'bar', 'spaaaaaaaaam', 'eggs'))
self.label = ui.Label()
@event.reaction
def update_label(self):
text = 'Combobox text: ' + self.combo.text
if self.combo.selected_index is not None:
text += ' (index %i)' % self.combo.selected_index
self.label.set_text(text)
Also see examples: control_with_keys.py.
-
class
flexx.ui.widgets._dropdown.
BaseDropdown
(*init_args, **kwargs)¶ Inherits from:
Widget
Base class for drop-down-like widgets.
actions: expand
-
expand
()¶ action – Expand the dropdown and give it focus, so that it can be used with the up/down keys.
-
-
class
flexx.ui.
ComboBox
(*init_args, **kwargs)¶ Inherits from:
BaseDropdown
The Combobox is a combination of a button and a popup list, optionally with an editable text. It can be used to select among a set of options in a more compact manner than a TreeWidget would. Optionally, the text of the combobox can be edited.
It is generally good practive to react to
user_selected
to detect user interaction, and react totext
,selected_key
orselected_index
to keep track of all kinds of (incl. programatic) interaction .When the combobox is expanded, the arrow keys can be used to select an item, and it can be made current by pressing Enter or spacebar. Escape can be used to collapse the combobox.
The
node
of this widget is a <span> with some child elements and quite a bit of CSS for rendering.properties: editable, options, placeholder_text, selected_index, selected_key, text
emitters: user_selected
actions: set_editable, set_options, set_placeholder_text, set_selected_index, set_selected_key, set_text, update_selected_index
-
editable
¶ BoolProp – Whether the combobox’s text is editable.
-
options
¶ TupleProp – A list of tuples (key, text) representing the options. Both keys and texts are converted to strings if they are not already. For items that are given as a string, the key and text are the same. If a dict is given, it is transformed to key-text pairs.
-
placeholder_text
¶ StringProp – The placeholder text to display in editable mode.
-
selected_index
¶ IntProp – The currently selected item index. Can be -1 if no item has been selected or when the text was changed manually (if editable). Can also be programatically set.
-
selected_key
¶ StringProp – The currently selected item key. Can be ‘’ if no item has been selected or when the text was changed manually (if editable). Can also be programatically set.
-
set_editable
(*val)¶ action – Setter for the ‘editable’ property.
-
set_options
(options)¶ action – set_options
-
set_placeholder_text
(*val)¶ action – Setter for the ‘placeholder_text’ property.
-
set_selected_index
(index)¶ action – set_selected_index
-
set_selected_key
(key)¶ action – set_selected_key
-
set_text
(*val)¶ action – Setter for the ‘text’ property.
-
text
¶ StringProp – The text displayed on the widget. This property is set when an item is selected from the dropdown menu. When editable, the
text
is also set when the text is edited by the user. This property is settable programatically regardless of the value ofeditable
.
-
update_selected_index
(text)¶ action – update_selected_index
-
user_selected
(index)¶ emitter – Event emitted when the user selects an item using the mouse or keyboard. The event has attributes
index
,key
andtext
.
-
-
class
flexx.ui.
DropdownContainer
(*init_args, **kwargs)¶ Inherits from:
BaseDropdown
A dropdown widget that shows its children when expanded. This can be used to e.g. make a collapsable tree widget. Some styling may be required for the child widget to be sized appropriately.
Note: This widget is currently broken, because pointer events do not work in the contained widget (at least on Firefox).
properties: text
actions: set_text
-
set_text
(*val)¶ action – Setter for the ‘text’ property.
-
text
¶ StringProp – The text displayed on the dropdown widget.
-
GroupWidget¶
Visually group a collection of input widgets. Example:
from flexx import app, event, ui
class Example(ui.GroupWidget):
def init(self):
self.set_title('A silly panel')
with ui.VBox():
self.progress = ui.ProgressBar(min=0, max=9,
text='Clicked {value} times')
self.but = ui.Button(text='click me')
@event.reaction('but.pointer_down')
def _button_pressed(self, *events):
self.progress.set_value(self.progress.value + 1)
-
class
flexx.ui.
GroupWidget
(*init_args, **kwargs)¶ Inherits from:
Widget
Widget to collect widgets in a named group. It does not provide a layout. This is similar to a QGroupBox or an HTML fieldset.
The
node
of this widget is a <fieldset>.-
JS
¶ alias of
GroupWidget
-
IFrame¶
with ui.HSplit():
ui.IFrame(url='bsdf.io')
ui.IFrame(url='http://flexx.readthedocs.io')
# Note: the rtd page does not seem to load on Firefox 57.04
-
class
flexx.ui.
IFrame
(*init_args, **kwargs)¶ Inherits from:
Widget
An iframe element, i.e. a container to show web-content. Note that some websites do not allow themselves to be rendered in a cross-source iframe.
The
node
of this widget is a <iframe>.properties: url
actions: set_url
-
set_url
(*val)¶ action – Setter for the ‘url’ property.
-
Label¶
from flexx import app, event, ui
class Example(ui.Widget):
def init(self):
with ui.HBox():
self.but = ui.Button(text='Push me')
self.label = ui.Label(flex=1, wrap=True, text='This is a label. ')
@event.reaction('but.pointer_down')
def _add_label_text(self, *events):
self.label.set_text(self.label.text + 'Yes it is. ')
-
class
flexx.ui.
Label
(*init_args, **kwargs)¶ Inherits from:
Widget
Widget to show text/html.
The
node
of this widget is a <div> with CSSword-wrap
andwhite-space
set appropriately.actions: set_html, set_text, set_wrap
-
html
¶ StringProp – The html shown in the label.
Warning: there is a risk of introducing openings for XSS attacks when html is introduced that you do not control (e.g. from user input).
-
set_html
(html)¶ action – Setter for the html property. Use with care.
-
set_text
(text)¶ action – Setter for the text property.
-
set_wrap
(*val)¶ action – Setter for the ‘wrap’ property.
-
text
¶ StringProp – The text shown in the label (HTML is shown verbatim).
-
wrap
¶ IntProp – Whether the content is allowed to be wrapped on multiple lines. Set to 0/False for no wrap (default), 1/True for word-wrap, 2 for character wrap.
-
Lineedit¶
The LineEdit
and MultiLineEdit
widgets provide a way for the user
to input text.
from flexx import app, event, ui
class Example(ui.Widget):
def init(self):
with ui.VBox():
self.line = ui.LineEdit(placeholder_text='type here')
self.l1 = ui.Label(html='<i>when user changes text</i>')
self.l2 = ui.Label(html='<i>when unfocusing or hitting enter </i>')
self.l3 = ui.Label(html='<i>when submitting (hitting enter)</i>')
ui.Widget(flex=1)
@event.reaction('line.user_text')
def when_user_changes_text(self, *events):
self.l1.set_text('user_text: ' + self.line.text)
@event.reaction('line.user_done')
def when_user_is_done_changing_text(self, *events):
self.l2.set_text('user_done: ' + self.line.text)
@event.reaction('line.submit')
def when_user_submits_text(self, *events):
self.l3.set_text('submit: ' + self.line.text)
-
class
flexx.ui.
LineEdit
(*init_args, **kwargs)¶ Inherits from:
Widget
An input widget to edit a line of text.
The
node
of this widget is a text <input>.properties: autocomp, disabled, password_mode, placeholder_text, text
emitters: submit, user_done, user_text
actions: set_autocomp, set_disabled, set_password_mode, set_placeholder_text, set_text
-
autocomp
¶ TupleProp – A tuple/list of strings for autocompletion. Might not work in all browsers.
-
disabled
¶ BoolProp – Whether the line edit is disabled.
-
password_mode
¶ BoolProp – Whether the insered text should be hidden.
-
placeholder_text
¶ StringProp – The placeholder text (shown when the text is an empty string).
-
set_autocomp
(*val)¶ action – Setter for the ‘autocomp’ property.
-
set_disabled
(*val)¶ action – Setter for the ‘disabled’ property.
-
set_password_mode
(*val)¶ action – Setter for the ‘password_mode’ property.
-
set_placeholder_text
(*val)¶ action – Setter for the ‘placeholder_text’ property.
-
set_text
(*val)¶ action – Setter for the ‘text’ property.
-
submit
()¶ emitter – Event emitted when the user strikes the enter or return key (but not when losing focus). Has
old_value
andnew_value
attributes (which are the same).
-
text
¶ StringProp – The current text of the line edit. Settable. If this is an empty string, the placeholder_text is displayed instead.
-
user_done
()¶ emitter – Event emitted when the user is done editing the text, either by moving the focus elsewhere, or by hitting enter. Has
old_value
andnew_value
attributes (which are the same).
-
user_text
(text)¶ emitter – Event emitted when the user edits the text. Has
old_value
andnew_value
attributes.
-
-
class
flexx.ui.
MultiLineEdit
(*init_args, **kwargs)¶ Inherits from:
Widget
An input widget to edit multiple lines of text.
The
node
of this widget is a <textarea>.properties: text
emitters: user_done, user_text
actions: set_text
-
set_text
(*val)¶ action – Setter for the ‘text’ property.
-
text
¶ StringProp – The current text of the multi-line edit. Settable. If this is an empty string, the placeholder_text is displayed instead.
-
user_done
()¶ emitter – Event emitted when the user is done editing the text by moving the focus elsewhere. Has
old_value
andnew_value
attributes (which are the same).
-
user_text
(text)¶ emitter – Event emitted when the user edits the text. Has
old_value
andnew_value
attributes.
-
Media widgets¶
from flexx import ui
class Example(ui.Widget):
def init(self):
with ui.HSplit():
url = 'http://www.w3schools.com/tags/mov_bbb.mp4'
ui.VideoWidget(source=url)
ui.YoutubeWidget(source='RG1P8MQS1cU')
with ui.VBox():
stretch = ui.CheckBox(text='Stretch')
ui.ImageWidget(flex=1, stretch=lambda:stretch.checked,
source='http://github.com/fluidicon.png')
-
class
flexx.ui.
ImageWidget
(*init_args, **kwargs)¶ Inherits from:
Widget
Display an image from a url.
The
node
of this widget is an <img> wrapped in a <div> (theouternode
) to handle sizing.actions: set_source, set_stretch
-
set_source
(*val)¶ action – Setter for the ‘source’ property.
-
set_stretch
(*val)¶ action – Setter for the ‘stretch’ property.
-
source
¶ StringProp – The source of the image, This can be anything that an HTML img element supports.
-
stretch
¶ BoolProp – Whether the image should stretch to fill all available space, or maintain its aspect ratio (default).
-
-
class
flexx.ui.
VideoWidget
(*init_args, **kwargs)¶ Inherits from:
Widget
Display a video from a url.
The
node
of this widget is a <video>.properties: source
actions: set_source
-
set_source
(*val)¶ action – Setter for the ‘source’ property.
-
source
¶ StringProp – The source of the video. This must be a url of a resource on the web.
-
-
class
flexx.ui.
YoutubeWidget
(*init_args, **kwargs)¶ Inherits from:
Widget
Display a Youtube video.
The
node
of this widget is a <div> containing an <iframe> that loads the youtube page.properties: source
actions: set_source
-
set_source
(*val)¶ action – Setter for the ‘source’ property.
-
source
¶ StringProp – The source of the video represented as the Youtube id.
-
Plotly¶
Simple example:
# Define data. This can also be generated with the plotly Python library
data = [{'type': 'bar',
'x': ['giraffes', 'orangutans', 'monkeys'],
'y': [20, 14, 23]}]
# Show
p = ui.PlotlyWidget(data=data)
Also see examples: plotly_gdp.py.
-
class
flexx.ui.
PlotlyWidget
(*init_args, **kwargs)¶ Inherits from:
Widget
A widget that shows a Plotly visualization.
properties: config, data, layout
actions: set_config, set_data, set_layout
-
config
¶ DictProp – The config for the plot.
-
data
¶ ListProp – The data (list of dicts) that describes the plot. This can e.g. be the output of the Python plotly API call.
-
layout
¶ DictProp – The layout dict to style the plot.
-
set_config
(*val)¶ action – Setter for the ‘config’ property.
-
set_data
(*val)¶ action – Setter for the ‘data’ property.
-
set_layout
(*val)¶ action – Setter for the ‘layout’ property.
-
PlotWidget¶
The plot widget provides rudimentary plotting functionality, mostly to
demonstrate how plots can be embedded in a Flexx GUI. It may be
sufficient for simple cases, but don’t expect it to ever support
log-plotting, legends, and other fancy stuff. For real plotting, see
e.g. BokehWidget
. There might also be a Plotly widget at some point.
Simple example:
p = ui.PlotWidget(xdata=range(5), ydata=[1,3,4,2,5],
line_width=4, line_color='red', marker_color='',
minsize=200)
Also see examples: sine.py, twente.py, monitor.py.
-
class
flexx.ui.
PlotWidget
(*init_args, **kwargs)¶ Inherits from:
CanvasWidget
Widget to show a plot of x vs y values. Enough for simple plotting tasks.
properties: line_color, line_width, marker_color, marker_size, xdata, xlabel, ydata, ylabel, yrange
actions: set_data, set_line_color, set_line_width, set_marker_color, set_marker_size, set_xlabel, set_ylabel, set_yrange
-
line_color
¶ ColorProp – The color of the line. Set to the empty string to hide the line.
-
line_width
¶ FloatProp – The width of the line, in pixels.
-
marker_color
¶ ColorProp – The color of the marker. Set to the empty string to hide the marker.
-
marker_size
¶ FloatProp – The size of the marker, in pixels.
-
set_data
(xdata, ydata)¶ action – Set the xdata and ydata.
-
set_line_color
(*val)¶ action – Setter for the ‘line_color’ property.
-
set_line_width
(*val)¶ action – Setter for the ‘line_width’ property.
-
set_marker_color
(*val)¶ action – Setter for the ‘marker_color’ property.
-
set_marker_size
(*val)¶ action – Setter for the ‘marker_size’ property.
-
set_xlabel
(*val)¶ action – Setter for the ‘xlabel’ property.
-
set_ylabel
(*val)¶ action – Setter for the ‘ylabel’ property.
-
set_yrange
(*val)¶ action – Setter for the ‘yrange’ property.
-
xdata
¶ TupleProp – A list of values for the x-axis. Set via the
set_data()
action.
-
xlabel
¶ StringProp – The label to show on the x-axis.
-
ydata
¶ TupleProp – A list of values for the y-axis. Set via the
set_data()
action.
-
ylabel
¶ StringProp – The label to show on the y-axis.
-
yrange
¶ FloatPairProp – The range for the y-axis. If (0, 0) (default) it is determined from the data.
-
ProgressBar¶
Example:
from flexx import app, event, ui
class Example(ui.Widget):
def init(self):
with ui.HBox():
self.b1 = ui.Button(flex=0, text='Less')
self.b2 = ui.Button(flex=0, text='More')
self.prog = ui.ProgressBar(flex=1, value=0.1, text='{percent} done')
@event.reaction('b1.pointer_down', 'b2.pointer_down')
def _change_progress(self, *events):
for ev in events:
if ev.source is self.b1:
self.prog.set_value(self.prog.value - 0.1)
else:
self.prog.set_value(self.prog.value + 0.1)
-
class
flexx.ui.
ProgressBar
(*init_args, **kwargs)¶ Inherits from:
Widget
A widget to show progress.
The
node
of this widget is a <div> containing a few HTML elements for rendering.properties: max, min, text, value
actions: set_max, set_min, set_text, set_value
-
max
¶ FloatProp – The maximum progress value.
-
min
¶ FloatProp – The minimum progress value.
-
set_max
(*val)¶ action – Setter for the ‘max’ property.
-
set_min
(*val)¶ action – Setter for the ‘min’ property.
-
set_text
(*val)¶ action – Setter for the ‘text’ property.
-
set_value
(value)¶ action – set_value
-
text
¶ StringProp – The label to display on the progress bar. Occurances of “{percent}” are replaced with the current percentage, and “{value}” with the current value.
-
value
¶ FloatProp – The progress value.
-
Slider¶
Simple example:
with flx.VBox():
flx.Slider(min=10, max=20, value=12)
flx.RangeSlider(min=10, max=90, value=(20, 60))
Also see examples: sine.py, twente.py, deep_event_connections.py.
-
class
flexx.ui.
Slider
(*init_args, **kwargs)¶ Inherits from:
Widget
An input widget to select a value in a certain range.
The
node
of this widget is a <div> containing a few HTML elements for rendering. It does not use a<input type='range'>
because of its different appearance and behaviour accross browsers.properties: disabled, max, min, step, text, value
emitters: user_done, user_value
actions: set_disabled, set_max, set_min, set_step, set_text, set_value
-
disabled
¶ BoolProp – Whether the slider is disabled.
-
max
¶ FloatProp – The maximum slider value.
-
min
¶ FloatProp – The minimal slider value.
-
set_disabled
(*val)¶ action – Setter for the ‘disabled’ property.
-
set_max
(*val)¶ action – Setter for the ‘max’ property.
-
set_min
(*val)¶ action – Setter for the ‘min’ property.
-
set_step
(*val)¶ action – Setter for the ‘step’ property.
-
set_text
(*val)¶ action – Setter for the ‘text’ property.
-
set_value
(value)¶ action – set_value
-
step
¶ FloatProp – The step size for the slider.
-
text
¶ StringProp – The label to display on the slider during dragging. Occurances of “{percent}” are replaced with the current percentage, and “{value}” with the current value. Default “{value}”.
-
user_done
()¶ emitter – Event emitted when the user stops manipulating the slider. Has
old_value
andnew_value
attributes (which have the same value).
-
user_value
(value)¶ emitter – Event emitted when the user manipulates the slider. Has
old_value
andnew_value
attributes.
-
value
¶ FloatProp – The current slider value.
-
-
class
flexx.ui.
RangeSlider
(*init_args, **kwargs)¶ Inherits from:
Slider
An input widget to select a range (i.e having two handles instead of one).
The
node
of this widget is a <div> containing a few HTML elements for rendering.properties: value
actions: set_value
-
set_value
(*value)¶ action – Set the RangeSlider’s value. Can be called using
set_value([val1, val2])
orset_value(val1, val2)
.
-
value
¶ FloatPairProp – The current slider value as a two-tuple.
-
TreeWidget¶
A TreeWidget
can contain TreeItems
, which in turn can contain
TreeItems
to construct a tree.
from flexx import app, ui
class Example(ui.Widget):
def init(self):
with ui.TreeWidget(max_selected=2):
for t in ['foo', 'bar', 'spam', 'eggs']:
ui.TreeItem(text=t, checked=True)
Also see examples: tree.py, control_with_keys.py.
-
class
flexx.ui.
TreeItem
(*init_args, **kwargs)¶ Inherits from:
Widget
An item to put in a TreeWidget. This widget should only be used inside a TreeWidget or another TreeItem.
Items are collapsable/expandable if their
collapsed
property is set toTrue
orFalse
(i.e. notNone
), or if they have sub items. Items are checkable if theirchecked
property is set toTrue
orFalse
(i.e. notNone
). Items are selectable depending on the selection policy defined byTreeWidget.max_selected
.If needed, the
_render_title()
and_render_text()
methods can be overloaded to display items in richer ways. See the documentation ofWidget._render_dom()
for details.The
outernode
of this widget is a <li> (a list-item in the tree or parent item’s<ul>
. Thenode
of this widget is a <span> that represents the row for this item (but not its children).properties: checked, collapsed, selected, text, title, visible
emitters: user_checked, user_collapsed, user_selected
actions: set_checked, set_collapsed, set_parent, set_selected, set_text, set_visible
-
checked
¶ TriStateProp – Whether this item is checked (i.e. has its checkbox set). The value can be None, True or False. None (the default) means that the item is not checkable.
-
collapsed
¶ TriStateProp – Whether this item is expanded (i.e. shows its children). The value can be None, True or False. None (the default) means that the item is not collapsable (unless it has sub items).
-
selected
¶ BoolProp – Whether this item is selected. Depending on the TreeWidget’s policy (max_selected), this can be set/unset on clicking the item.
-
set_checked
(*val)¶ action – Setter for the ‘checked’ property.
-
set_collapsed
(*val)¶ action – Setter for the ‘collapsed’ property.
-
set_parent
(parent, pos=None)¶ action – set_parent
-
set_selected
(*val)¶ action – Setter for the ‘selected’ property.
-
set_text
(*val)¶ action – Setter for the ‘text’ property.
-
set_visible
(*val)¶ action – Setter for the ‘visible’ property.
-
text
¶ StringProp – The text for this item. Can be used in combination with
title
to obtain two columns.
-
title
¶ StringProp – The title for this item that appears before the text. Intended for display of key-value pairs. If a title is given, the text is positioned in a second (virtual) column of the tree widget.
-
user_checked
(checked)¶ emitter – Event emitted when the user (un)checks this item. Has
old_value
andnew_value
attributes.
-
user_collapsed
(collapsed)¶ emitter – Event emitted when the user (un)collapses this item. Has
old_value
andnew_value
attributes.
-
user_selected
(selected)¶ emitter – Event emitted when the user (un)selects this item. Has
old_value
andnew_value
attributes. One can call this emitter directly to emulate a user-selection, but note that this bypasses the max_selected policy.
-
visible
¶ BoolProp – Whether this item (and its sub items) is visible.
-
-
class
flexx.ui.
TreeWidget
(*init_args, **kwargs)¶ Inherits from:
Widget
A Widget that can be used to structure information in a list or a tree. It’s items are represented by its children, which may only be TreeItem objects. Sub items can be created by instantiating TreeItems in the context of another TreeItem.
When the items in the tree have no sub-items themselves, the TreeWidget is in “list mode”. Otherwise, items can be collapsed/expanded etc.
The
node
of this widget is a <div> with some child elements and quite a bit of CSS for rendering.Style
This widget can be fully styled using CSS, using the following CSS classes:
flx-listmode
is set on the widget’s node if no items have sub items.
Style classes for a TreeItem’s elements:
flx-TreeItem
indicates the row of an item (its text, icon, and checkbox).flx-TreeItem > collapsebut
the element used to collapse/expand an item.flx-TreeItem > checkbut
the element used to check/uncheck an item.flx-TreeItem > text
the element that contains the text of the item.flx-TreeItem > title
the element that contains the title of the item.
Style classes applied to the TreeItem, corresponding to its properties:
visible-true
andvisible-false
indicate visibility.selected-true
andselected-false
indicate selection state.checked-true
,checked-false
andchecked-null
indicate checked state, with thenull
variant indicating not-checkable.collapsed-true
,collapsed-false
andcollapsed-null
indicate collapse state, with thenull
variant indicating not-collapsable.
properties: max_selected
actions: set_max_selected
methods: get_all_items, highlight_get, highlight_hide, highlight_show, highlight_show_item, highlight_toggle_checked, highlight_toggle_selected
-
get_all_items
()¶ Get a flat list of all TreeItem instances in this Tree (including sub children and sub-sub children, etc.), in the order that they are shown in the tree.
-
highlight_get
()¶ Get the “current” item. This is the currently highlighted item if there is one. Otherwise it can be the last highlighted item or the last clicked item.
-
highlight_hide
()¶ Stop highlighting the “current” item.
-
highlight_show
(step=0)¶ Highlight the “current” item, optionally moving step items.
-
highlight_show_item
(item)¶ Highlight the given item.
-
highlight_toggle_checked
()¶ Convenience method to toggle the “checked” property of the current item.
-
highlight_toggle_selected
()¶ Convenience method to toggle the “selected” property of the current item.
-
max_selected
¶ IntProp – The maximum number of selected items:
- If 0 (default) there is no selection.
- If 1, there can be one selected item.
- If > 1, up to this number of items can be selected by clicking them.
- If -1, any number of items can be selected by holding Ctrl or Shift.
-
set_max_selected
(*val)¶ action – Setter for the ‘max_selected’ property.
App reference¶
Wrapping a Component into an Application¶
-
class
flexx.app.
App
(cls, *args, **kwargs)¶ Specification of a Flexx app.
Strictly speaking, this is a container for a
PyComponent
/JsComponent
class plus the args and kwargs that it is to be instantiated with.Parameters: - cls (Component) – the PyComponent or JsComponent class (e.g. Widget) that represents this app.
- args – positional arguments used to instantiate the class (and received
in its
init()
method). - kwargs – keyword arguments used to initialize the component’s properties.
-
cls
¶ The Component class that is the basis of this app.
-
dump
(fname=None, link=2)¶ Get a dictionary of web assets that statically represents the app.
The returned dict contains at least one html file. Any session-specific or shared data is also included. If link is 2/3, all shared assets are included too (and the main document links to them). A link value of 0/1 may be prefered for performance or ease of distribution, but with link 2/3 debugging is easier and multiple apps can share common assets.
When a process only dumps/exports an app, no server is started. Tornado is not even imported (we have a test for this). This makes it possible to use Flexx to dump an app and then serve it with any tool one likes.
Parameters: - fname (str, optional) – the name of the main html asset. If not given or None, the name of the component class is used. Must end in .html/.htm/.hta.
- link (int) –
whether to link (JS and CSS) assets or embed them: A values of 0/1 is recommended for single (and standalone) apps, while multiple apps can share common assets by using 2/3.
- 0: all assets are embedded into the main html document.
- 1: normal assets are embedded, remote assets remain remote.
- 2: all assets are linked (as separate files). Default.
- 3: normal assets are linked, remote assets remain remote.
Returns: A collection of assets.
Return type: dict
-
export
(filename, link=2, overwrite=True)¶ Export this app to a static website.
Also see dump(). An app that contains no data, can be exported to a single html document by setting link to 0.
Parameters: - filename (str) – Path to write the HTML document to. If the filename ends with .hta, a Windows HTML Application is created. If a directory is given, the app is exported to appname.html in that directory.
- link (int) –
whether to link (JS and CSS) assets or embed them:
- 0: all assets are embedded into the main html document.
- 1: normal assets are embedded, remote assets remain remote.
- 2: all assets are linked (as separate files). Default.
- 3: normal assets are linked, remote assets remain remote.
- overwrite (bool, optional) – if True (default) will overwrite files that already exist. Otherwise existing files are skipped. The latter makes it possible to efficiently export a series of apps to the same directory and have them share common assets.
-
freeze
(dirname, launch='firefox-app', excludes=('numpy', ), includes=())¶ Create an executable that can be distributed as a standalone desktop application. This process (known as “freezing”) requires PyInstaller.
Note: this method is experimental. See https://flexx.readthedocs.io/en/stable/freeze.html for more information.
Parameters: - dirname (str) – Path to generate the executable in. Some temporary files and directories will be created during the freezing process. The actual executable will be placed in “dist/app_name”, where app_name is the (lowercase) name of the application class.
- launch (str) – The argument to use for the call to
flx.launch()
. If set to None,flx.serve()
will be used instead, and you will be responsible for connecting a browser. - excludes (list) – A list of module names to exclude during freezing. By default Numpy is excluded because PyInstaller detects it even though Flexx does not use it. Override this if you do use Numpy.
- includes (list) – A list of module name to include during freezing.
-
is_served
¶ Whether this app is already registered by the app manager.
-
launch
(runtime=None, **runtime_kwargs)¶ Launch this app as a desktop app in the given runtime. See https://webruntime.readthedocs.io for details.
Parameters: - runtime (str) – the runtime to launch the application in. Default ‘app or browser’.
- runtime_kwargs – kwargs to pass to the
webruntime.launch
function. A few names are passed to runtime kwargs if not already present (‘title’ and ‘icon’).
Returns: an instance of the given class.
Return type:
-
name
¶ The name of the app, i.e. the url path that this app is served at.
-
publish
(name, token, url=None)¶ Publish this app as static HTML on the web.
This is an experimental feature! We will try to keep your app published, but make no guarantees. We reserve the right to remove apps or shut down the web server completely.
Parameters: - name (str) – The name by which to publish this app. Must be unique within the scope of the published site.
- token (str) – a secret token. This is stored at the target website. Subsequent publications of the same app name must have the same token.
- url (str) – The url to POST the app to. If None (default), the default Flexx live website url will be used.
-
serve
(name=None)¶ Start serving this app.
This registers the given class with the internal app manager. The app can be loaded via ‘http://hostname:port/name’.
Parameters: name (str, optional) – the relative URL path to serve the app on. If this is ''
(the empty string), this will be the main app. If not given or None, the name of the component class is used.
-
url
¶ The url to acces this app. This raises an error if serve() has not been called yet or if Flexx’ server is not yet running.
-
flexx.app.
serve
(cls, name=None, properties=None)¶ Shorthand for
app.App(cls).serve(name)
.
-
flexx.app.
launch
(cls, runtime=None, properties=None, **runtime_kwargs)¶ Shorthand for
app.App(cls).launch(runtime, **runtime_kwargs)
.
-
flexx.app.
export
(cls, filename, properties=None, **kwargs)¶ Shorthand for
app.App(cls).export(filename, ...)
.
The Component classes¶
-
class
flexx.app.
BaseAppComponent
(*init_args, **property_values)¶ Inherits from
Component
Abstract class for Component classes that can be “shared” between Python and JavaScript. The concrete implementations are:
- The
PyComponent
class, which operates in Python, but has a proxy object in JavaSript to which properties are synced and from which actions can be invoked. - The
JsComponent
class, which operates in JavaScript, but can have a proxy object in Python to which properties are synced and from which actions can be invoked. - The
StubComponent
class, which represents a component class that is somewhere else, perhaps in another session. It does not have any properties, nor actions. But it can be “moved around”.
-
root
¶ attribute – The component that represents the root of the application. Alias for session.app.
-
session
¶ attribute – The session to which this component belongs. The component id is unique within its session.
-
uid
¶ attribute – A unique identifier for this component; a combination of the session and component id’s.
- The
-
class
flexx.app.
PyComponent
(*init_args, **property_values)¶ Inherits from
BaseAppComponent
Base component class that operates in Python, but is accessible in JavaScript, where its properties and events can be observed, and actions can be invoked.
PyComponents can only be instantiated in Python, and always have a corresponding proxy object in JS. PyComponents can be disposed only from Python. Disposal also happens if the Python garbage collector collects a PyComponent.
-
JS
¶ alias of
PyComponent
-
-
class
flexx.app.
JsComponent
(*init_args, **kwargs)¶ Inherits from
BaseAppComponent
Base component class that operates in JavaScript, but is accessible in Python, where its properties and events can be observed, and actions can be invoked.
JsComponents can be instantiated from both JavaScript and Python. A corresponding proxy component is not necessarily present in Python. It is created automatically when needed (e.g. when referenced by a property). A JsComponent can be explicitly disposed from both Python and JavaScript. When the Python garbage collector collects a JsComponent (or really, the proxy thereof), only the Python side proxy is disposed; the JsComponent in JS itself will be unaffected. Make sure to call
dispose()
when needed!-
JS
¶ alias of
JsComponent
-
-
class
flexx.app.
StubComponent
(session, id)¶ Class to represent stub proxy components to take the place of components that do not belong to the current session, or that do not exist for whatever reason. These objects cannot really be used, but they can be moved around.
-
flexx.app.
get_component_classes
()¶ Get a list of all known PyComponent and JsComponent subclasses.
-
class
flexx.app.
LocalProperty
(*args, doc='', settable=False)¶ A generic property that is only present at the local side of the component, i.e. not at the proxy. Intended for properties that the other side should not care about, and/or for wich syncing would be problematic, e.g. for performance or because it contains components that we want to keep local.
Session and Assets¶
An asset is represented using an Asset
object that defines its sources,
dependencies, etc. Assets can be specific to the session or shared across sessions.
The AssetStore
provides all shared assets for clients connected to the current
process. The global store is at flexx.app.assets
.
The session object handles the connection between Python and JavaScript,
and it allows adding client-side assets, which for instance makes it
easy to create extensions based on existing JS libraries.
-
class
flexx.app.
Asset
(name, source=None)¶ Class to represent an asset (JS or CSS) to be included on the page. Users will typically use
app.assets.add_shared_asset()
, see the corresponding docs for details.-
name
¶ The (file) name of this asset.
-
remote
¶ Whether the asset is remote (client will load it from elsewhere). If True, the source specifies the URL.
-
source
¶ The source for this asset. Can be str, URL or callable.
-
to_html
(path='{}', link=3)¶ Get HTML element tag to include in the document.
Parameters: - path (str) – the path of this asset, in which ‘{}’ can be used as a placeholder for the asset name.
- link (int) –
whether to link to this asset:
- 0: the asset is embedded.
- 1: normal assets are embedded, remote assets remain remote.
- 2: the asset is linked (and served by our server).
- 3: (default) normal assets are linked, remote assets remain remote.
-
to_string
()¶ Get the string code for this asset. Even for remote assets.
-
-
class
flexx.app._assetstore.
AssetStore
¶ Provider of shared assets (CSS, JavaScript) and data (images, etc.). Keeps track of JSModules and makes them available via asset bundles. The global asset store object can be found at
flexx.app.assets
. Assets and data in the asset store can be used by all sessions. Each session object also keeps track of data.Assets with additional JS or CSS to load can be used simply by creating/importing them in a module that defines the JsComponent class that needs the asset.
Add an asset to the store so that the client can load it from the server. Users typically only need this to provide an asset without loading it in the main page, e.g. when the asset is loaded by a secondary page, a web worker, or AJAX.
Parameters: - name (str) – the asset name, e.g. ‘foo.js’ or ‘bar.css’. Can contain slashes to emulate a file system. e.g. ‘spam/foo.js’. If a URL is given, both name and source are implicitly set (and its a remote asset).
- source (str, function) –
the source for this asset. Can be:
- The source code.
- A URL (str starting with ‘http://’ or ‘https://’),
making this a “remote asset”. Note that
App.export()
provides control over how (remote) assets are handled. - A funcion that should return the source code, and which is called only when the asset is used. This allows defining assets without causing side effects when they’re not used.
Returns: the (relative) url at which the asset can be retrieved.
Return type: str
Add data to serve to the client (e.g. images), which is shared between sessions. It is an error to add data with a name that is already registered. See
Session.add_data()
to set data per-session and use actions to send data to JsComponent objects directly.Parameters: - name (str) – the name of the data, e.g. ‘icon.png’.
- data (bytes) – the data blob.
Returns: the (relative) url at which the data can be retrieved.
Return type: str
-
associate_asset
(mod_name, asset_name, source=None)¶ Associate an asset with the given module. The assets will be loaded when the module that it is associated with is used by JavaScript. Multiple assets can be associated with a module, and an asset can be associated with multiple modules.
The intended usage is to write the following inside a module that needs the asset:
app.assets.associate_asset(__name__, ...)
.Parameters: - mod_name (str) – The name of the module to associate the asset with.
- asset_name (str) – The name of the asset to associate. Can be an already registered asset, or a new asset.
- source (str, callable, optional) – The source for a new asset. See
add_shared_asset()
for details. It is an error to supply a source if the asset_name is already registered.
Returns: the (relative) url at which the asset can be retrieved.
Return type: str
-
get_asset
(name)¶ Get the asset instance corresponding to the given name or None if it not known.
-
get_asset_names
()¶ Get a list of all asset names.
-
get_associated_assets
(mod_name)¶ Get the names of the assets associated with the given module name. Sorted by instantiation time.
-
get_data
(name)¶ Get the data (as bytes) corresponding to the given name or None if it not known.
-
get_data_names
()¶ Get a list of all data names.
-
modules
¶ The JSModule objects known to the asset store. Each module corresponds to a Python module.
-
update_modules
()¶ Collect and update the JSModule instances that correspond to Python modules that define Component classes. Any newly created modules get added to all corresponding assets bundles (creating them if needed).
It is safe (and pretty fast) to call this more than once since only missing modules are added. This gets called automatically by the Session object.
-
class
flexx.app.
Session
(app_name, store=None, request=None)¶ A connection between Python and the client runtime (JavaScript).
The session is what holds together the app widget, the web runtime, and the websocket instance that connects to it.
Responsibilities:
- Send messages to the client and process messages received by the client.
- Keep track of PyComponent instances used by the session.
- Keep track of JsComponent instances associated with the session.
- Ensure that the client has all the module definitions it needs.
-
add_data
(name, data)¶ Add data to serve to the client (e.g. images), specific to this session. Returns the link at which the data can be retrieved. Note that actions can be used to send (binary) data directly to the client (over the websocket).
Parameters: - name (str) – the name of the data, e.g. ‘icon.png’. If data has already been set on this name, it is overwritten.
- data (bytes) – the data blob.
Returns: the (relative) url at which the data can be retrieved.
Return type: str
-
app
¶ The root PyComponent or JsComponent instance that represents the app.
-
app_name
¶ The name of the application that this session represents.
-
assets_to_ignore
¶ The set of names of assets that should not be pushed to the client, e.g. because they are already present on the page. Add names to this set to prevent them from being loaded.
-
call_after_roundtrip
(callback, *args)¶ A variant of
call_soon()
that calls a callback after a py-js roundrip. This can be convenient to delay an action until after other things have settled down.
-
close
()¶ Close the session: close websocket, close runtime, dispose app.
-
co_eval
(js)¶ Coroutine to evaluate JS in the client, wait for the result, and then return it. It is recomended to use this method only for testing purposes.
-
co_roundtrip
()¶ Coroutine to wait for one Py-JS-Py roundtrip.
-
get_component_instance
(id)¶ Get PyComponent or JsComponent instance that is associated with this session and has the corresponding id. The returned value can be None if it does not exist, and a returned component can be disposed.
Gets the value of the cookie with the given name, else default. Note that cookies only really work for web apps.
-
get_data
(name)¶ Get the data corresponding to the given name. This can be data local to the session, or global data. Returns None if data by that name is unknown.
-
get_data_names
()¶ Get a list of names of the data provided by this session.
-
id
¶ The unique identifier of this session.
-
keep_alive
(ob, iters=1)¶ Keep an object alive for a certain amount of time, expressed in Python-JS ping roundtrips. This is intended for making JsComponent (i.e. proxy components) survice the time between instantiation triggered from JS and their attachement to a property, though any type of object can be given.
-
present_modules
¶ The set of module names that is (currently) available at the client.
-
remove_data
(name)¶ Remove the data associated with the given name. If you need this, consider using actions instead. Note that data is automatically released when the session is closed.
-
request
¶ The tornado request that was at the origin of this session.
-
runtime
¶ The runtime that is rendering this app instance. Can be None if the client is a browser.
-
send_command
(*command)¶ Send a command to the other side. Commands consists of at least one argument (a string representing the type of command).
Sets the given cookie name/value with the given options. Set value to None to clear. The cookie value is secured using flexx.config.cookie_secret; don’t forget to set that config value in your server. Additional keyword arguments are set on the Cookie.Morsel directly.
-
status
¶ The status of this session. The lifecycle for each session is:
- status 1: pending
- status 2: connected
- status 0: closed
Event reference¶
Component¶
-
class
flexx.event.
Component
(*init_args, **property_values)¶ The base component class.
Components have attributes to represent static values, properties to represent state, actions that can mutate properties, and reactions that react to events such as property changes.
Initial values of properties can be provided by passing them as keyword arguments.
Subclasses can use
Property
(or one of its subclasses) to define properties, and theaction
,reaction
, andemitter
decorators to create actions, reactions. and emitters, respectively.class MyComponent(event.Component): foo = event.FloatProp(7, settable=True) spam = event.Attribute() @event.action def inrease_foo(self): self._mutate_foo(self.foo + 1) @event.reaction('foo') def on_foo(self, *events): print('foo was set to', self.foo) @event.reaction('bar') def on_bar(self, *events): for ev in events: print('bar event was emitted') @event.emitter def bar(self, v): return dict(value=v) # the event to emit
-
_comp_apply_property_values
(values)¶ Apply given property values, prefer using a setter, mutate otherwise.
-
_comp_init_property_values
(property_values)¶ Initialize property values, combining given kwargs (in order) and default values.
-
_comp_init_reactions
()¶ Create our own reactions. These will immediately connect.
-
_comp_stop_capturing_events
()¶ Stop capturing events and flush the captured events. This gets scheduled to be called asap after initialization. But components created in our init() go first.
-
_mutate
(prop_name, value, mutation='set', index=-1)¶ Mutate a
property
. Can only be called from anaction
.Each Component class will also have an auto-generated mutator function for each property: e.g. property
foo
can be mutated withc._mutate('foo', ..)
orc._mutate_foo(..)
.Parameters: - prop_name (str) – the name of the property being mutated.
- value – the new value, or the partial value for partial mutations.
- mutation (str) – the kind of mutation to apply. Default is ‘set’.
Partial mutations to list-like
properties
can be applied by using ‘insert’, ‘remove’, or ‘replace’. If other than ‘set’, index must be specified, and >= 0. If ‘remove’, then value must be an int specifying the number of items to remove. - index – the index at which to insert, remove or replace items. Must be an int for list properties.
The ‘replace’ mutation also supports multidensional (numpy) arrays. In this case
value
can be an ndarray to patch the data with, andindex
a tuple of elements.
-
_registered_reactions_hook
()¶ This method is called when the reactions change, can be overloaded in subclasses. The original method returns a list of event types for which there is at least one registered reaction. Overloaded methods should return this list too.
-
disconnect
(type, reaction=None)¶ Disconnect reactions.
Parameters: - type (str) – the type for which to disconnect any reactions. Can include the label to only disconnect reactions that were registered with that label.
- reaction (optional) – the reaction object to disconnect. If given, only this reaction is removed.
-
dispose
()¶ Use this to dispose of the object to prevent memory leaks. Make all subscribed reactions forget about this object, clear all references to subscribed reactions, and disconnect all reactions defined on this object.
-
emit
(type, info=None)¶ Generate a new event and dispatch to all event reactions.
Parameters: - type (str) – the type of the event. Should not include a label.
- info (dict) – Optional. Additional information to attach to the event object. Note that the actual event is a Dict object that allows its elements to be accesses as attributes.
-
get_event_handlers
(type)¶ Get a list of reactions for the given event type. The order is the order in which events are handled: alphabetically by label. Intended mostly for debugging purposes.
Parameters: type (str) – the type of event to get reactions for. Should not include a label.
-
get_event_types
()¶ Get the known event types for this component. Returns a list of event type names, for which there is a property/emitter or for which any reactions are registered. Sorted alphabetically. Intended mostly for debugging purposes.
-
id
¶ attribute – The string by which this component is identified.
-
init
()¶ Initializer method. This method can be overloaded when creating a custom class. It is called with this component as a context manager (i.e. it is the active component), and it receives any positional arguments that were passed to the constructor.
-
-
flexx.event.
mutate_array
(array, ev)¶ Function to mutate a list- or array-like property in-place. Used by Component. The
ev
must be a dict with elements:- mutation: ‘set’, ‘insert’, ‘remove’ or ‘replace’.
- objects: the values to set/insert/replace, or the number of iterms to remove.
- index: the (non-negative) index to insert/replace/remove at.
-
flexx.event.
mutate_dict
(d, ev)¶ Function to mutate an dict property in-place. Used by Component. The
ev
must be a dict with elements:- mutation: ‘set’, ‘insert’, ‘remove’ or ‘replace’.
- objects: the dict to set/insert/replace, or a list if keys to remove.
- index: not used.
Attributes¶
-
class
flexx.event.
Attribute
(doc='')¶ Attributes are (readonly, and usually static) values associated with Component classes. They expose and document a value without providing means of observing changes like
Property
does. (The actual value is taken fromcomponent._xx
, with “xx” the name of the attribute.)
Properties¶
-
class
flexx.event.
Property
(*args, doc='', settable=False)¶ Base property class. Properties are (readonly) attributes associated with
Component
classes, which can bemutated
only byactions
. The baseProperty
class can have any value, the subclasses validate/convert the value when it is mutated.Parameters: - initial_value – The initial value for the property. If omitted, a default value is used (specific for the type of property).
- settable (bool) – If True, a corresponding setter action is automatically created that can be used to set the property. Default False.
- doc (str) – The documentation string for this property (optional).
Example usage:
class MyComponent(event.Component): foo = event.AnyProp(7, doc="A property that can be anything") bar = event.StringProp(doc='A property that can only be string') spam = event.IntProp(8, settable=True) >> c = MyComponent() >> c.set_spam(9) # use auto-generated setter action
One can also implement custom properties:
class MyCustomProp(event.Property): ''' A property that can only be 'a', 'b' or 'c'. ''' _default = 'a' def _validate(self, value, name, data): if value not in 'abc': raise TypeError('MyCustomProp %r must be "a", "b" or "c".' % name) return value
-
class
flexx.event.
AnyProp
(*args, doc='', settable=False)¶ A property that can be anything (like Property). Default None.
-
class
flexx.event.
BoolProp
(*args, doc='', settable=False)¶ A property who’s values are converted to bool. Default False.
-
class
flexx.event.
TriStateProp
(*args, doc='', settable=False)¶ A property who’s values can be False, True and None.
-
class
flexx.event.
IntProp
(*args, doc='', settable=False)¶ A propery who’s values are integers. Floats and strings are converted. Default 0.
-
class
flexx.event.
FloatProp
(*args, doc='', settable=False)¶ A propery who’s values are floats. Integers and strings are converted. Default 0.0.
-
class
flexx.event.
StringProp
(*args, doc='', settable=False)¶ A propery who’s values are strings. Default empty string.
-
class
flexx.event.
TupleProp
(*args, doc='', settable=False)¶ A propery who’s values are tuples. In JavaScript the values are Array objects that have some of their methods disabled. Default empty tuple.
-
class
flexx.event.
ListProp
(*args, doc='', settable=False)¶ A propery who’s values are lists. Default empty list. The value is always copied upon setting, so one can safely provide an initial list.
Warning: updating the list in-place (e.g. use
append()
) will not trigger update events! In-place updates can be done via the_mutate
method.
-
class
flexx.event.
DictProp
(*args, doc='', settable=False)¶ A property who’s values are dicts. Default empty dict. The value is always copied upon setting, so one can safely provide an initial dict.
Warning: updating the dict in-place (e.g. use
update()
) will not trigger update events! In-place updates can be done via the_mutate
method.
-
class
flexx.event.
ComponentProp
(*args, doc='', settable=False)¶ A propery who’s values are Component instances or None. Default None.
-
class
flexx.event.
FloatPairProp
(*args, doc='', settable=False)¶ A property that represents a pair of float values, which can also be set using a scalar.
-
class
flexx.event.
EnumProp
(*args, doc='', settable=False)¶ A property that represents a choice between a fixed set of (string) values.
Useage:
foo = EnumProp(['optionA', 'optionB', ...], 'default', ...)
. If no initial value is provided, the first option is used.
-
class
flexx.event.
ColorProp
(*args, doc='', settable=False)¶ A property that represents a color. The value is represented as a (dict-like) object that has the following attributes:
- t: a 4-element tuple (RGBA) with values between 0 and 1.
- css: a CSS string in the form ‘rgba(r,g,b,a)’.
- hex: a hex RGB color in the form ‘#rrggbb’ (no transparency).
- alpha: a scalar between 0 and 1 indicating the transparency.
The color can be set using:
- An object as described above.
- A tuple (length 3 or 4) with floats between 0 and 1.
- A hex RGB color like ‘#f00’ or ‘#aa7711’.
- A hex RGBA color like ‘#f002’ or ‘#ff000022’.
- A CSS string “rgb(…)” or “rgba(…)”
- Simple Matlab-like names like ‘k’, ‘w’, ‘r’, ‘g’, ‘b’, etc.
- A few common color names like ‘red’, ‘yellow’, ‘cyan’.
- Further, string colors can be prefixed with “light”, “lighter”, “dark” and “darker”.
- Setting to None or “” results in fully transparent black.
Actions¶
-
flexx.event.
action
(func)¶ Decorator to turn a method of a Component into an
Action
.Actions change the state of the application by
mutating
properties
. In fact, properties can only be changed via actions.Actions are asynchronous and thread-safe. Invoking an action will not apply the changes directly; the action is queued and handled at a later time. The one exception is that when an action is invoked from anoher action, it is handled directly.
Although setting properties directly might seem nice, their use would mean that the state of the application can change while the app is reacting to changes in the state. This might be managable for small applications, but as an app grows this easily results in inconsistencies and bugs. Separating actions (which modify state) and reactions (that react to it) makes apps easier to understand and debug. This is the core idea behind frameworks such as Elm, React and Veux. And Flexx adopts it as well.
Example usage:
class MyComponent(event.Component): count = event.IntProp(0) @action def increase_counter(self): self._mutate_count(self.count + 1) # call mutator function
-
class
flexx.event.
Action
(ob, func, name, doc)¶ Action objects are wrappers around Component methods. They take care of queueing action invokations rather than calling the function directly, unless the action is called from another action (in this case it would a direct call). This class should not be instantiated directly; use
event.action()
instead.
Reactions¶
-
flexx.event.
reaction
(*connection_strings, mode='normal')¶ Decorator to turn a method of a Component into a
Reaction
.A reaction can be connected to multiple event types. Each connection string represents an event type to connect to.
Also see the
Component.reaction()
method.class MyObject(event.Component): @event.reaction('first_name', 'last_name') def greet(self, *events): print('hello %s %s' % (self.first_name, self.last_name))
A reaction can operate in a few different modes. By not specifying any connection strings, the mode is “auto”: the reaction will automatically trigger when any of the properties used in the function changes. See
get_mode()
for details.Connection string follow the following syntax rules:
- Connection strings consist of parts separated by dots, thus forming a path. If an element on the path is a property, the connection will automatically reset when that property changes (a.k.a. dynamism, more on this below).
- Each part can end with one star (‘*’), indicating that the part is a list and that a connection should be made for each item in the list.
- With two stars, the connection is made recursively, e.g. “children**” connects to “children” and the children’s children, etc.
- Stripped of ‘*’, each part must be a valid identifier (ASCII).
- The total string optionally has a label suffix separated by a colon. The label itself may consist of any characters.
- The string can have a “!” at the very start to suppress warnings for connections to event types that Flexx is not aware of at initialization time (i.e. not corresponding to a property or emitter).
An extreme example could be
"!foo.children**.text:mylabel"
, which connects to the “text” event of the children (and their children, and their children’s children etc.) of thefoo
attribute. The “!” is common in cases like this to suppress warnings if not all children have atext
event/property.
-
class
flexx.event.
Reaction
(ob, func, mode, connection_strings)¶ Reaction objects are wrappers around Component methods. They connect to one or more events. This class should not be instantiated directly; use
event.reaction()
orComponent.reaction()
instead.-
dispose
()¶ Disconnect all connections so that there are no more references to components.
-
get_connection_info
()¶ Get a list of tuples (name, connection_names), where connection_names is a list of type names (including label) for the made connections.
-
get_mode
()¶ Get the mode for this reaction:
- ‘normal’: events are handled in the order that they were emitted. Consequently, there can be multiple calls per event loop iteration if other reactions were triggered as well.
- ‘greedy’: this reaction receives all its events (since the last event loop iteration) in a single call (even if this breaks the order of events with respect to other reactions). Use this when multiple related events must be handled simultenously (e.g. when syncing properties).
- ‘auto’: this reaction tracks what properties it uses, and is automatically triggered when any of these properties changes. Like ‘greedy’ there is at most one call per event loop iteration. Reactions with zero connection strings always have mode ‘auto’.
The ‘normal’ mode generally offers the most consistent behaviour. The ‘greedy’ mode allows the event system to make some optimizations. Combined with the fact that there is at most one call per event loop iteration, this can provide higher performance where it matters. Reactions with mode ‘auto’ can be a convenient way to connect things up. Although it allows the event system to make the same optimizations as ‘greedy’, it also needs to reconnect the reaction after each time it is called, which can degregade performance especially if many properties are accessed by the reaction.
-
get_name
()¶ Get the name of this reaction, usually corresponding to the name of the function that this reaction wraps.
-
reconnect
(index)¶ (re)connect the index’th connection.
-
Emitter¶
-
flexx.event.
emitter
(func)¶ Decorator to turn a method of a Component into an
Emitter
.An emitter makes it easy to emit specific events, and is also a placeholder for documenting an event.
class MyObject(event.Component): @emitter def spam(self, v): return dict(value=v) m = MyObject() m.spam(42) # emit the spam event
The method being decorated can have any number of arguments, and should return a dictionary that represents the event to generate. The method’s docstring is used as the emitter’s docstring.
-
class
flexx.event.
Emitter
(ob, func, name, doc)¶ Emitter objects are wrappers around Component methods. They take care of emitting an event when called and function as a placeholder for documenting an event. This class should not be instantiated directly; use
event.emitter()
instead.
Dict¶
-
class
flexx.event.
Dict
¶ A dict in which the items can be get/set as attributes.
This provides a lean way to represent structured data, and works well in combination with autocompletion. Keys can be anything that are otherwise valid keys, but keys that are not valid identifiers or that are methods of the dict class (e.g. ‘items’ or ‘copy’) can only be get/set in the classic way.
Example:
>> d = Dict(foo=3) >> d.foo 3 >> d['foo'] = 4 >> d.foo 4 >> d.bar = 5 >> d.bar 5
loop¶
-
class
flexx.event.
Loop
¶ The singleton Flexx event loop at
flexx.event.loop
. This holds the queue of pending calls, actions, and reactions. These are queued separately to realize a consistent one-way data-flow. Further, this object keeps track of (per thread) active components (i.e. the components whose context manager is currently active).Users typically do not need to be aware of the loop object, as it is used internally by Flexx, though it can be useful during debugging.
This event system integrates with Python’s builtin asyncio system, configurable via
Loop.integrate()
. This system can run in a separate thread, but there can be only one active flexx event loop per process.This object can also be used as a context manager; an event loop iteration takes place when the context exits.
-
add_action_invokation
(action, args)¶ Schedule the handling of an action. Automatically called when an action object is called.
-
add_reaction_event
(reaction, ev)¶ Schulde the handling of a reaction. Automatically called by components.
-
call_soon
(func, *args)¶ Arrange for a callback to be called as soon as possible. The callback is called after
call_soon()
returns, when control returns to the event loop.This operates as a FIFO queue, callbacks are called in the order in which they are registered. Each callback will be called exactly once.
Any positional arguments after the callback will be passed to the callback when it is called.
This method is thread-safe: the callback will be called in the thread corresponding with the loop. It is therefore actually more similar to asyncio’s
call_soon_threadsafe()
.Also see
asyncio.get_event_loop().call_soon()
andasyncio.get_event_loop().call_later()
.
-
can_mutate
(component=None)¶ Whether mutations can be done to the given component, and whether invoked actions on the component are applied directly.
-
get_active_component
()¶ Get the currently “active” component (for this thread), or None.
-
get_active_components
()¶ Get a tuple that represents the stack of “active” components. Each thread has its own stack. Should only be used directly inside a Component context manager.
-
has_pending
()¶ Get whether there are any pending actions, reactions, or calls.
-
integrate
(loop=None, reset=True)¶ Integrate the Flexx event system with the given asyncio event loop (or the default one). Also binds the event system to the current thread.
From this point, any (pending) calls to the iter callback by the previous thread will be ignored.
By calling this without calling reset(), it should be possible to hot-swap the system from one loop (and/or thread) to another (though this is currently not tested).
-
iter
()¶ Do one event loop iteration; process pending calls, actions and reactions. These tree types of items are each queued in separate queues, and are handled in the aforementioned order.
-
register_prop_access
(component, prop_name)¶ Register access of a property, to keep track of automatic reactions.
-
reset
()¶ Reset the loop, purging all pending calls, actions and reactions. This is mainly intended for test-related code.
-
flexx.util¶
Flexx’ util
module contains utilities that are used internally,
some of which can be useful outside of Flexx. Note that most modules
in flexx.util
are independent; using them does not import any other
Flexx modules.
Configuration class¶
This page documents the Config class. For learning how to configure Flexx,
see configuring flexx
.
-
class
flexx.util.config.
Config
(name, *sources, **options)¶ Class for configuration objects.
A Config object has a set of options, which can be str, int, float, bool, or a tuple of any of the above. Options can be set from different sources:
- Each option has a default value.
- From .cfg or .ini files.
- From strings in ini format.
- From environment variables.
- From command-line arguments.
- By setting the config option directly.
Parameters: - name (str) – the name by which to identify this config. This name is used as a prefix in environment variables and command line arguments, and optionally as a section header in .cfg files.
- *sources – Sources to initialize the option values with.
These can be strings in ini format, or .ini or .cfg filenames.
If a file is given that does not exist, it is simply ignored.
Special prefixes
~/
and~appdata/
are expanded to the home dir and appdata dir. - **options – The options specification: each option consists of a 3-element tuple (default, type, docstring).
Example
config = Config('myconfig', '~appdata/.myconfig.cfg', foo=(False, bool, 'Whether to foo'), bar=(0.0, float, 'The size of the bar'), spam=('1,2,3', [int], 'A tuple of ints'))
With this, options can be set:
- With an entry
foo = 3
in “~appdata/.myconfig.cfg”. - With a string
"foo = 3"
passed at initialization. - With an environment variable named
MYCONFIG_FOO
. - With a command line argument
--myconfig-foo=3
. - By doing
config.foo = 3
, orconfig['foo'] = 3
in Python.
Notes
- Option names are case insensitive, except for attribute access and environment variables (the latter must be all uppercase).
- All values can be set as a Python object or a string; they are automatically converted to the correct type.
- Each instance gets a docstring that lists all options, so it can easily be used in e.g. Sphynx docs.
-
load_from_file
(filename)¶ Load config options from a file, as if it was given as a source during initialization. This means that options set via argv, environ or directly will not be influenced.
-
load_from_string
(text, filename='<string>')¶ Load config options from a string, as if it was given as a source during initialization. This means that options set via argv, environ or directly will not be influenced.
Logging in Flexx¶
Flexx uses the standard Python logging facilities, but adds functionality, most notably the ability to filter messages by a string or regexp.
-
flexx.
set_log_level
(level, match=None)¶ Set the logging level and match filter
Parameters: - level (str, int) – The verbosity of messages to print. If a str, it can be either DEBUG, INFO, WARNING, ERROR, or CRITICAL. Note that these are for convenience and are equivalent to passing in logging.DEBUG, etc.
- match (str, regexp, None) – String to match. Only those messages
that contain
match
as a substring (and has the appropriatelevel
) will be displayed. Match can also be a compiled regexp.
Notes
If level is DEBUG, the method emitting the log message will be prepended to each log message. Note that if
level
is DEBUG or if thematch
option is used, a small overhead is added to each logged message.
-
class
flexx.util.logging.
capture_log
(level, match=None)¶ Context manager to capture log messages. Useful for testing. Usage:
with capture_log(level, match) as log: ... # log is a list strings (as they would have appeared in the console)
JavaScript minification¶
-
flexx.util.minify.
minify
(code, remove_whitespace=False)¶ Very basic minification of JavaScript code. Will likely support more advanced minifcation in the future.
Parameters: - code (str) – the JavaScript code to minify.
- remove_whitespace (bool) – if True, removes all non-functional whitespace. Otherwise remove all trailing whitespace and indents using tabs to preserve space. Default False.
Command line interface¶
Flexx has a command line interface to perform some simple tasks.
Invoke it via python -m flexx
. Additional command line arguments
can be provided to configure Flexx, see
configuring flexx
.
Flexx command line interface
python -m flexx <command> [args]
help show information on how to use this command.
info show info on flexx server process corresponding to given port,
log Start listening to log messages from a server process - STUB
stop stop the flexx server process corresponding to the given port.
version print the version number
Configuring Flexx¶
This page lists the configuration options available to Flexx, implemented
via the Config
class. Configuration
options are read from <appdata>/.flexx.cfg
(check
flexx.util.config.appdata_dir()
for the actual location),
and can also be set using
environment variables and command line arguments, as explained below.
Alternatively, options can be set directly in Python via
flexx.config.foo = 3
.
-
flexx.
config
¶ Configuration object for flexx
The options below can be set from different sources, and are evaluated in the following order:
- From the default value.
- From .cfg or .ini file, or a string in cfg format.
- From environment variables, e.g.
FLEXX_FOO=3
. - From command-line arguments, e.g.
--flexx-foo=3
. - From setting the config option directly, e.g.
config.foo = 3
.
Use
print(config)
to get a summary of the current values and from which sources they were set.Parameters: - browser_stacktrace (bool) – Show server stack traces in browser window (default True)
- cookie_secret (str) – The secret key to encode cookies. (default ‘flexx_secret’)
- host_whitelist (str) – Comma separated list of allowed <host>:<port> values to pass cross-origin checks. (default ‘’)
- hostname (str) – The default hostname to serve apps. (default ‘localhost’)
- log_level (str) – The log level to use (DEBUG, INFO, WARNING, ERROR) (default ‘info’)
- port (int) – The default port to serve apps. Zero means auto-select. (default 0)
- ssl_certfile (str) – The cert file for https server. (default ‘’)
- ssl_keyfile (str) – The key file for https server. (default ‘’)
- tornado_debug (bool) – Setting the tornado application debug flag allows autoreload and other debugging features. (default ‘false’)
- webruntime (str) – The default web runtime to use. Default is “app or browser”. (default ‘’)
- ws_timeout (int) – If the websocket is idle for this amount of seconds, it is closed. (default 20)
Examples¶
This page provides a list of examples. Some demonstate a specific application,
while others show a tool/technique that is generically useful. In the latter case
you can import the widget using e.g. from flexxamples.howtos.editor_cm import CodeEditor
.
Note that most examples are written in such a way that they work in the
browser (by subclassing Widget
). If you are creating a desktop
application, you probably want to use PyWidget
to create your
high-level widgets instead.
Testers¶
Demos¶
Howtos¶
- adding_handlers.py
- array_props.py
- basic_emit.py
- bokehdemo.py
- bootstrap.py
- box_vs_fix_layout.py
- buttons.py
- control_with_keys.py
- cookies.py
- deep_event_connections.py
- echarts_example.py
- editor_ace.py
- editor_cm.py
- hello_world.py
- icons.py
- jquery.py
- leaflet.py
- local_assets.py
- mutual_dependent_props.py
- oneliners.py
- openlayers.py
- python_in_js.py
- react_to_props.py
- redirect.py
- scrollable.py
- send_data.py
- serve_data.py
- serve_multiple1.py
- serve_multiple2.py
- serve_ssl.py
- serve_with_aiohttp.py
- serve_with_asgineer.py
- serve_with_flask.py
- splitters.py
- store.py
- threaded.py
- tree.py
About¶
Release notes¶
v0.8.0 (26-04-2019)
- Adds a PyWidget class that can be used as a base class for your high-level widgets. Because it is a PyComponent, this makes it much easier to write apps that fully work in Python (desktop-like apps).
- The
FormLayout
uses CSSgrid
instead of<table>
. - A new
GridLayout
widget. - A new
MultiLineEdit
widget. - Improvements to docs and guide.
- Support for freezing Flexx apps to standalone executables (via PyInstaller).
Also see the overview of 0.8 issues and pull request
v0.7.1 (03-12-2018)
- Improved shutdown behavior (#533).
- Small fix in App.export (#532).
- Fix bahevior when navigating back to a Flexx app (#536).
v0.7.0 (02-11-2018)
- New examples for Openlayers and including local assets (by @ocobacho).
- Tests, demos and readme are included in the sdist, helping packaging on Linux (by @toddrme2178).
- Some performance tweaks which should help in larger applications.
- Add
outernode
attribute in TreeItem` widget, enabling more powerful subclasses. - The
Combobox
is smarter about the placement of the “dropdown”. - A new
RangeSlider
widget.
Also see the overview of 0.7 issues and pull request
v0.6.2 (04-10-2018)
- Bugfix in combobox.
- BSDF check dtype in JS.
v0.6.0 (02-10-2018)
Most notable changes:
- Add
Widget.minsize_from_children
property (#497). - Update BSDF (data serialization).
- Widgets van be orphaned upon initialization by setting parent to None (#493)
- Some internal improvements on the dropdown widget.
Also see the overview of 0.6 issues and pull request
v0.5.0 (13-09-2018)
This release marks the end of the alpha stage of Flexx. Until now, we took the liberty to redesign various parts of Flexx and break the API several times. From now on, we care a lot more about backwards compatibility.
Some highlights of changes from the last release:
- A major refactoring of the event system.
- We spun out the PyScript transpiler into the PScript project, as well as the webruntime and dialite project. This means that Flexx itself is focussed on the GUI aspect alone.
- Added touch support.
- Dropped the depency on Phosphor.js.
- A new combined namespace:
from flexx import flx
. - A proper guide in the docs.
- More examples.
Also see the overview of 0.5 pull request and overview of 0.5 issues corresponding to this release.
v0.4.2 (13-10-2017)
This release was not published to Pypi. It served mainly as a tag right before the big refactoring. There were a lot of improvements, but we felt that the state of Flexx was still very much in flux (no pun intended).
Also see the overview of 0.4.2 pull request and overview of 0.4.2 issues corresponding to this release.
v0.4.1 (10-07-2016)
A few [fixes](https://github.com/flexxui/flexx/milestone/8).
v0.4 (07-07-2016)
A lot of work and major changes compared to the previous release. Most notably:
- Completely new event system
flexx.event
replacesflexx.react
. - System for configure Flexx through config files, env variables and command line arguments.
- Better logging.
- More widgets, more examples.
- Better notebook support.
- Fixed nasty bug where new profile data was stored on each launch of the XUL runtime.
- Better support for testing and running Flexx in a separate thread.
Also see the overview of 0.4 pull request and overview of 0.4 issues corresponding to this release.
v0.3.1 (19-02-2016)
A few small fixes, and improvements to distribution. The universal wheel on Pypi for v0.3 did not work on Python 2.7. Flexx now includes a recipe to build a noarch conda package.
Also see the overview of 0.3.1 pull request.
v0.3 (15-02-2016)
The most important changes with respect to the previous release are:
- Flexx now works on Legacy Python (i.e. Python 2.7). The source code is automatically translated during installation.
- Improvements to nested FlexLayout on Chrome
- A command-line tool to stop and get info on running Flexx servers.
- More tests
- A new Canvas widget.
- PyScript uses bound functions for methods and functions without selt/this as first arg.
Also see the overview of 0.3 pull request and overview of 0.3 issues corresponding to this release.
v0.2 (13-10-2015)
We changed a lot, broke API’s, improved things, and probbaly broke other things. Here’s a summary of the most important bits:
- Set up Travis CI, and added more unit tests.
- Layout of ui widgets is based on Phosphorjs.
- Style compliance (and tested on Travis).
- Refactored PyScript, and made it much more feature complete.
- PyScript makes use of common ast, and now works on 3.2-3.5, and pypy.
- We now have a way to include assets (js, css, images).
- The assets make it possible to e.g. embed a Bokeh plot, or a jQuery widget.
Also see the overview of 0.2 pull request and overview of 0.2 issues corresponding to this release.
v0.1 (27-08-2015)
First release.
Contributing to Flexx¶
If you like Flexx and want to help out, there’s a few things you can do. First off, feedback; it’s very useful to hear what you like and what you struggle with. Feel free to use the Github issue tracker for this.
You can help by contributing to the documentation, and/or by creating nice examples.
If you would like to help out with the codebase itself, e.g. by fixing bugs, making improvements, or submitting new types of widgets, you probably need to a few extra libraries (don’t worry, it’s ‘nothing fancy):
- pytest and pytest-cov (get them via conda or pip)
- flake8 (get it via conda or pip)
- Nodejs
- Firefox
We follow the Github workflow:
- You fork the Github repo and make a clone to your local computer.
- You update to the latest version:
git checkout master && git pull
- You make a new branch:
git checkout -b mywork
- You make changes to the code and commit these:
git commit
- You push to your fork
git push
- Create a Pull Request from the Github interface.
Freezing Flexx apps¶
Flexx needs special care when freezing, because it needs access to the Python source code in order to compile it to JavaScript.
The easy way¶
There is experimental support to make freezing very easy:
from flexx import flx
class Example(flx.Widget):
def init(self):
flx.Button(text="Hi there")
if __name__ == "__main__":
app = flx.App(Example)
app.freeze("~/Desktop/flexx_apps")
The more explicit way¶
The above approach does most of the magic behind the scenes. For more control, you can also use a more explicit approach.
First, create a script that represents your application entry point. It is important that this script does not define any new Flexx widgets. It should look something like this:
# Install hook so we we can import modules from source when frozen.
from flexx.util import freeze
freeze.install()
# Run your app as usual
from flexx import flx
from my_module import MyWidget
app = flx.App(MyWidget)
app.launch("firefox-app")
flx.run()
Next, use PyInstaller
as usual to create an app directory. Consider
using --exclude-module numpy
(PyInstaller thinks that Flexx needs Numpy, but this not the case).
Next, copy the source code of the modules that define Flexx widgets. If you
run PyInstaller from a script (using PyInstaller.__main__.run()
) then you
can combine it.
from flexx.util import freeze
freeze.copy_module("flexx", app_dir) # always
freeze.copy_module("flexxamples", app_dir) # used something from here?
freeze.copy_module("my_module", app_dir)
That should be it!
Flexx overview¶
The image above outlines the structure of Flexx.
The event module provides a powerful property and event system that
makes it easy to connect different parts of your application. Central to
the event system is the Component
class.
The app module runs the server to which the web runtime connects (via a
websocket). Further, it extends the Component
class into the
PyComponent
and JsComponent
classes. Objects of these classes
live in Python and JavaScript respectively, but (can) have a representation
on the other side, from which properties can be accessed, and actions be invoked.
The ui module defines all widgets (based on JsComponent
).
The external webruntime package is used to launch a browser looking like a dektop app. The pscript library is used throughout Flexx to compile Python code to JavaScript.
Motivation¶
The primary motivation for Flexx is the undeniable fact that the web (i.e. browser technology) has become an increasingly popular method for delivering applications to users, also for (interactive) scientific content.
The purpose of Flexx is to provide a single application framework to create desktop applications and web apps. By making use of browser technology, the library itself can be relatively small and pure Python, making it widely and easily available.
By making use of PScript (Python to JavaScript translation), the entire library is written without (hardly) a line of JavaScript. This makes it easier to develop than if we would have a corresponding “flexx.js” to maintain. Further, it allows users to easily define callback methods that are executed in JavaScript, allowing for higher performance when needed.
Libraries written for Python, but not in Python have a much harder time to survive, because users don’t easily become contributors. This is one of the reasons of the success of e.g. scikit-image, while e.g. Mayavi has a much harder time attracting developers. Since Flexx is written in a combination of Python and PScript, its user community is more likely to take an active role in its development.
Some background on the Flexx event system¶
Flexx’ event system is quite flexible and designed to cover the needs of a variety of event/messaging mechanisms. But we did not get there in one go.
History¶
In the early days of Flexx, the event system was based on the ideas of reactive programming, where information supposedly flows through your application. Although this style does have benefits, we found it very unnatural for GUI applications. Therefore we made a major refactoring to build an event system using a more classic property based approach. We build in a bit of asynchronicity to deal with some of the common problems with MVC, and were quite happy. However, as we started building larger applications, the system started showing its limitations. Several months of discussion and design, followed by another several months of coding, resulted in the current event system.
Patterns¶
This section discusses how the event system relates to some common patterns, and how these can be implemented.
Observer pattern¶
The idea of the observer pattern is that observers keep track (the state of) of an object, and that an object is agnostic about what it’s tracked by. For example, in a music player, instead of writing code to update the window-title inside the function that starts a song, there would be a concept of a “current song”, and the window would listen for changes to the current song to update the title when it changes.
In flexx.event
, a Component
object keeps track of its observers
(reactions) and notifies them when there are changes. In our music player
example, there would be a property “current_song”, and a reaction to
take action when it changes.
As is common in the observer pattern, the reactions keep track of the
objects that they observe. Therefore both Reaction
and Component
objects have a dispose()
method for cleaning up.
Signals and slots¶
The Qt GUI toolkit makes use of a mechanism called “signals and slots” as
an easy way to connect different components of an application. In
flexx.event
signals translate to properties and assoctated setter actions,
and slots to the reactions that connect to them.
Although signals and slots provide a convenient mechanism, they make it easy to create “spaghetti apps” where the information flows all over the place, which is exactly what frameworks like Flux, Veux and Flexx try to overcome.
Overloadable event handlers¶
In Qt, the “event system” consists of methods that handles an event, which
can be overloaded in subclasses to handle an event differently. In
flexx.event
, actions and reactions can similarly be re-implemented in
subclasses, and these can call the original handler using super()
if needed.
Publish-subscribe pattern¶
In pub-sub, publishers generate messages identified by a ‘topic’, and subscribers can subscribe to such topics. There can be zero or more publishers and zero or more subscribers to any topic.
In flexx.event
a Component object can play the role of a broker.
Publishers can simply emit events. The event type represents the message
topic. Subscribers are represented by handlers.