├── flexx ├── util │ ├── __init__.py │ ├── tests │ │ ├── test_cli.py │ │ └── test_logging.py │ ├── freeze.py │ ├── testing.py │ └── getresource.py ├── resources │ ├── README.md │ ├── flexx.ico │ ├── flexx.png │ └── iconmaker.py ├── flx_flask.py ├── README.md ├── ui │ ├── README.md │ ├── pywidgets │ │ └── __init__.py │ ├── layouts │ │ ├── __init__.py │ │ ├── _layout.py │ │ ├── _pinboard.py │ │ ├── _stack.py │ │ └── _form.py │ ├── __init__.py │ └── widgets │ │ ├── __init__.py │ │ ├── _markdown.py │ │ ├── _iframe.py │ │ ├── _plotly.py │ │ ├── _group.py │ │ ├── _color.py │ │ └── _label.py ├── flx.py ├── event │ ├── __init__.py │ ├── tests │ │ ├── test_no_dups.py │ │ ├── test_asyncio.py │ │ ├── test_dict.py │ │ └── test_threading.py │ ├── _attribute.py │ ├── _dict.py │ └── _emitter.py ├── app │ ├── tests │ │ └── test_app.py │ ├── __init__.py │ └── live_tester.py ├── _config.py └── __init__.py ├── flexxamples ├── __init__.py ├── demos │ ├── __init__.py │ ├── demo.py │ ├── circles.py │ ├── sine.py │ ├── themed_form.py │ ├── mondriaan.py │ ├── video_viewer.py │ ├── plotly_gdp.py │ └── app_layout.py ├── howtos │ ├── __init__.py │ ├── static │ │ ├── js │ │ │ ├── script.js │ │ │ └── data.json │ │ └── css │ │ │ └── style.css │ ├── hello_world.py │ ├── flask_backend.py │ ├── serve_multiple1.py │ ├── serve_multiple2.py │ ├── scrollable.py │ ├── cookies.py │ ├── splitters.py │ ├── basic_emit.py │ ├── local_assets.py │ ├── oneliners.py │ ├── serve_with_flask.py │ ├── serve_ssl.py │ ├── redirect.py │ ├── react_to_props.py │ ├── deep_event_connections.py │ ├── jquery.py │ ├── serve_with_asgineer.py │ ├── mutual_dependent_props.py │ ├── threaded.py │ ├── editor_ace.py │ ├── python_side_widget2.py │ ├── adding_handlers.py │ ├── tree.py │ ├── icons.py │ ├── buttons.py │ ├── array_props.py │ ├── editor_cm.py │ ├── bokehdemo.py │ ├── store.py │ ├── python_in_js.py │ ├── serve_with_aiohttp.py │ ├── serve_data.py │ ├── box_vs_fix_layout.py │ ├── send_data.py │ ├── bootstrap.py │ ├── control_with_keys.py │ └── flask_server.py ├── testers │ ├── __init__.py │ ├── deep2.py │ ├── minsize.py │ ├── find_prime.py │ ├── errors.py │ ├── mouse_and_touch.py │ ├── deep1.py │ ├── tricky_events.py │ └── ws_speed.py ├── ui_usage │ ├── label.py │ ├── tabs.py │ ├── dropdown_container.py │ ├── group.py │ ├── markdown.py │ └── stack.py └── README.md ├── docs ├── examples │ └── README.md ├── .requirements.txt ├── cli.rst ├── event │ ├── index.rst │ └── api.rst ├── util │ ├── minify.rst │ ├── config.rst │ ├── logging.rst │ └── index.rst ├── reference.rst ├── about.rst ├── ui │ └── index.rst ├── guide │ ├── index.rst │ ├── notebooks.rst │ ├── assets_data.rst │ └── debugging.rst ├── app │ ├── index.rst │ └── api.rst ├── scripts │ ├── flexxdocsgen.py │ └── gencommonast.py ├── configuration.rst ├── index.rst ├── overview.rst ├── contributing.rst ├── motivation.rst ├── start.rst └── freeze.rst ├── demo ├── README.md ├── Dockerfile └── demo.py ├── tasks ├── demo.py ├── _config.py ├── __main__.py ├── pscript.py ├── help.py ├── README.md ├── clean.py ├── __init__.py ├── copyright.py ├── ws.py └── docs.py ├── MANIFEST.in ├── .gitignore ├── LICENSE ├── setup.cfg ├── .github └── workflows │ └── ci.yml └── CONTRIBUTING.md /flexx/util/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /flexxamples/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /flexxamples/demos/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /flexxamples/howtos/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /flexxamples/testers/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /flexxamples/howtos/static/js/script.js: -------------------------------------------------------------------------------- 1 | alert("script loaded"); -------------------------------------------------------------------------------- /docs/examples/README.md: -------------------------------------------------------------------------------- 1 | This is where all the autogenerated examples go. 2 | -------------------------------------------------------------------------------- /flexx/resources/README.md: -------------------------------------------------------------------------------- 1 | This is where static resources that Flexx needs are placed. -------------------------------------------------------------------------------- /docs/.requirements.txt: -------------------------------------------------------------------------------- 1 | sphinxcontrib-napoleon 2 | tornado 3 | pscript 4 | webruntime 5 | dialite 6 | -------------------------------------------------------------------------------- /flexx/resources/flexx.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flexxui/flexx/HEAD/flexx/resources/flexx.ico -------------------------------------------------------------------------------- /flexx/resources/flexx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flexxui/flexx/HEAD/flexx/resources/flexx.png -------------------------------------------------------------------------------- /flexx/flx_flask.py: -------------------------------------------------------------------------------- 1 | # flake8: noqa 2 | from .app._flaskhelpers import register_blueprints, serve, start_thread 3 | -------------------------------------------------------------------------------- /docs/cli.rst: -------------------------------------------------------------------------------- 1 | ---------------------- 2 | Command line interface 3 | ---------------------- 4 | 5 | .. automodule:: flexx.__main__ 6 | -------------------------------------------------------------------------------- /demo/README.md: -------------------------------------------------------------------------------- 1 | # Flexx demo 2 | 3 | This directory contains a simple demo server script, and a Dockerfile to deploy it as a server. 4 | -------------------------------------------------------------------------------- /docs/event/index.rst: -------------------------------------------------------------------------------- 1 | :orphan: 2 | 3 | ----------- 4 | flexx.event 5 | ----------- 6 | 7 | .. toctree:: 8 | :maxdepth: 2 9 | 10 | api 11 | -------------------------------------------------------------------------------- /flexx/README.md: -------------------------------------------------------------------------------- 1 | flexx package 2 | ------------- 3 | 4 | The root package for flexx.ui, flexx.app, flexx.event, 5 | and the combined flexx.flx namespace. 6 | -------------------------------------------------------------------------------- /flexx/ui/README.md: -------------------------------------------------------------------------------- 1 | flexx.ui subpackage 2 | ------------------- 3 | 4 | A GUI toolkit based on web technologies (HTML5/CSS/JS), with a Pythonic 5 | API. 6 | -------------------------------------------------------------------------------- /docs/util/minify.rst: -------------------------------------------------------------------------------- 1 | ======================= 2 | JavaScript minification 3 | ======================= 4 | 5 | 6 | .. autofunction:: flexx.util.minify.minify 7 | -------------------------------------------------------------------------------- /docs/reference.rst: -------------------------------------------------------------------------------- 1 | Reference 2 | ========= 3 | 4 | 5 | .. toctree:: 6 | :maxdepth: 2 7 | 8 | ui/api 9 | app/api 10 | event/api 11 | util/index 12 | cli 13 | configuration -------------------------------------------------------------------------------- /flexx/ui/pywidgets/__init__.py: -------------------------------------------------------------------------------- 1 | """ Namespace for all PyWidgets (widgets that operate in Python). 2 | """ 3 | # flake8: noqa 4 | 5 | from .. import PyWidget 6 | from ._filebrowser import FileBrowserWidget 7 | -------------------------------------------------------------------------------- /docs/about.rst: -------------------------------------------------------------------------------- 1 | About 2 | ===== 3 | 4 | 5 | .. toctree:: 6 | :maxdepth: 2 7 | 8 | releasenotes 9 | contributing 10 | freeze 11 | overview 12 | motivation 13 | eventsystembackground 14 | -------------------------------------------------------------------------------- /flexxamples/howtos/static/css/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: #21c2ff; 3 | } 4 | button.border-red{ 5 | border: 4px solid red; 6 | } 7 | button.border-green{ 8 | border: 4px solid green; 9 | width: 100%; 10 | } -------------------------------------------------------------------------------- /tasks/demo.py: -------------------------------------------------------------------------------- 1 | from invoke import task 2 | 3 | @task 4 | def demo(ctx): 5 | """show a quick Flexx demo 6 | """ 7 | from flexx.ui.examples.demo import Demo 8 | from flexx import app 9 | app.launch(Demo) 10 | app.run() 11 | -------------------------------------------------------------------------------- /tasks/_config.py: -------------------------------------------------------------------------------- 1 | """ 2 | Config and definitions specific to Flexx. 3 | """ 4 | 5 | import os.path as op 6 | 7 | from . import ROOT_DIR, THIS_DIR # noqa 8 | 9 | NAME = 'flexx' 10 | DOC_DIR = op.join(ROOT_DIR, 'docs') 11 | DOC_BUILD_DIR = op.join(DOC_DIR, '_build') 12 | -------------------------------------------------------------------------------- /docs/ui/index.rst: -------------------------------------------------------------------------------- 1 | :orphan: 2 | 3 | -------- 4 | flexx.ui 5 | -------- 6 | 7 | The ui module provides a variety of :class:`widget ` 8 | classes based on :class:`JsComponent `. 9 | 10 | .. toctree:: 11 | :maxdepth: 1 12 | 13 | api 14 | -------------------------------------------------------------------------------- /docs/util/config.rst: -------------------------------------------------------------------------------- 1 | =================== 2 | Configuration class 3 | =================== 4 | 5 | This page documents the Config class. For learning how to configure Flexx, 6 | see :func:`configuring flexx `. 7 | 8 | .. autoclass:: flexx.util.config.Config 9 | :members: 10 | -------------------------------------------------------------------------------- /docs/guide/index.rst: -------------------------------------------------------------------------------- 1 | Guide 2 | ===== 3 | 4 | .. toctree:: 5 | :maxdepth: 2 6 | 7 | widget_basics 8 | widgets_components 9 | event_system 10 | reactions 11 | pscript_modules_scope 12 | assets_data 13 | patterns 14 | running 15 | debugging 16 | -------------------------------------------------------------------------------- /flexx/flx.py: -------------------------------------------------------------------------------- 1 | """ 2 | The flexx.flx module provides a namspace combining all the things from 3 | flexx.app, flexx.event, and flexx.ui. 4 | """ 5 | 6 | # flake8: noqa 7 | 8 | from . import __version__, config, set_log_level 9 | from .event import * 10 | from .app import * 11 | from .ui import * 12 | -------------------------------------------------------------------------------- /tasks/__main__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Make this module itself executable as an alias for invoke. 3 | """ 4 | 5 | import sys 6 | import subprocess 7 | 8 | cmd = ['invoke'] 9 | if len(sys.argv) == 1: 10 | cmd.append('help') 11 | else: 12 | cmd.extend(sys.argv[1:]) 13 | 14 | subprocess.check_call(cmd) 15 | -------------------------------------------------------------------------------- /tasks/pscript.py: -------------------------------------------------------------------------------- 1 | from invoke import task 2 | 3 | # todo: also print meta info like globals etc. 4 | 5 | @task(help=dict(code='the Python code to transpile')) 6 | def py2js(ctx, code): 7 | """transpile given Python code to JavaScript 8 | """ 9 | from pscript import py2js 10 | print(py2js(code)) 11 | -------------------------------------------------------------------------------- /docs/util/logging.rst: -------------------------------------------------------------------------------- 1 | ================ 2 | Logging in Flexx 3 | ================ 4 | 5 | Flexx uses the standard Python logging facilities, but adds functionality, 6 | most notably the ability to filter messages by a string or regexp. 7 | 8 | .. autofunction:: flexx.set_log_level 9 | 10 | .. autoclass:: flexx.util.logging.capture_log 11 | -------------------------------------------------------------------------------- /docs/app/index.rst: -------------------------------------------------------------------------------- 1 | :orphan: 2 | 3 | --------- 4 | flexx.app 5 | --------- 6 | 7 | The app module builds upont the ``event`` module to provide 8 | :class:`PyComponent ` and 9 | :class:`JsComponent `, and handles the connection 10 | between Python and a browser. 11 | 12 | .. toctree:: 13 | :maxdepth: 2 14 | 15 | api 16 | -------------------------------------------------------------------------------- /flexx/util/tests/test_cli.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import subprocess 3 | import flexx 4 | 5 | from flexx.util.testing import run_tests_if_main, raises 6 | 7 | 8 | def test_cli(): 9 | cmd = [sys.executable, '-m', 'flexx', 'version'] 10 | v = subprocess.check_output(cmd, stderr=subprocess.STDOUT).decode().strip() 11 | assert v == flexx.__version__ 12 | 13 | 14 | run_tests_if_main() 15 | -------------------------------------------------------------------------------- /flexxamples/ui_usage/label.py: -------------------------------------------------------------------------------- 1 | """ 2 | Simple use of a label 3 | """ 4 | 5 | from flexx import flx 6 | 7 | 8 | class Example(flx.Widget): 9 | 10 | def init(self): 11 | self.label = flx.Label(text="Number:") 12 | self.label = flx.Label(html=" 45 ") 13 | 14 | 15 | if __name__ == '__main__': 16 | m = flx.launch(Example, 'default-browser') 17 | flx.run() 18 | -------------------------------------------------------------------------------- /flexx/ui/layouts/__init__.py: -------------------------------------------------------------------------------- 1 | """ Namespace for all layout widgets. 2 | """ 3 | 4 | # flake8: noqa 5 | 6 | from .._widget import Widget 7 | 8 | from ._layout import Layout 9 | from ._hv import HVLayout, HBox, VBox, HFix, VFix, HSplit, VSplit 10 | from ._stack import StackLayout 11 | from ._tabs import TabLayout 12 | from ._pinboard import PinboardLayout 13 | from ._form import FormLayout 14 | from ._grid import GridLayout 15 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include LICENSE README.md 2 | recursive-include docs * 3 | recursive-include demo * 4 | recursive-include flexx README.md 5 | 6 | prune docs/_build 7 | prune docs/ext/__pycache__ 8 | 9 | recursive-include tasks * 10 | recursive-include flexx/app/tests * 11 | recursive-include flexx/event/tests * 12 | recursive-include flexx/util/tests * 13 | 14 | global-exclude .git* 15 | global-exclude *.pyo 16 | global-exclude *.pyc 17 | -------------------------------------------------------------------------------- /docs/util/index.rst: -------------------------------------------------------------------------------- 1 | :orphan: 2 | 3 | ========== 4 | flexx.util 5 | ========== 6 | 7 | Flexx' ``util`` module contains utilities that are used internally, 8 | some of which can be useful outside of Flexx. Note that most modules 9 | in ``flexx.util`` are independent; using them does not import any other 10 | Flexx modules. 11 | 12 | 13 | .. toctree:: 14 | :maxdepth: 1 15 | 16 | config 17 | logging 18 | minify 19 | -------------------------------------------------------------------------------- /tasks/help.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | 3 | from invoke import task 4 | 5 | from ._config import NAME 6 | 7 | 8 | @task 9 | def help(ctx): 10 | """Get info on usage. 11 | """ 12 | 13 | print('Developer tools for project %s\n' % NAME.capitalize()) 14 | print(' invoke [arg] to run a task') 15 | print(' invoke --help to get info on a task') 16 | print() 17 | subprocess.call('invoke --list') 18 | -------------------------------------------------------------------------------- /flexxamples/howtos/hello_world.py: -------------------------------------------------------------------------------- 1 | # doc-export: Main 2 | """ 3 | Simple hello world following the recommended style of writing apps, 4 | using a custom widget that is populated in its ``init()``. 5 | """ 6 | 7 | 8 | from flexx import flx 9 | 10 | class Main(flx.Widget): 11 | 12 | def init(self): 13 | self.b1 = flx.Button(text='Hello') 14 | self.b2 = flx.Button(text='World') 15 | 16 | if __name__ == '__main__': 17 | m = flx.launch(Main) 18 | flx.run() 19 | -------------------------------------------------------------------------------- /flexx/ui/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | The Flexx widgets. 3 | """ 4 | 5 | import logging 6 | logger = logging.getLogger(__name__) 7 | del logging 8 | 9 | # flake8: noqa 10 | 11 | # We follow the convention of having one module per widget class (or a 12 | # small set of closely related classes). In order not to pollute the 13 | # namespaces, we prefix the module names with an underscrore. 14 | 15 | from ._widget import Widget, PyWidget, create_element 16 | from .layouts import * 17 | from .widgets import * 18 | from .pywidgets import * 19 | -------------------------------------------------------------------------------- /flexxamples/howtos/flask_backend.py: -------------------------------------------------------------------------------- 1 | """ 2 | Simple use of a the markdown widget, 3 | using a custom widget that is populated in its ``init()``. 4 | """ 5 | 6 | from flexx import flx 7 | 8 | 9 | class Example(flx.Widget): 10 | 11 | def init(self): 12 | content = "# Welcome\n\n" \ 13 | "This flexx app is now served with flask! " 14 | flx.Markdown(content=content, style='background:#EAECFF;') 15 | 16 | if __name__ == '__main__': 17 | m = flx.launch(Example, 'default-browser', backend='flask') 18 | flx.run() 19 | -------------------------------------------------------------------------------- /flexxamples/ui_usage/tabs.py: -------------------------------------------------------------------------------- 1 | """ 2 | Simple example of TabLayout 3 | """ 4 | 5 | from flexx import flx 6 | 7 | 8 | class Example(flx.Widget): 9 | 10 | def init(self): 11 | with flx.TabLayout() as self.t: 12 | self.a = flx.MultiLineEdit(title='input', style='background:#a00;') 13 | self.b = flx.Widget(title='green', style='background:#0a0;') 14 | self.c = flx.Widget(title='blue', style='background:#00a;') 15 | 16 | 17 | if __name__ == '__main__': 18 | m = flx.launch(Example, 'default-browser') 19 | flx.run() 20 | -------------------------------------------------------------------------------- /flexx/ui/layouts/_layout.py: -------------------------------------------------------------------------------- 1 | """ Layout 2 | """ 3 | 4 | from . import Widget 5 | 6 | 7 | class Layout(Widget): 8 | """ Abstract class for widgets that layout their child widgets. 9 | """ 10 | 11 | CSS = """ 12 | 13 | body { 14 | margin: 0; 15 | padding: 0; 16 | } 17 | 18 | .flx-Layout { 19 | /* sizing of widgets/layouts inside layout is defined per layout */ 20 | width: 100%; 21 | height: 100%; 22 | margin: 0; 23 | padding: 0; 24 | border-spacing: 0; 25 | border: 0; 26 | } 27 | 28 | """ 29 | -------------------------------------------------------------------------------- /flexx/event/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | The Flexx event system. 3 | """ 4 | 5 | import logging 6 | logger = logging.getLogger(__name__) 7 | del logging 8 | 9 | import sys 10 | assert sys.version_info > (3, 5), "Flexx.event needs Python 3.5+" 11 | del sys 12 | 13 | # flake8: noqa 14 | from ._dict import Dict 15 | from ._loop import Loop, loop 16 | from ._action import Action, action 17 | from ._reaction import Reaction, reaction 18 | from ._emitter import emitter, Emitter 19 | from ._attribute import Attribute 20 | from ._property import * 21 | from ._component import Component, mutate_array, mutate_dict 22 | -------------------------------------------------------------------------------- /docs/scripts/flexxdocsgen.py: -------------------------------------------------------------------------------- 1 | """ Run scripts to generate docs for Flexx 2 | """ 3 | 4 | #import examplesgenerator 5 | import genuiclasses 6 | import genexamples 7 | import gencommonast 8 | 9 | 10 | def init(): 11 | print('GENERATING DOCS ...') 12 | 13 | print(' Generating docs for UI classes.') 14 | genuiclasses.main() 15 | print(' Generating examples.') 16 | genexamples.main() 17 | 18 | 19 | def clean(app, *args): 20 | genuiclasses.clean() 21 | genexamples.clean() 22 | 23 | 24 | def setup(app): 25 | init() 26 | app.connect('build-finished', clean) 27 | 28 | -------------------------------------------------------------------------------- /flexxamples/howtos/serve_multiple1.py: -------------------------------------------------------------------------------- 1 | """ 2 | Import apps from other example modules, and host these as separate apps 3 | from one process. 4 | """ 5 | 6 | from flexx import app 7 | 8 | from flexxamples.demos.monitor import Monitor 9 | from flexxamples.demos.chatroom import ChatRoom 10 | from flexxamples.demos.demo import Demo 11 | from flexxamples.demos.colab_painting import ColabPainting 12 | 13 | 14 | if __name__ == '__main__': 15 | # This example is setup as a server app 16 | app.serve(Monitor) 17 | app.serve(ChatRoom) 18 | app.serve(ColabPainting) 19 | app.serve(Demo) 20 | app.start() 21 | -------------------------------------------------------------------------------- /docs/configuration.rst: -------------------------------------------------------------------------------- 1 | ----------------- 2 | Configuring Flexx 3 | ----------------- 4 | 5 | This page lists the configuration options available to Flexx, implemented 6 | via the :class:`Config ` class. Configuration 7 | options are read from ``/.flexx.cfg`` (check 8 | ``flexx.util.config.appdata_dir()`` for the actual location), 9 | and can also be set using 10 | environment variables and command line arguments, as explained below. 11 | Alternatively, options can be set directly in Python via 12 | ``flexx.config.foo = 3``. 13 | 14 | .. autodata:: flexx.config 15 | :annotation: 16 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | .. Flexx documentation master file, created by 2 | sphinx-quickstart on Fri Apr 10 15:35:18 2015. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | Welcome to Flexx's documentation! 7 | ================================= 8 | 9 | .. automodule:: flexx 10 | 11 | 12 | Contents 13 | -------- 14 | 15 | .. toctree:: 16 | :maxdepth: 2 17 | 18 | start 19 | guide/index 20 | reference 21 | examples/index 22 | about 23 | 24 | 25 | Indices and tables 26 | ------------------ 27 | 28 | * :ref:`genindex` 29 | * :ref:`modindex` 30 | * :ref:`search` 31 | -------------------------------------------------------------------------------- /flexxamples/README.md: -------------------------------------------------------------------------------- 1 | # Flexxamples 2 | 3 | This package/directory contains the examples for the Flexx UI library. The 4 | examples are divided in three categories. Most examples (in particular the 5 | ones in `demos`) can be imported and used as widgets in larger apps, or 6 | served alongside your main app. Each example can also be run as a script. 7 | 8 | Note that most examples are written by subclassing ``Widget`` making them 9 | work standalone in Flexx' html documentation. Note that if you are making 10 | a desktop application, you probably want to use ``PyWidget``. 11 | 12 | Example notebooks are available at https://github.com/flexxui/flexx-notebooks 13 | -------------------------------------------------------------------------------- /flexxamples/howtos/serve_multiple2.py: -------------------------------------------------------------------------------- 1 | # doc-export: MultiApp 2 | """ 3 | Import apps from other example modules, and host these as widgets in a 4 | single app. 5 | """ 6 | 7 | from flexx import flx 8 | 9 | from flexxamples.demos.drawing import Drawing 10 | from flexxamples.howtos.splitters import Split 11 | from flexxamples.demos.twente import Twente 12 | 13 | 14 | class MultiApp(flx.TabLayout): 15 | def init(self): 16 | Drawing(title='Drawing') 17 | Split(title='Split') 18 | Twente(title='Twente') 19 | 20 | 21 | if __name__ == '__main__': 22 | # This example is setup as a desktop app 23 | flx.launch(MultiApp) 24 | flx.run() 25 | -------------------------------------------------------------------------------- /demo/Dockerfile: -------------------------------------------------------------------------------- 1 | # mypaas.service = flexx.demo 2 | # 3 | # mypaas.url = https://demo.flexx.app 4 | # mypaas.url = https://demo1.flexx.app 5 | # 6 | # mypaas.scale = 0 7 | # mypaas.maxmem = 256m 8 | 9 | FROM ubuntu:20.04 10 | 11 | RUN apt update \ 12 | && apt install -y python3-pip \ 13 | && pip3 --no-cache-dir install pip --upgrade \ 14 | && pip3 --no-cache-dir install psutil markdown tornado 15 | 16 | WORKDIR /root 17 | COPY . . 18 | 19 | RUN pip3 --no-cache-dir install dialite webruntime pscript \ 20 | && pip3 --no-cache-dir install https://github.com/flexxui/flexx/archive/master.zip 21 | 22 | CMD ["python3", "demo.py", "--flexx-hostname=0.0.0.0", "--flexx-port=80"] 23 | -------------------------------------------------------------------------------- /flexxamples/howtos/scrollable.py: -------------------------------------------------------------------------------- 1 | # doc-export: ScrollExample 2 | """ 3 | Example that shows how to make the content of a widget scrollable. 4 | It comes down to setting a style attribute: "overflow-y: auto;". 5 | """ 6 | 7 | from flexx import flx 8 | 9 | 10 | class ScrollExample(flx.Widget): 11 | 12 | CSS = """ 13 | .flx-ScrollExample { 14 | overflow-y: scroll; // scroll or auto 15 | } 16 | """ 17 | 18 | def init(self): 19 | 20 | with flx.Widget(): 21 | for i in range(100): 22 | flx.Button(text="button " + str(i)) 23 | 24 | 25 | if __name__ == '__main__': 26 | m = flx.launch(ScrollExample) 27 | flx.run() 28 | -------------------------------------------------------------------------------- /flexxamples/ui_usage/dropdown_container.py: -------------------------------------------------------------------------------- 1 | """ 2 | Simple use of a dropdown containing a tree widget 3 | """ 4 | 5 | from flexx import flx 6 | 7 | 8 | class Example(flx.Widget): 9 | 10 | CSS = ''' 11 | .flx-DropdownContainer > .flx-TreeWidget { 12 | min-height: 150px; 13 | } 14 | ''' 15 | 16 | def init(self): 17 | # A nice and cosy tree view 18 | with flx.DropdownContainer(text='Scene graph'): 19 | with flx.TreeWidget(max_selected=1): 20 | for i in range(20): 21 | flx.TreeItem(text='foo %i' % i, checked=False) 22 | 23 | 24 | if __name__ == '__main__': 25 | m = flx.launch(Example) 26 | flx.run() 27 | -------------------------------------------------------------------------------- /flexxamples/ui_usage/group.py: -------------------------------------------------------------------------------- 1 | """ 2 | Simple use of a group containing a few widgets 3 | """ 4 | 5 | from flexx import flx 6 | 7 | 8 | class Example(flx.Widget): 9 | 10 | def init(self): 11 | with flx.GroupWidget(title='A silly panel'): 12 | with flx.VBox(): 13 | self.progress = flx.ProgressBar(min=0, max=9, 14 | text='Clicked {value} times') 15 | self.but = flx.Button(text='click me') 16 | 17 | @flx.reaction('but.pointer_down') 18 | def _button_pressed(self, *events): 19 | self.progress.set_value(self.progress.value + 1) 20 | 21 | 22 | if __name__ == '__main__': 23 | m = flx.launch(Example) 24 | flx.run() 25 | -------------------------------------------------------------------------------- /flexxamples/ui_usage/markdown.py: -------------------------------------------------------------------------------- 1 | """ 2 | Simple use of a the markdown widget, 3 | using a custom widget that is populated in its ``init()``. 4 | """ 5 | 6 | from flexx import flx 7 | 8 | 9 | class Example(flx.Widget): 10 | 11 | def init(self): 12 | content = "# Welcome\n\n" \ 13 | "Hello. Welcome to my **website**." \ 14 | "This is an example of a widget container for markdown content. " \ 15 | "The content can be text or a link.\n\n" 16 | content += "\n\n".join(["a new line to test long files" for a in range(100)]) 17 | flx.Markdown(content=content, style='background:#EAECFF;height:60%;') 18 | 19 | 20 | if __name__ == '__main__': 21 | m = flx.launch(Example, 'default-browser') 22 | flx.run() 23 | -------------------------------------------------------------------------------- /flexxamples/demos/demo.py: -------------------------------------------------------------------------------- 1 | # doc-export: Demo 2 | """ 3 | A demo with few lines of code with some fancy widgets, which works 4 | as an exported app, so it can be embedded e.g. on the main page. 5 | """ 6 | 7 | from flexx import flx 8 | from flexxamples.demos.splines import Splines 9 | from flexxamples.demos.twente import Twente 10 | from flexxamples.demos.drawing import Drawing 11 | 12 | class Demo(flx.Widget): 13 | 14 | def init(self): 15 | with flx.TabLayout(): 16 | Splines(title='Spline demo') 17 | Twente(title='Temperature vis') 18 | Drawing(title='Drawing app') 19 | flx.YoutubeWidget(title='Video') 20 | 21 | 22 | if __name__ == '__main__': 23 | a = flx.App(Demo, title='Flexx demo') 24 | m = a.launch() 25 | flx.run() 26 | -------------------------------------------------------------------------------- /docs/guide/notebooks.rst: -------------------------------------------------------------------------------- 1 | :orphan: 2 | 3 | Notebook tutorials 4 | ------------------ 5 | 6 | * `Using the webruntime module `_ 7 | * `The basics of PScript `_ 8 | * `The basics of the event system `_ 9 | * `The basics of flexx.app `_ 10 | * `Basic GUI building `_ 11 | 12 | (Note that these `do not work in JupyterLab `_ 13 | at the moment.) 14 | -------------------------------------------------------------------------------- /flexxamples/howtos/cookies.py: -------------------------------------------------------------------------------- 1 | """ 2 | Mmmm, cookies ... 3 | Small example for using cookies to (securely) store user data accross sessions. 4 | """ 5 | 6 | from flexx import flx 7 | 8 | 9 | class Cookies(flx.PyWidget): 10 | 11 | def init(self): 12 | 13 | with flx.Widget(): 14 | flx.Label(text='Refreshing the page should ' 15 | 'maintain the value of the line edit.') 16 | self.edit = flx.LineEdit(placeholder_text='username', 17 | text=self.session.get_cookie('username', '')) 18 | 19 | @flx.reaction('edit.text') 20 | def _update_cookie(self, *events): 21 | self.session.set_cookie('username', self.edit.text) 22 | 23 | 24 | if __name__ == '__main__': 25 | m = flx.launch(Cookies, 'browser') 26 | flx.start() 27 | -------------------------------------------------------------------------------- /flexxamples/howtos/splitters.py: -------------------------------------------------------------------------------- 1 | # doc-export: Split 2 | """ 3 | Splitter widgets are cool! 4 | """ 5 | 6 | from flexx import flx 7 | 8 | 9 | class Split(flx.Widget): 10 | 11 | def init(self): 12 | 13 | with flx.HSplit(): 14 | flx.Widget(style='background:#f00') 15 | with flx.VSplit(): 16 | flx.Widget(style='background:#0f0') 17 | with flx.HSplit(): 18 | flx.Widget(style='background:#ff0') 19 | with flx.VSplit(): 20 | flx.Widget(style='background:#f0f') 21 | with flx.HSplit(): 22 | flx.Widget(style='background:#0ff') 23 | flx.Widget(style='background:#00f') 24 | 25 | 26 | if __name__ == '__main__': 27 | m = flx.launch(Split) 28 | flx.run() 29 | -------------------------------------------------------------------------------- /flexx/ui/widgets/__init__.py: -------------------------------------------------------------------------------- 1 | """ Namespace for all widgets (that are not layouts). 2 | """ 3 | 4 | # flake8: noqa 5 | 6 | from .. import Widget 7 | 8 | from ._button import BaseButton, Button, ToggleButton, RadioButton, CheckBox 9 | from ._lineedit import LineEdit, MultiLineEdit 10 | from ._label import Label 11 | from ._group import GroupWidget 12 | from ._iframe import IFrame 13 | from ._canvas import CanvasWidget 14 | from ._color import ColorSelectWidget 15 | from ._media import ImageWidget, VideoWidget, YoutubeWidget 16 | 17 | from ._progressbar import ProgressBar 18 | from ._slider import Slider, RangeSlider 19 | from ._tree import TreeWidget, TreeItem 20 | from ._dropdown import ComboBox, DropdownContainer 21 | 22 | from ._plotwidget import PlotWidget 23 | from ._plotly import PlotlyWidget 24 | from ._bokeh import BokehWidget 25 | from ._markdown import Markdown 26 | -------------------------------------------------------------------------------- /flexxamples/howtos/basic_emit.py: -------------------------------------------------------------------------------- 1 | """ 2 | Example demonstrating the emitting of events using the emit() method. 3 | 4 | The "!" in the event name is to supress a warning for connecting to an 5 | event that is not known beforehand (i.e. there is no corresponding 6 | property or emitter). 7 | """ 8 | 9 | from flexx import event 10 | 11 | class Basic(event.Component): 12 | 13 | @event.reaction('!foo') 14 | def on_foo(self, *events): 15 | print('foo reaction called with %i events' % len(events)) 16 | 17 | @event.reaction('!bar') 18 | def on_bar(self, *events): 19 | print('bar reaction called with %i events' % len(events)) 20 | 21 | b = Basic() 22 | 23 | # Emit dummy events 24 | b.emit('foo', {}) 25 | b.emit('foo', {}) 26 | b.emit('bar', {}) 27 | b.emit('spam', {}) # we can emit this, but nobody's listening 28 | 29 | # Handle events 30 | event.loop.iter() 31 | -------------------------------------------------------------------------------- /flexx/app/tests/test_app.py: -------------------------------------------------------------------------------- 1 | from flexx.util.testing import run_tests_if_main, raises 2 | 3 | from flexx import app, event 4 | 5 | 6 | class MyPropClass1(app.PyComponent): 7 | foo = event.IntProp(1, settable=True) 8 | 9 | 10 | class MyPropClass2(MyPropClass1): 11 | def init(self, foo_val=11): 12 | self.set_foo(foo_val) 13 | 14 | 15 | def test_launching_with_props(): 16 | 17 | m = app.launch(MyPropClass1) 18 | assert m.foo == 1 19 | m.session.close() 20 | 21 | m = app.App(MyPropClass1, foo=3).launch() 22 | assert m.foo == 3 23 | m.session.close() 24 | 25 | 26 | def test_launching_with_init_args(): 27 | 28 | m = app.launch(MyPropClass2) 29 | event.loop.iter() 30 | assert m.foo == 11 31 | m.session.close() 32 | 33 | m = app.App(MyPropClass2, 13).launch() 34 | event.loop.iter() 35 | assert m.foo == 13 36 | m.session.close() 37 | 38 | 39 | run_tests_if_main() 40 | -------------------------------------------------------------------------------- /flexx/event/tests/test_no_dups.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | def test_that_tests_dont_have_multiple_functions_with_same_name(): 4 | dir = os.path.dirname(__file__) 5 | for fname in os.listdir(dir): 6 | if not (fname.startswith('test_') and fname.endswith('.py')): 7 | continue 8 | print(fname) 9 | text = open(os.path.join(dir, fname), 'rb').read().decode() 10 | func_names = set() 11 | 12 | for line in text.splitlines(): 13 | line = line.split('(')[0].strip() 14 | if line.startswith('def '): 15 | func_name = line[4:] 16 | if func_name.startswith('test_'): 17 | print(func_name) 18 | assert func_name not in func_names, (fname, func_name) 19 | func_names.add(func_name) 20 | 21 | 22 | if __name__ == '__main__': 23 | test_that_tests_dont_have_multiple_functions_with_same_name() 24 | -------------------------------------------------------------------------------- /flexxamples/testers/deep2.py: -------------------------------------------------------------------------------- 1 | """ 2 | Example to mix BoxPanel and VBox, which at some point failed in Chrome. Now 3 | Flexx takes precautions to make it work. This example is to test that it 4 | still works. 5 | """ 6 | 7 | from flexx import app, ui 8 | 9 | 10 | class Red(ui.Widget): 11 | CSS = '.flx-Red { background: #ff0000;}' 12 | 13 | 14 | class Deep2(ui.Widget): 15 | 16 | def init(self): 17 | 18 | with ui.VBox(): 19 | 20 | ui.Label(text='Widgets in BoxPanels in a widget in a vbox') 21 | 22 | with ui.Widget(flex=1): 23 | with ui.VFix(): 24 | with ui.HFix(): 25 | Red(flex=1) 26 | Red(flex=1) 27 | with ui.HFix(): 28 | Red(flex=1) 29 | Red(flex=1) 30 | 31 | 32 | if __name__ == '__main__': 33 | m = app.launch(Deep2, 'app') 34 | app.run() 35 | -------------------------------------------------------------------------------- /flexxamples/howtos/local_assets.py: -------------------------------------------------------------------------------- 1 | # doc-export: LocalAssets 2 | """ 3 | Simple hello world app loading local assets. 4 | """ 5 | 6 | from flexx import flx 7 | import os 8 | 9 | BASE_DIR = os.getcwd() 10 | 11 | with open(BASE_DIR + '/static/css/style.css') as f: 12 | style = f.read() 13 | 14 | with open(BASE_DIR + '/static/js/script.js') as f: 15 | script = f.read() 16 | 17 | flx.assets.associate_asset(__name__, 'style.css', style) 18 | flx.assets.associate_asset(__name__, 'script.js', script) 19 | 20 | 21 | class Main(flx.Widget): 22 | def init(self): 23 | flx.Widget(flex=1) 24 | with flx.VBox(): 25 | with flx.HBox(): 26 | self.b1 = flx.Button(text='Hello', css_class="border-red", flex=1) 27 | self.b2 = flx.Button(text='World', css_class="border-green", flex=1) 28 | flx.Widget(flex=1) 29 | 30 | 31 | if __name__ == '__main__': 32 | m = flx.launch(Main) 33 | flx.run() 34 | -------------------------------------------------------------------------------- /docs/overview.rst: -------------------------------------------------------------------------------- 1 | Flexx overview 2 | -------------- 3 | 4 | .. image:: _static/overview.svg 5 | 6 | The image above outlines the structure of Flexx. 7 | The *event* module provides a powerful property and event system that 8 | makes it easy to connect different parts of your application. Central to 9 | the event system is the ``Component`` class. 10 | The *app* module runs the server to which the web runtime connects (via a 11 | websocket). Further, it extends the ``Component`` class into the 12 | ``PyComponent`` and ``JsComponent`` classes. Objects of these classes 13 | live in Python and JavaScript respectively, but (can) have a representation 14 | on the other side, from which properties can be accessed, and actions be invoked. 15 | The *ui* module defines all widgets (based on ``JsComponent``). 16 | 17 | The external *webruntime* package is used to launch a browser looking like 18 | a dektop app. The *pscript* library is used throughout Flexx to compile 19 | Python code to JavaScript. 20 | -------------------------------------------------------------------------------- /flexxamples/howtos/oneliners.py: -------------------------------------------------------------------------------- 1 | """ 2 | This example demonstrates how a property can be set to follow other properties 3 | by setting the property to be a function, which is converted to a reaction by 4 | Flexx. 5 | 6 | This feature makes basic plumbing very easy, like e.g. showing values 7 | of properties in a label widget. 8 | """ 9 | 10 | # todo: also a one-liner to invoke actions from emitters (issue #425) 11 | 12 | from flexx import event 13 | 14 | 15 | class Person(event.Component): 16 | 17 | first_name = event.StringProp('Jane', settable=True) 18 | last_name = event.StringProp('Doe', settable=True) 19 | 20 | 21 | class Greeter(event.Component): 22 | 23 | message = event.StringProp('', settable=True) 24 | 25 | @event.reaction 26 | def show_message(self): 27 | print('Message:', self.message) 28 | 29 | p = Person() 30 | 31 | # This is the line that this is about 32 | g = Greeter(message=lambda: p.first_name + ' ' + p.last_name) 33 | 34 | p.set_first_name('Alice') 35 | 36 | event.loop.iter() 37 | -------------------------------------------------------------------------------- /tasks/README.md: -------------------------------------------------------------------------------- 1 | ----- 2 | tasks 3 | ----- 4 | 5 | Tools for developers, such as testing, building docs/website, etc. 6 | 7 | Usage:: 8 | 9 | invoke task ... 10 | invoke --help task 11 | 12 | This makes use of the invoke package to translate CLI commands to function 13 | calls. This package is set up so that new tasks can be added simply by adding 14 | a module that defines one or more tasks, this makes it easy to share tasks 15 | between projects. 16 | 17 | Each project must implement its own _config.py, so that the tasks themselves 18 | can be project-agnostic. 19 | 20 | Names that you can `from ._config import ...`: 21 | 22 | * NAME - the name of the project 23 | * THIS_DIR - the path of the tasks directory 24 | * ROOT_DIR - the root path of the repository 25 | * DOC_DIR - the path to the docs 26 | * DOC_BUILD_DIR - the path to where the docs are build 27 | * ... - more may be added, depending on what tasks are present 28 | 29 | 30 | License 31 | ------- 32 | 33 | Consider this code public domain unless stated otherwise. 34 | -------------------------------------------------------------------------------- /flexxamples/howtos/serve_with_flask.py: -------------------------------------------------------------------------------- 1 | """ 2 | Example of serving a Flexx app using a regular web server. In this case flask. 3 | See serve_with_aiohttp.py for a slightly more advanced example. 4 | """ 5 | 6 | from flask import Flask 7 | 8 | from flexx import flx 9 | from flexxamples.howtos.editor_cm import CodeEditor 10 | 11 | # Define an app 12 | 13 | class MyApp(flx.Widget): 14 | def init(self): 15 | with flx.HBox(): 16 | CodeEditor(flex=1) 17 | flx.Widget(flex=1) 18 | 19 | 20 | # Dump it to a dictionary of assets that we can serve. Make the main 21 | # page index.html. The link=0 means to pack the whole app into a single 22 | # html page (note that data (e.g. images) will still be separate). 23 | app = flx.App(MyApp) 24 | assets = app.dump('index.html', link=0) 25 | 26 | 27 | # Do the flask thing 28 | 29 | app = Flask(__name__) 30 | 31 | @app.route('/') 32 | def handler(): 33 | return assets['index.html'].decode() 34 | 35 | 36 | if __name__ == '__main__': 37 | app.run(host='localhost', port=8080) 38 | -------------------------------------------------------------------------------- /flexxamples/howtos/serve_ssl.py: -------------------------------------------------------------------------------- 1 | """ 2 | Flexx can be configured to use SSL. 3 | 4 | This example first creates a self-signed certificate and then uses it to create 5 | a SSL enabled web server (through Tornado ssl_option argument). 6 | 7 | Application served through this server is loaded on the browser with 'https' 8 | protocol and its websocket is using 'wss'. 9 | """ 10 | 11 | from flexx import flx 12 | 13 | # generate self-signed certificate for this example 14 | import os 15 | 16 | CERTFILE = '/tmp/self-signed.crt' 17 | KEYFILE = '/tmp/self-signed.key' 18 | 19 | os.system('openssl req -x509 -nodes -days 1 -batch -newkey rsa:2048 ' 20 | '-keyout %s -out %s' % (KEYFILE, CERTFILE)) 21 | 22 | # use the self-signed certificate as if specified in normal config 23 | flx.config.ssl_certfile = CERTFILE 24 | flx.config.ssl_keyfile = KEYFILE 25 | 26 | 27 | # Some very secret Model 28 | class Example(flx.Widget): 29 | def init(self): 30 | flx.Button(text='Secret Button') 31 | 32 | # run application 33 | flx.serve(Example, 'Example') 34 | flx.start() 35 | -------------------------------------------------------------------------------- /flexxamples/ui_usage/stack.py: -------------------------------------------------------------------------------- 1 | """ 2 | Example of VBox, HBox and StackLayout 3 | """ 4 | 5 | from flexx import event, flx 6 | 7 | 8 | class Example(flx.Widget): 9 | 10 | def init(self): 11 | with flx.VBox(): 12 | with flx.HBox(): 13 | self.buta = flx.Button(text='red') 14 | self.butb = flx.Button(text='green') 15 | self.butc = flx.Button(text='blue') 16 | flx.Widget(flex=1) # space filler 17 | with flx.StackLayout(flex=1) as self.stack: 18 | self.buta.w = flx.Widget(style='background:#a00;') 19 | self.butb.w = flx.Widget(style='background:#0a0;') 20 | self.butc.w = flx.Widget(style='background:#00a;') 21 | 22 | @event.reaction('buta.pointer_down', 'butb.pointer_down', 'butc.pointer_down') 23 | def _stacked_current(self, *events): 24 | button = events[-1].source 25 | self.stack.set_current(button.w) 26 | 27 | 28 | if __name__ == '__main__': 29 | m = flx.launch(Example, 'default-browser') 30 | flx.run() 31 | -------------------------------------------------------------------------------- /tasks/clean.py: -------------------------------------------------------------------------------- 1 | import os 2 | import shutil 3 | import fnmatch 4 | 5 | from invoke import task 6 | 7 | from ._config import ROOT_DIR, NAME 8 | 9 | 10 | @task 11 | def clean(ctx): 12 | """ clear all .pyc modules and __pycache__ dirs 13 | """ 14 | count1, count2 = 0, 0 15 | 16 | for root, dirnames, filenames in os.walk(ROOT_DIR): 17 | for dirname in dirnames: 18 | if dirname == '__pycache__': 19 | shutil.rmtree(os.path.join(root, dirname)) 20 | count1 += 1 21 | print('removed %i __pycache__ dirs' % count1) 22 | 23 | for root, dirnames, filenames in os.walk(ROOT_DIR): 24 | for filename in fnmatch.filter(filenames, '*.pyc'): 25 | os.remove(os.path.join(root, filename)) 26 | count2 += 1 27 | print('removed %i .pyc files' % count2) 28 | 29 | for dir in ['dist', 'build', NAME+'.egg-info', 'htmlcov']: 30 | dirname = os.path.join(ROOT_DIR, dir) 31 | if os.path.isdir(dirname): 32 | shutil.rmtree(dirname) 33 | print('Removed directory %r' % dir) 34 | -------------------------------------------------------------------------------- /flexxamples/howtos/redirect.py: -------------------------------------------------------------------------------- 1 | # doc-export: Redirect 2 | """ 3 | Example that demonstrates redirecting and opening a new page. 4 | This example only works when opened in a browser (not with 5 | ``launch(Redirect, 'app')``). 6 | 7 | Note that one can also open a webpage from Python: 8 | 9 | import webbrowser 10 | webbrowser.open('http://python.org') 11 | """ 12 | 13 | from flexx import flx 14 | 15 | 16 | class Redirect(flx.Widget): 17 | 18 | def init(self): 19 | self.but1 = flx.Button(text='Redirect') 20 | self.but2 = flx.Button(text='Open new page') 21 | 22 | @flx.reaction('but1.pointer_click') 23 | def on_redirect(self, *events): 24 | global window 25 | window.location.href = 'http://python.org' # allow going back 26 | # window.location.replace('http://python.org') # hard redirect 27 | 28 | @flx.reaction('but2.pointer_click') 29 | def on_opennew(self, *events): 30 | global window 31 | window.open('http://python.org', '_blank') 32 | 33 | 34 | if __name__ == '__main__': 35 | m = flx.launch(Redirect, 'browser') 36 | flx.start() 37 | -------------------------------------------------------------------------------- /flexxamples/demos/circles.py: -------------------------------------------------------------------------------- 1 | # doc-export: Circles 2 | """ 3 | Example that shows animated circles. Note that it would probably be more 4 | efficient to use a canvas for this sort of thing. 5 | """ 6 | 7 | from time import time 8 | 9 | from flexx import flx 10 | 11 | 12 | class Circle(flx.Label): 13 | 14 | CSS = """ 15 | .flx-Circle { 16 | background: #f00; 17 | border-radius: 10px; 18 | width: 10px; 19 | height: 10px; 20 | } 21 | """ 22 | 23 | class Circles(flx.Widget): 24 | 25 | def init(self): 26 | with flx.PinboardLayout(): 27 | self._circles = [Circle() for i in range(32)] 28 | self.tick() 29 | 30 | def tick(self): 31 | global Math, window 32 | t = time() 33 | for i, circle in enumerate(self._circles): 34 | x = Math.sin(i*0.2 + t) * 30 + 50 35 | y = Math.cos(i*0.2 + t) * 30 + 50 36 | circle.apply_style(dict(left=x + '%', top=y + '%')) 37 | window.setTimeout(self.tick, 30) 38 | 39 | 40 | if __name__ == '__main__': 41 | m = flx.App(Circles).launch('app') 42 | flx.run() 43 | -------------------------------------------------------------------------------- /demo/demo.py: -------------------------------------------------------------------------------- 1 | """ 2 | Demo server. 3 | """ 4 | 5 | import sys 6 | import asyncio 7 | 8 | from flexx import app 9 | 10 | from flexxamples.demos.monitor import Monitor 11 | from flexxamples.demos.chatroom import ChatRoom 12 | from flexxamples.demos.demo import Demo 13 | from flexxamples.demos.colab_painting import ColabPainting 14 | from flexxamples.demos.d3_collision import CollisionDemo 15 | from flexxamples.demos.plotly_gdp import PlotlyGeoDemo 16 | 17 | 18 | async def exit_server_after_a_while(): 19 | # Exit the server after 12 hours, after which it will start up again 20 | # (using Docker with auto-restart). This makes sure that the server 21 | # is not bother too much in case we have a memory leak. 22 | await asyncio.sleep(12 * 3600) 23 | sys.exit() 24 | 25 | 26 | asyncio.ensure_future(exit_server_after_a_while()) 27 | 28 | 29 | if __name__ == "__main__": 30 | # This example is setup as a server app 31 | # app.serve(Monitor) 32 | # app.serve(ChatRoom) 33 | app.serve(ColabPainting) 34 | app.serve(CollisionDemo) 35 | # app.serve(PlotlyGeoDemo) # CORS fail? 36 | app.serve(Demo) 37 | app.start() 38 | -------------------------------------------------------------------------------- /flexxamples/howtos/react_to_props.py: -------------------------------------------------------------------------------- 1 | """ 2 | Example that demonstrates three ways to react to changes in properties. 3 | 4 | All reaction functions get called once when ``foo`` changes. In the first 5 | reaction, we have no information other than the current value of foo. 6 | In the other reactions we have more information about how `foo` changed. 7 | """ 8 | 9 | from flexx import event 10 | 11 | 12 | class Test(event.Component): 13 | 14 | foo = event.IntProp(0, settable=True) 15 | 16 | @event.reaction 17 | def react_to_foo_a(self): 18 | print('A: foo changed to %i' % self.foo) 19 | 20 | @event.reaction('foo') 21 | def react_to_foo_b(self, *events): 22 | # This function 23 | print('B: foo changed from %i to %i' % (events[0].old_value, 24 | events[-1].new_value)) 25 | 26 | @event.reaction('foo') 27 | def react_to_foo_c(self, *events): 28 | print('C: foo changed:') 29 | for ev in events: 30 | print(' from %i to %i' % (ev.old_value, ev.new_value)) 31 | 32 | 33 | c = Test() 34 | 35 | c.set_foo(3) 36 | c.set_foo(7) 37 | 38 | event.loop.iter() 39 | -------------------------------------------------------------------------------- /flexxamples/howtos/deep_event_connections.py: -------------------------------------------------------------------------------- 1 | # doc-export: DeepEventConnections 2 | """ 3 | Example that demonstrates how one can connect to events deep in the 4 | hierachy. Instead of using the star notation to select all children, 5 | one can use a double-star to select also the children's children, and 6 | their children, etc. 7 | """ 8 | 9 | from flexx import flx 10 | 11 | 12 | class DeepEventConnections(flx.Widget): 13 | 14 | def init(self): 15 | # Put a label and some sliders deep in the hierarchy 16 | 17 | with flx.VBox(): 18 | self.label = flx.Label() 19 | with flx.HFix(flex=1): 20 | for j in range(2): 21 | with flx.VBox(flex=1): 22 | for i in range(5): 23 | flx.Slider(value=i/5) 24 | 25 | @flx.reaction('!children**.value') 26 | def on_slider_change(self, *events): 27 | for ev in events: 28 | self.label.set_text('Slider %s changed to %f' % 29 | (ev.source.id, ev.new_value)) 30 | 31 | 32 | if __name__ == '__main__': 33 | m = flx.launch(DeepEventConnections) 34 | flx.run() 35 | -------------------------------------------------------------------------------- /flexxamples/demos/sine.py: -------------------------------------------------------------------------------- 1 | # doc-export: SineExample 2 | """ 3 | A sine, with sliders to manipulate phase and amplitude. 4 | """ 5 | 6 | from flexx import flx 7 | 8 | class SineExample(flx.Widget): 9 | 10 | def init(self): 11 | time = [i/100 for i in range(100)] 12 | with flx.VBox(): 13 | with flx.HBox(): 14 | flx.Label(text='Frequency:') 15 | self.slider1 = flx.Slider(min=1, max=10, value=5, flex=1) 16 | flx.Label(text='Phase:') 17 | self.slider2 = flx.Slider(min=0, max=6, value=0, flex=1) 18 | self.plot = flx.PlotWidget(flex=1, xdata=time, xlabel='time', 19 | ylabel='amplitude', title='a sinusoid') 20 | 21 | @flx.reaction 22 | def __update_amplitude(self, *events): 23 | global Math 24 | freq, phase = self.slider1.value, self.slider2.value 25 | ydata = [] 26 | for x in self.plot.xdata: 27 | ydata.append(Math.sin(freq*x*2*Math.PI+phase)) 28 | self.plot.set_data(self.plot.xdata, ydata) 29 | 30 | 31 | if __name__ == '__main__': 32 | m = flx.launch(SineExample) 33 | flx.run() 34 | -------------------------------------------------------------------------------- /flexxamples/howtos/jquery.py: -------------------------------------------------------------------------------- 1 | # doc-export: Example 2 | """ 3 | Example to demonstrate a jQuery widget. 4 | This demonstrates how Flexx can interact wih other JS frameworks. 5 | """ 6 | 7 | from pscript import RawJS 8 | 9 | from flexx import flx 10 | 11 | 12 | # Associate assets needed by this app. 13 | flx.assets.associate_asset(__name__, "http://code.jquery.com/jquery-1.10.2.js") 14 | flx.assets.associate_asset(__name__, "http://code.jquery.com/ui/1.11.4/jquery-ui.js") 15 | flx.assets.associate_asset(__name__, 16 | "http://code.jquery.com/ui/1.11.4/themes/smoothness/jquery-ui.css") 17 | 18 | 19 | class DatePicker(flx.Widget): 20 | 21 | def _create_dom(self): 22 | global window 23 | node = window.document.createElement('input') 24 | RawJS('$')(node).datepicker() 25 | return node 26 | 27 | 28 | class Example(flx.Widget): 29 | 30 | def init(self): 31 | 32 | with flx.FormLayout(): 33 | self.start = DatePicker(title='Start date') 34 | self.end = DatePicker(title='End date') 35 | flx.Widget(flex=1) 36 | 37 | 38 | if __name__ == '__main__': 39 | m = flx.launch(Example, 'app') 40 | flx.run() 41 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | 5 | # IDEs and such 6 | .idea 7 | .vscode 8 | 9 | # Specific to here 10 | exp/html_examples.txt 11 | flexx/resources/phosphor-all.*.js 12 | flexx/resources/phosphor-all.*.css 13 | _flexx-notebooks 14 | _flexx-book 15 | _feedstock 16 | flexx_legacy 17 | _phosphor-all 18 | examples/ui/*.html 19 | 20 | # C extensions 21 | *.so 22 | 23 | # Distribution / packaging 24 | .Python 25 | env/ 26 | bin/ 27 | build/ 28 | develop-eggs/ 29 | dist/ 30 | eggs/ 31 | parts/ 32 | sdist/ 33 | var/ 34 | *.egg-info/ 35 | .installed.cfg 36 | *.egg 37 | .ipynb_checkpoints 38 | 39 | # Installer logs 40 | pip-log.txt 41 | pip-delete-this-directory.txt 42 | 43 | # Unit test / coverage reports 44 | htmlcov/ 45 | .tox/ 46 | .coverage 47 | .cache 48 | nosetests.xml 49 | coverage.xml 50 | .pytest_cache 51 | 52 | # Translations 53 | *.mo 54 | 55 | # Mr Developer 56 | .mr.developer.cfg 57 | .project 58 | .pydevproject 59 | 60 | # Rope 61 | .ropeproject 62 | 63 | # Django stuff: 64 | *.log 65 | *.pot 66 | 67 | # Sphinx documentation and website 68 | docs/_build/ 69 | doc/_build/ 70 | _website/ 71 | _live/ 72 | _gh-pages/ 73 | 74 | # Eclipse 75 | .settings/ -------------------------------------------------------------------------------- /flexx/event/_attribute.py: -------------------------------------------------------------------------------- 1 | """ 2 | Implements the attribute class. 3 | """ 4 | 5 | from ._action import BaseDescriptor 6 | 7 | 8 | class Attribute(BaseDescriptor): 9 | """ Attributes are (readonly, and usually static) values associated with 10 | Component classes. They expose and document a value without 11 | providing means of observing changes like ``Property`` does. (The 12 | actual value is taken from ``component._xx``, with "xx" the name 13 | of the attribute.) 14 | 15 | """ 16 | 17 | def __init__(self, doc=''): 18 | # Set doc 19 | if not isinstance(doc, str): 20 | raise TypeError('event.Attribute() doc must be a string.') 21 | self._doc = doc 22 | self._set_name('anonymous_attribute') 23 | 24 | def _set_name(self, name): 25 | self._name = name # or func.__name__ 26 | self.__doc__ = self._format_doc('attribute', name, self._doc) 27 | 28 | def __set__(self, instance, value): 29 | t = 'Cannot set attribute %r.' 30 | raise AttributeError(t % self._name) 31 | 32 | def __get__(self, instance, owner): 33 | if instance is None: 34 | return self 35 | return getattr(instance, '_' + self._name) 36 | -------------------------------------------------------------------------------- /flexxamples/howtos/serve_with_asgineer.py: -------------------------------------------------------------------------------- 1 | """ 2 | Example of serving a Flexx app using a regular web server. In this case Asgineer. 3 | https://github.com/almarklein/asgineer 4 | """ 5 | 6 | import asgineer 7 | 8 | from flexx import flx 9 | from flexxamples.howtos.editor_cm import CodeEditor 10 | 11 | # Define an app 12 | 13 | class MyApp(flx.Widget): 14 | def init(self): 15 | with flx.HBox(): 16 | CodeEditor(flex=1) 17 | flx.Widget(flex=1) 18 | 19 | 20 | # Dump it to a dictionary of assets that we can serve. Make the main 21 | # page index.html. The link=0 means to pack the whole app into a single 22 | # html page (note that data (e.g. images) will still be separate). 23 | app = flx.App(MyApp) 24 | assets = app.dump('index.html', link=0) 25 | 26 | 27 | # Do the Asgineer thing. Use make_asset_handler() for a solid and 28 | # lightning fast way to serve assets from memory (it includes HTTP 29 | # caching and compression). 30 | 31 | asset_handler = asgineer.utils.make_asset_handler(assets) 32 | 33 | @asgineer.to_asgi 34 | async def main_handler(request): 35 | return await asset_handler(request) 36 | 37 | 38 | if __name__ == '__main__': 39 | asgineer.run(main_handler, "uvicorn", "localhost:8080") 40 | -------------------------------------------------------------------------------- /docs/contributing.rst: -------------------------------------------------------------------------------- 1 | Contributing to Flexx 2 | --------------------- 3 | 4 | If you like Flexx and want to help out, there's a few things you can do. 5 | First off, feedback; it's very useful to hear what you like and what you 6 | struggle with. Feel free to use the 7 | `Github issue tracker `_ for this. 8 | 9 | You can help by contributing to the documentation, and/or by creating nice 10 | examples. 11 | 12 | If you would like to help out with the codebase itself, e.g. by fixing bugs, 13 | making improvements, or submitting new types of widgets, you probably need 14 | to a few extra libraries (don't worry, it's 'nothing fancy): 15 | 16 | * pytest and pytest-cov (get them via conda or pip) 17 | * flake8 (get it via conda or pip) 18 | * Nodejs 19 | * Firefox 20 | 21 | We follow the Github workflow: 22 | 23 | * You fork the `Github repo `_ and make a clone 24 | to your local computer. 25 | * You update to the latest version: ``git checkout master && git pull`` 26 | * You make a new branch: ``git checkout -b mywork`` 27 | * You make changes to the code and commit these: ``git commit`` 28 | * You push to your fork ``git push`` 29 | * Create a Pull Request from the Github interface. 30 | -------------------------------------------------------------------------------- /flexxamples/howtos/mutual_dependent_props.py: -------------------------------------------------------------------------------- 1 | """ 2 | This example demonstrates how two mutually dependent props can be set 3 | without getting into an infinite loop. Since flexx uses actions, this 4 | is rather trivial. 5 | """ 6 | 7 | from flexx import event 8 | 9 | 10 | class Temperature(event.Component): 11 | """ Temperature object with a settable prop for both Celcius and 12 | Fahrenheit. 13 | """ 14 | 15 | c = event.FloatProp(doc='Temperature in degrees Celcius') 16 | f = event.FloatProp(doc='Temperature in degrees Fahrenheit') 17 | 18 | @event.action 19 | def set_c(self, t): 20 | t = float(t) 21 | self._mutate_c(t) 22 | self._mutate_f(t * 1.8 + 32) 23 | 24 | @event.action 25 | def set_f(self, t): 26 | t = float(t) 27 | self._mutate_f(t) 28 | self._mutate_c((t - 32) / 1.8) 29 | 30 | @event.reaction 31 | def on_temp_change(self): 32 | # This gets called once with two events when either C or F is changed. 33 | print(' temp in Celcius: %1.1f C' % self.c) 34 | print(' temp in Fahrenheit: %1.1f F' % self.f) 35 | 36 | t = Temperature() 37 | 38 | print('Water is freezing:') 39 | t.set_c(0) 40 | event.loop.iter() 41 | 42 | print('Average annual temp in California') 43 | t.set_f(59.4) 44 | event.loop.iter() 45 | -------------------------------------------------------------------------------- /flexxamples/howtos/threaded.py: -------------------------------------------------------------------------------- 1 | """ 2 | Example showing running Flexx' event loop in another thread. 3 | This is not a recommended use in general. 4 | 5 | Invoking actions is thread-safe. Actions and reactions are always 6 | executed in the thread that runs the event loop. 7 | 8 | The flx.create_server() is used to (re)create the server object. It is 9 | important that the used asyncio loop is local to the thread. 10 | """ 11 | 12 | import time 13 | import threading 14 | import asyncio 15 | 16 | from flexx import flx 17 | 18 | 19 | class MyComponent1(flx.Component): 20 | 21 | foo = flx.Property(0, settable=True) 22 | 23 | @flx.reaction('foo') 24 | def on_foo(self, *events): 25 | for ev in events: 26 | print('foo changed to', ev.new_value) 27 | 28 | # Create component in main thread 29 | comp = MyComponent1() 30 | 31 | # Start server in its own thread 32 | def start_flexx(): 33 | flx.create_server(loop=asyncio.new_event_loop()) 34 | flx.start() 35 | 36 | t = threading.Thread(target=start_flexx) 37 | t.start() 38 | 39 | # Manipulate component from main thread 40 | # (the component's on_foo() gets called from other thread) 41 | for i in range(5, 9): 42 | time.sleep(1) 43 | comp.set_foo(i) 44 | 45 | # Stop event loop (this is thread-safe) and wait for thread to end 46 | flx.stop() 47 | t.join() 48 | -------------------------------------------------------------------------------- /docs/motivation.rst: -------------------------------------------------------------------------------- 1 | Motivation 2 | ---------- 3 | 4 | The primary motivation for Flexx is the undeniable fact that the web 5 | (i.e. browser technology) has become an increasingly popular method for 6 | delivering applications to users, also for (interactive) scientific 7 | content. 8 | 9 | The purpose of Flexx is to provide a single application framework to 10 | create desktop applications and web apps. By making use of browser 11 | technology, the library itself can be relatively small and pure Python, 12 | making it widely and easily available. 13 | 14 | By making use of PScript (Python to JavaScript translation), the entire 15 | library is written without (hardly) a line of JavaScript. This makes it easier 16 | to develop than if we would have a corresponding "flexx.js" to maintain. 17 | Further, it allows users to easily define callback methods that are 18 | executed in JavaScript, allowing for higher performance when needed. 19 | 20 | Libraries written for Python, but not *in* Python have a much harder 21 | time to survive, because users don't easily become contributors. This 22 | is one of the reasons of the success of e.g. scikit-image, while e.g. Mayavi 23 | has a much harder time attracting developers. Since Flexx is written 24 | in a combination of Python and PScript, its user community is more 25 | likely to take an active role in its development. 26 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015-2020, Flexx developers 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | -------------------------------------------------------------------------------- /flexx/ui/widgets/_markdown.py: -------------------------------------------------------------------------------- 1 | """ Markdown widget 2 | 3 | Widget containing a string which content gets rendered and shown as markdown text. 4 | 5 | See the working example from `flexxamples/ui_usage/markdown.py`. 6 | 7 | Simple usage: 8 | 9 | .. UIExample:: 200 10 | 11 | def init(self): 12 | content = "# Welcome\n\n" \ 13 | "Hello. Welcome to my **website**." \ 14 | "This is an example of a widget container for markdown content. " \ 15 | "The content can be text or a link.\n\n" 16 | ui.Markdown(content=content, style='background:#EAECFF;height:60%;') 17 | 18 | """ 19 | 20 | from ... import app, event 21 | from . import Widget 22 | 23 | app.assets.associate_asset( 24 | __name__, 'https://cdnjs.cloudflare.com/ajax/libs/showdown/1.9.1/showdown.min.js' 25 | ) 26 | 27 | 28 | class Markdown(Widget): 29 | """ A widget that shows a rendered Markdown content. 30 | """ 31 | CSS = """ 32 | 33 | .flx-Markdown { 34 | height: min(100vh,100%); 35 | overflow-y: auto; 36 | } 37 | """ 38 | 39 | content = event.StringProp(settable=True, doc=""" 40 | The markdown content to be rendered 41 | """) 42 | 43 | @event.reaction 44 | def __content_change(self): 45 | global showdown 46 | conv = showdown.Converter() 47 | self.node.innerHTML = conv.makeHtml(self.content) 48 | -------------------------------------------------------------------------------- /flexx/ui/layouts/_pinboard.py: -------------------------------------------------------------------------------- 1 | """ PinboardLayout 2 | 3 | Free positioning (absolute or relative) of child widgets. Example: 4 | 5 | .. UIExample:: 200 6 | 7 | from flexx import app, ui 8 | 9 | class Example(ui.Widget): 10 | 11 | def init(self): 12 | 13 | with ui.PinboardLayout(): 14 | self.b1 = ui.Button(text='Stuck at (20, 20)', 15 | style='left:20px; top:20px;') 16 | self.b2 = ui.Button(text='Dynamic at (30%, 30%)', 17 | style='left:30%; top:30%; height:100px;') 18 | self.b3 = ui.Button(text='Dynamic at (50%, 70%)', 19 | style='left:50%; top:70%;') 20 | 21 | """ 22 | 23 | from . import Layout 24 | 25 | 26 | class PinboardLayout(Layout): 27 | """ Unconstrained absolute and relative positioning of child widgets. 28 | 29 | This simply places child widgets using CSS "position: absolute". Use 30 | CSS "left" and "top" to position the widget (using a "px" or "%" suffix). 31 | Optionally "width", "height", "right" and "bottom" can also be used. 32 | 33 | The ``node`` of this widget is a 34 | `
`_. 35 | """ 36 | 37 | CSS = """ 38 | .flx-PinboardLayout > .flx-Widget { 39 | position: absolute; 40 | } 41 | """ 42 | -------------------------------------------------------------------------------- /flexxamples/demos/themed_form.py: -------------------------------------------------------------------------------- 1 | # doc-export: ThemedForm 2 | """ 3 | Simple example that shows two forms, one which is stretched, and one 4 | in which we use a dummy Widget to fill up space so that the form is 5 | more compact. 6 | 7 | This example also demonstrates how CSS can be used to apply a greenish theme. 8 | """ 9 | 10 | from flexx import flx 11 | 12 | 13 | class ThemedForm(flx.Widget): 14 | 15 | CSS = """ 16 | .flx-Button { 17 | background: #9d9; 18 | } 19 | .flx-LineEdit { 20 | border: 2px solid #9d9; 21 | } 22 | """ 23 | 24 | def init(self): 25 | 26 | with flx.HFix(): 27 | with flx.FormLayout() as self.form: 28 | self.b1 = flx.LineEdit(title='Name:', text='Hola') 29 | self.b2 = flx.LineEdit(title='Age:', text='Hello world') 30 | self.b3 = flx.LineEdit(title='Favorite color:', text='Foo bar') 31 | flx.Button(text='Submit') 32 | with flx.FormLayout() as self.form: 33 | self.b4 = flx.LineEdit(title='Name:', text='Hola') 34 | self.b5 = flx.LineEdit(title='Age:', text='Hello world') 35 | self.b6 = flx.LineEdit(title='Favorite color:', text='Foo bar') 36 | flx.Button(text='Submit') 37 | flx.Widget(flex=1) # Add a spacer 38 | 39 | 40 | if __name__ == '__main__': 41 | m = flx.launch(ThemedForm, 'app') 42 | flx.run() 43 | -------------------------------------------------------------------------------- /docs/start.rst: -------------------------------------------------------------------------------- 1 | --------------- 2 | Getting started 3 | --------------- 4 | 5 | Installation 6 | ------------ 7 | 8 | * ``pip install flexx`` 9 | * ``conda install flexx -c conda-forge`` 10 | * Old school: ``python setup.py install`` 11 | * Clone the repo and add the root dir to your PYTHONPATH (developer mode) 12 | 13 | When using Pip or Conda, the dependencies will be installed automatically. 14 | 15 | 16 | Dependencies 17 | ------------ 18 | 19 | Being pure Python and cross platform, Flexx should work (almost) 20 | anywhere where there's Python and a browser. Flexx is written for Python 21 | 3.5+ and also works on Pypy. 22 | 23 | Flexx further depends on the following packages (all are pure Python, 24 | and the latter three are projects under the flexxui umbrella): 25 | 26 | * `Tornado `_ 27 | * `PScript `_ 28 | * `Webruntime `_ 29 | * `Dialite `_ 30 | 31 | 32 | Supported browsers 33 | ------------------ 34 | 35 | Flexx aims to support all modern browsers, including Firefox, Chrome and Edge. 36 | Internet Explorer version 10 and up should work, but some things may be flaky. 37 | 38 | To run apps that look like desktop apps, we recommend having Firefox or nw.js installed. 39 | 40 | 41 | Current status 42 | -------------- 43 | 44 | Flexx is in beta status; some 45 | parts of the API may change, but we do care about backwards compatibility. 46 | -------------------------------------------------------------------------------- /flexx/ui/widgets/_iframe.py: -------------------------------------------------------------------------------- 1 | """ IFrame 2 | 3 | .. UIExample:: 100 4 | 5 | with ui.HSplit(): 6 | ui.IFrame(url='bsdf.io') 7 | ui.IFrame(url='http://flexx.readthedocs.io') 8 | # Note: the rtd page does not seem to load on Firefox 57.04 9 | 10 | """ 11 | 12 | from ... import event 13 | from . import Widget 14 | 15 | 16 | class IFrame(Widget): 17 | """ An iframe element, i.e. a container to show web-content. 18 | Note that some websites do not allow themselves to be rendered in 19 | a cross-source iframe. 20 | 21 | The ``node`` of this widget is a 22 | `