diff --git a/000-default.conf b/000-default.conf
new file mode 100644
index 00000000..240e9f0f
--- /dev/null
+++ b/000-default.conf
@@ -0,0 +1,40 @@
+
+ # The ServerName directive sets the request scheme, hostname and port that
+ # the server uses to identify itself. This is used when creating
+ # redirection URLs. In the context of virtual hosts, the ServerName
+ # specifies what hostname must appear in the request's Host: header to
+ # match this virtual host. For the default virtual host (this file) this
+ # value is not decisive as it is used as a last resort host regardless.
+ # However, you must set it for any further virtual host explicitly.
+ #ServerName www.example.com
+
+ ServerAdmin webmaster@localhost
+ DocumentRoot /var/www/html
+
+ # Available loglevels: trace8, ..., trace1, debug, info, notice, warn,
+ # error, crit, alert, emerg.
+ # It is also possible to configure the loglevel for particular
+ # modules, e.g.
+ #LogLevel info ssl:warn
+
+ ErrorLog ${APACHE_LOG_DIR}/error.log
+ CustomLog ${APACHE_LOG_DIR}/access.log combined
+
+ #rewrite rules for remi on port 8080
+ ProxyPreserveHost On
+ RewriteEngine On
+ RewriteCond %{HTTP:Upgrade} =websocket [NC]
+ RewriteRule /foo(.*) ws://127.0.0.1:8080/$1 [P,L]
+ RewriteCond %{HTTP:Upgrade} !=websocket [NC]
+ RewriteRule /foo(.*) http://127.0.0.1:8080/$1 [P,L]
+
+
+ # For most configuration files from conf-available/, which are
+ # enabled or disabled at a global level, it is possible to
+ # include a line for only one particular virtual host. For example the
+ # following line enables the CGI configuration for this host only
+ # after it has been globally disabled with "a2disconf".
+ #Include conf-available/serve-cgi-bin.conf
+
+
+# vim: syntax=apache ts=4 sw=4 sts=4 sr noet
diff --git a/README.md b/README.md
index 76b7dbfa..833ce5a9 100644
--- a/README.md
+++ b/README.md
@@ -1,386 +1,29 @@
-[](https://travis-ci.com/dddomodossola/remi)
-
-
-
-
-
-
- GUI library for your Python applications
-
-
-
-Remi is a GUI library for Python applications that gets rendered in web browsers.
-This allows you to access your interface locally and remotely.
-
-
-
-There is also a **drag n drop GUI Editor**. Look at the [Editor](https://github.com/dddomodossola/remi/tree/master/editor) subfolder to download your copy.
-
-
-
-Getting Started
-===
-For a **stable** version:
-```
-pip install remi
-```
-
-For the most updated **experimental** version [Download](https://github.com/dddomodossola/remi/archive/master.zip) or check out Remi from git and install
-
-```
-python setup.py install
-```
-or install directly using pip
-
-```
-pip install git+https://github.com/dddomodossola/remi.git
-```
-
-Then start the test script (download it from github https://github.com/dddomodossola/remi/blob/master/examples/widgets_overview_app.py):
-```
-python widgets_overview_app.py
-```
-
-
-Remi
-===
-Platform independent Python GUI library. In less than 100 Kbytes of source code, perfect for your diet.
-
-
-
-
-
-Remi enables developers to create platform independent GUI with Python. The entire GUI is rendered in your browser. **No HTML** is required, Remi automatically translates your Python code into HTML. When your app starts, it starts a web server that will be accessible on your network.
-
-A basic application appears like this:
-
-```py
-import remi.gui as gui
-from remi import start, App
-
-class MyApp(App):
- def __init__(self, *args):
- super(MyApp, self).__init__(*args)
-
- def main(self):
- container = gui.VBox(width=120, height=100)
- self.lbl = gui.Label('Hello world!')
- self.bt = gui.Button('Press me!')
-
- # setting the listener for the onclick event of the button
- self.bt.onclick.do(self.on_button_pressed)
-
- # appending a widget to another, the first argument is a string key
- container.append(self.lbl)
- container.append(self.bt)
-
- # returning the root widget
- return container
-
- # listener function
- def on_button_pressed(self, widget):
- self.lbl.set_text('Button pressed!')
- self.bt.set_text('Hi!')
-
-# starts the web server
-start(MyApp)
-```
-
-In order to see the user interface, open your preferred browser and type "http://127.0.0.1:8081".
-You can change the URL address by specific **kwargs at `start` function call. This will be discussed later.
-
-Tested on Android, Linux, Windows.
-Useful on Raspberry Pi for Python script development. It allows interacting with your Raspberry Pi remotely from your mobile device.
-
-
-FAQ
-===
-- **Why another GUI lib?**
-Kivy, PyQT, and PyGObject all require native code for the host operating system, which means installing or compiling large dependencies. Remi needs only a web browser to show your GUI.
-
-- **Do I need to know HTML?**
-NO, It is not required, you have to code only in Python.
-
-- **Is it open source?**
-For sure! Remi is released under the Apache License. See the ``LICENSE`` file for more details.
-
-- **Do I need some kind of web server?**
-No, it's included.
-
-
-Brief tutorial
-===
-Import Remi library and some other useful stuff.
-
-```py
-import remi.gui as gui
-from remi import start, App
-```
-
-Subclass the `App` class and declare a `main` function that will be the entry point of the application. Inside the main function you have to return the root widget.
-
-```py
-class MyApp(App):
- def __init__(self, *args):
- super(MyApp, self).__init__(*args)
-
- def main(self):
- lbl = gui.Label("Hello world!", width=100, height=30)
-
- # return of the root widget
- return lbl
-```
-
-Outside the main class, start the application by calling the function `start` and passing the name of the class you declared previously as the parameter:
-
-```py
-# starts the webserver
-start(MyApp)
-```
-
-Run the script. If it's all OK the GUI will be opened automatically in your browser, otherwise, you have to type in the address bar "http://127.0.0.1:8081".
-
-You can customize optional parameters in the `start` call like:
-
-```py
-start(MyApp, address='127.0.0.1', port=8081, multiple_instance=False, enable_file_cache=True, update_interval=0.1, start_browser=True)
-```
-
-Parameters:
-- address: network interface IP
-- port: listen port
-- multiple_instance: boolean, if True multiple clients that connect to your script has different App instances (identified by unique cookie session identifier)
-- enable_file_cache: boolean, if True enable resource caching
-- update_interval: GUI update interval in seconds. If zero, the update happens at each change. If zero, the App.idle method is not called.
-- start_browser: boolean that defines if the browser should be opened automatically at startup
-- standalone: boolean, indicates where to run the application as a standard Desktop application with its own window. If False, the interface is shown in a browser webpage.
-
-Additional Parameters:
-- username: for a basic HTTP authentication
-- password: for a basic HTTP authentication
-- certfile: SSL certificate filename
-- keyfile: SSL key file
-- ssl_version: authentication version (i.e. ssl.PROTOCOL_TLSv1_2). If None disables SSL encryption
-
-All widgets constructors accept two standards**kwargs that are:
-- width: can be expressed as int (and is interpreted as a pixel) or as str (and you can specify the measuring unit like '10%')
-- height: can be expressed as int (and is interpreted as a pixel) or as str (and you can specify the measuring unit like '10%')
-
-
-Events and callbacks
-===
-Widgets expose a set of events that happen during user interaction.
-Such events are a convenient way to define the application behavior.
-Each widget has its own callbacks, depending on the type of user interaction it allows.
-The specific callbacks for the widgets will be illustrated later.
-
-In order to register a function as an event listener you have to call a function like eventname.do (i.e. onclick.do) passing as parameters the callback that will manage the event.
-Follows an example:
-
-```py
-import remi.gui as gui
-from remi import start, App
-
-class MyApp(App):
- def __init__(self, *args):
- super(MyApp, self).__init__(*args)
-
- def main(self):
- container = gui.VBox(width=120, height=100)
- self.lbl = gui.Label('Hello world!')
- self.bt = gui.Button('Press me!')
-
- # setting the listener for the onclick event of the button
- self.bt.onclick.do(self.on_button_pressed)
-
- # appending a widget to another, the first argument is a string key
- container.append(self.lbl)
- container.append(self.bt)
-
- # returning the root widget
- return container
-
- # listener function
- def on_button_pressed(self, widget):
- self.lbl.set_text('Button pressed!')
- self.bt.set_text('Hi!')
-
-# starts the web server
-start(MyApp)
-```
-
-In the shown example *self.bt.onclick.do(self.on_button_pressed)* registers the self's *on_button_pressed* function as a listener for the event *onclick* exposed by the Button widget.
-Simple, easy.
-
-Listener's callbacks will receive the emitter's instance firstly, then all other parameters provided by the specific event.
-
-
-Besides the standard event registration (as aforementioned), it is possible to pass user parameters to listener functions. This can be achieves appending parameters to the *do* function call.
-
-```py
-import remi.gui as gui
-from remi import start, App
-
-class MyApp(App):
- def __init__(self, *args):
- super(MyApp, self).__init__(*args)
-
- def main(self):
- container = gui.VBox(width=120, height=100)
- self.lbl = gui.Label('Hello world!')
- self.bt = gui.Button('Hello name!')
- self.bt2 = gui.Button('Hello name surname!')
-
- # setting the listener for the onclick event of the buttons
- self.bt.onclick.do(self.on_button_pressed, "Name")
- self.bt2.onclick.do(self.on_button_pressed, "Name", "Surname")
-
- # appending a widget to another
- container.append(self.lbl)
- container.append(self.bt)
- container.append(self.bt2)
-
- # returning the root widget
- return container
-
- # listener function
- def on_button_pressed(self, widget, name='', surname=''):
- self.lbl.set_text('Button pressed!')
- widget.set_text('Hello ' + name + ' ' + surname)
-
-# starts the web server
-start(MyApp)
-```
-
-This allows great flexibility, getting different behaviors with the same event listener definition.
-
-
-HTML Attribute accessibility
-===
-Sometimes it is required to access Widget's HTML representation in order to manipulate HTML attributes.
-The library allows accessing this information easily.
-
-A simple example: It is the case where you would like to add a hover text to a widget. This can be achieved by the *title* attribute of an HTML tag.
-In order to do this:
-
-```py
- widget_instance.attributes['title'] = 'Your title content'
-```
-
-A special case of HTML attribute is the *style*.
-The style attributes can be altered in this way:
-
-```py
- widget_instance.style['color'] = 'red'
-```
-
-The assignment of a new attribute automatically creates it.
-
-For a reference list of HTML attributes, you can refer to https://www.w3schools.com/tags/ref_attributes.asp
-
-For a reference list of style attributes, you can refer to https://www.w3schools.com/cssref/default.asp
-
-Take care about internally used attributes. These are:
-- **class**: It is used to store the Widget class name for styling purpose
-- **id**: It is used to store the instance id of the widget for callback management
-
-
-Remote access
-===
-If you are using your REMI app remotely, with a DNS and behind a firewall, you can specify special parameters in the `start` call:
-- **port**: HTTP server port. Don't forget to NAT this port on your router;
-
-```py
-start(MyApp, address='0.0.0.0', port=8081)
-```
-
-
-Standalone Execution
-===
-I suggest using the browser as a standard interface window.
-
-However, you can avoid using the browser.
-This can be simply obtained joining REMI and [PyWebView](https://github.com/r0x0r/pywebview).
-Here is an example about this [standalone_app.py](https://github.com/dddomodossola/remi/blob/development/examples/standalone_app.py).
-
-**Be aware that PyWebView uses qt, gtk and so on to create the window. An outdated version of these libraries can cause UI problems. If you experience UI issues, update these libraries, or better avoid standalone execution.**
-
-
-Authentication
-===
-In order to limit remote access to your interface, you can define a username and password. It consists of a simple authentication process.
-Just define the parameters **username** and **password** in the start call:
-```py
-start(MyApp, username='myusername', password='mypassword')
-```
-
-
-Styling
-===
-In order to define a new style for your app, you have to do the following.
-Create a *res* folder and pass it to your App class constructor:
-```python
-class MyApp(App):
- def __init__(self, *args):
- res_path = os.path.join(os.path.dirname(__file__), 'res')
- super(MyApp, self).__init__(*args, static_file_path={'res':res_path})
-```
-
-Copy the standard style.css file from the remi folder and paste it inside your *res* folder. Edit it in order to customize.
-This way the standard *style.css* file gets overridden by the one you created.
-
-
-Compatibility
-===
-Remi is made to be compatible from Python2.7 to Python3.X. Please notify compatibility issues.
-
-
-Security
-===
-Remi should be intended as a standard desktop GUI framework.
-The library itself doesn't implement security strategies, and so it is advised to not expose its access to unsafe public networks.
-
-When loading data from external sources, consider protecting the application from potential javascript injection before displaying the content directly.
-
-
-Contributors
-===
-Thank you for collaborating with us to make Remi better!
-
-The real power of opensource is contributors. Please feel free to participate in this project, and consider to add yourself to the [contributors list](doc/contributors.md).
-Yes, I know that GitHub already provides a list of contributors, but I feel that I must mention who helps.
-
-
-
-
-
-
-Projects using Remi
-===
-[PySimpleGUI](https://github.com/PySimpleGUI/PySimpleGUI): Launched in 2018 Actively developed and supported. Supports tkinter, Qt, WxPython, Remi (in browser). Create custom layout GUI's simply. Python 2.7 & 3 Support. 100+ Demo programs & Cookbook for rapid start. Extensive documentation.
-
-[App Template For REMI](https://github.com/cheak1974/remi-app-template): A really well written template for multiview applications.
-
-[Web based dynamic reconfigure for ROS robots](https://github.com/awesomebytes/web_dyn_reconf)
-
-[razmq](https://github.com/MrYsLab/razmq)
-
-[Espresso-ARM](http://hallee.github.io/espresso-arm/)
-
-[PiPresents](https://github.com/KenT2/pipresents-gapless)
-
-[The Python Banyan Framework](https://github.com/MrYsLab/python_banyan)
-
-[LightShowPi show manager](https://github.com/Chrispizzi75/ShowManager)
-
-[rElectrum](https://github.com/emanuelelaface/rElectrum): A powerful promising Electrum wallet manager for safe transactions.
-
-Other Implementations
-===
-Listed here are other implementations of this library:
-- [**cremi**](https://github.com/cyberpro4/cremi): (WIP) developed for your C++ projects by [Claudio Cannatà ](https://github.com/cyberpro4).
+# DESCRIPTION
+This is development fork of [Remi GUI](https://github.com/dddomodossola/remi). It aims at adding the "url_root" feature in remi, see [here](https://github.com/dddomodossola/remi/issues/430), with the branch of the same name.
+The main usage of this feature is to access remi instance (or even multiple simultaneous remi instances) from one single port on a server, i.e port 80 or 443. To specify the remi instance you want to reach from the client, use "url_root" : myserver.com/\
+
+# HOWTO
+To try this feature, you can use apache as frontend webserver (port 80 by default), then write some rules (rewrite) to forward traffic on remi server (port 8080 for example). Below is a sample installation/configuration steps.
+
+* install remi from this repo
+* install apache2 and enable mod_rewrite, proxy, proxy_http, proxy_wstunnel (`a2enmod rewrite proxy proxy_http proxy_wstunnel` on linux)
+* write the following rules at the end of your apache virtualhost, replace "foo" with your "url_root" (see 000-default.conf):
+```
+RewriteEngine On
+RewriteCond %{HTTP:Upgrade} =websocket [NC]
+RewriteRule /foo(.*) ws://127.0.0.1:8080/$1 [P,L]
+RewriteCond %{HTTP:Upgrade} !=websocket [NC]
+RewriteRule /foo(.*) http://127.0.0.1:8080/$1 [P,L]
+```
+* set url_root in your remi script (see url_root_test.py for example)
+* restart apache, launch remi, use your browser to reach http://localhost/foo (note we use the default port 80 of apache frontend)
+
+# TODO
+At least :
+
+* move `url_root` to the right place inside remi (note that this variable should also be accessible from `gui.py`)
+* ~~make `url_root` an optional parameter which defaults to `""`~~ Currently done in `class App[...]`.
+* test this feature with https/wss
+* continue implementation of some Remi widgets: where hard links are sent to the client (currently done : Image, VideoPlayer, SvgImage, FileFolderItem).
+* For `Image`, `VideoPlayer`, `SvgImage` widgets, setters were modified to unconditionally prefix the input src url with `url_root`. This will cause problem for embedding contents from external server/website.
+* ~~issue with no immediate clean solution: all calls (by default 3) to "/res:" in "style.css" are broken as they are not prefixed with "url_root". We should discuss about that.~~ Finally fixed by replacing all three absolute imports by relative imports (/res -> ./res) in style.css.
diff --git a/remi/gui.py b/remi/gui.py
index c33b5fd7..bd3288b4 100644
--- a/remi/gui.py
+++ b/remi/gui.py
@@ -1345,7 +1345,7 @@ def set_icon_file(self, filename, rel="icon"):
rel (str): leave it unchanged (standard "icon")
"""
mimetype, encoding = mimetypes.guess_type(filename)
- self.add_child("favicon", ''%(rel, filename, mimetype))
+ self.add_child("favicon", ''%(rel, url_root, filename, mimetype))
def set_icon_data(self, base64_data, mimetype="image/png", rel="icon"):
""" Allows to define an icon for the App
@@ -1355,7 +1355,7 @@ def set_icon_data(self, base64_data, mimetype="image/png", rel="icon"):
mimetype (str): mimetype of the image ("image/png" or "image/x-icon"...)
rel (str): leave it unchanged (standard "icon")
"""
- self.add_child("favicon", ''%(rel, base64_data, mimetype))
+ self.add_child("favicon", ''%(rel, url_root, base64_data, mimetype))
def set_internal_js(self, app_identifier, net_interface_ip, pending_messages_queue_length, websocket_timeout_timer_ms):
self.add_child('internal_js',
@@ -1406,7 +1406,7 @@ def set_internal_js(self, app_identifier, net_interface_ip, pending_messages_que
var self = this;
try{
- this._ws = new WebSocket(ws_wss + '://%(host)s/');
+ this._ws = new WebSocket(ws_wss + '://%(host)s%(url_root)s/');
console.debug('opening websocket');
this._ws.onopen = function(evt){
@@ -1587,7 +1587,7 @@ def set_internal_js(self, app_identifier, net_interface_ip, pending_messages_que
};
Remi.prototype.uploadFile = function(widgetID, eventSuccess, eventFail, eventData, file){
- var url = '/';
+ var url = '/%(url_root)s';
var xhr = new XMLHttpRequest();
var fd = new FormData();
xhr.open('POST', url, true);
@@ -1623,6 +1623,7 @@ def set_internal_js(self, app_identifier, net_interface_ip, pending_messages_que
window.remi = new Remi();
""" % {'host':net_interface_ip,
+ 'url_root':url_root,
'max_pending_messages':pending_messages_queue_length,
'messaging_timeout':websocket_timeout_timer_ms,
'emitter_identifier':app_identifier,
@@ -2817,7 +2818,7 @@ class Image(Widget):
@editor_attribute_decorator("WidgetSpecific", '''Image data or url''', 'base64_image', {})
def attr_src(self): return self.attributes.get('src', '')
@attr_src.setter
- def attr_src(self, value): self.attributes['src'] = str(value)
+ def attr_src(self, value): self.attributes['src'] = url_root+str(value)
@attr_src.deleter
def attr_src(self): del self.attributes['src']
@@ -2829,14 +2830,14 @@ def __init__(self, image='', *args, **kwargs):
"""
super(Image, self).__init__(*args, **kwargs)
self.type = 'img'
- self.attributes['src'] = image
+ self.attributes['src'] = url_root+image
def set_image(self, image):
"""
Args:
image (str): an url to an image or a base64 data string
"""
- self.attributes['src'] = image
+ self.attributes['src'] = url_root+image
class Table(Container):
@@ -3826,7 +3827,7 @@ def __init__(self, path_and_filename, text, is_folder=False, *args, **kwargs):
self.icon.onclick.connect(self.onclick)
else:
self.icon.onclick.connect(self.onselection)
- icon_file = '/res:folder.png' if is_folder else '/res:file.png'
+ icon_file = url_root+'/res:folder.png' if is_folder else url_root+'/res:file.png'
self.icon.css_background_image = "url('%s')" % icon_file
self.label = Label(text)
self.label.onclick.connect(self.onselection)
@@ -4083,7 +4084,7 @@ class VideoPlayer(Widget):
@editor_attribute_decorator("WidgetSpecific", '''Video url''', str, {})
def attr_src(self): return self.attributes.get('src', '')
@attr_src.setter
- def attr_src(self, value): self.attributes['src'] = str(value)
+ def attr_src(self, value): self.attributes['src'] = url_root+str(value)
@property
@editor_attribute_decorator("WidgetSpecific", '''Video poster img''', 'base64_image', {})
@@ -4112,7 +4113,7 @@ def attr_type(self, value): self.attributes['type'] = str(value).lower()
def __init__(self, video='', poster=None, autoplay=False, loop=False, *args, **kwargs):
super(VideoPlayer, self).__init__(*args, **kwargs)
self.type = 'video'
- self.attributes['src'] = video
+ self.attributes['src'] = url_root+video
self.attributes['preload'] = 'auto'
self.attributes['controls'] = None
self.attributes['poster'] = poster
@@ -4522,7 +4523,7 @@ def attr_preserveAspectRatio(self): del self.attributes['preserveAspectRatio']
@editor_attribute_decorator("WidgetSpecific", '''Image data or url or a base64 data string, html attribute xlink:href''', 'base64_image', {})
def image_data(self): return self.attributes.get('xlink:href', '')
@image_data.setter
- def image_data(self, value): self.attributes['xlink:href'] = str(value)
+ def image_data(self, value): self.attributes['xlink:href'] = url_root+str(value)
@image_data.deleter
def image_data(self): del self.attributes['xlink:href']
@@ -4538,7 +4539,7 @@ def __init__(self, image_data='', x=0, y=0, w=100, h=100, *args, **kwargs):
"""
super(SvgImage, self).__init__(*args, **kwargs)
self.type = 'image'
- self.image_data = image_data
+ self.image_data = url_root+image_data
self.set_position(x, y)
_MixinSvgSize.set_size(self, w, h)
diff --git a/remi/res/style.css b/remi/res/style.css
index 2eafb50f..f5ee8554 100644
--- a/remi/res/style.css
+++ b/remi/res/style.css
@@ -5,7 +5,7 @@
font-family: 'Roboto';
font-style: normal;
font-weight: 400;
- src: local('Roboto'), local('Roboto-Regular'), url('/res:font.woff2') format('woff2');
+ src: local('Roboto'), local('Roboto-Regular'), url('./res:font.woff2') format('woff2');
}
.remi-main textarea:focus,
.remi-main input[type='checkbox']:focus,
@@ -652,11 +652,11 @@ body.remi-main > div {
display: block;
}
.remi-main .TreeItem[has-subtree='true'][treeopen='false']{
- background-image: url('/res:plus.png');
+ background-image: url('./res:plus.png');
background-position: 0px 4px;
}
.remi-main .TreeItem[has-subtree='true'][treeopen='true']{
- background-image: url('/res:minus.png');
+ background-image: url('./res:minus.png');
background-position: 0px 4px;
border-bottom: 0;
}
diff --git a/remi/server.py b/remi/server.py
index 0ff2de66..7039d070 100644
--- a/remi/server.py
+++ b/remi/server.py
@@ -321,6 +321,7 @@ def __init__(self, request, client_address, server, **app_args):
self._app_args = app_args
self.root = None
self._log = logging.getLogger('remi.request')
+ self.url_root=self._app_args.get("url_root","")
super(App, self).__init__(request, client_address, server)
def _get_list_from_app_args(self, name):
@@ -368,7 +369,7 @@ def _instance(self):
head = gui.HEAD(self.server.title)
# use the default css, but append a version based on its hash, to stop browser caching
- head.add_child('internal_css', "\n")
+ head.add_child('internal_css', "\n"%(self.url_root))
body = gui.BODY()
body.add_class('remi-main')
@@ -610,6 +611,9 @@ def do_GET(self):
if do_process:
path = str(unquote(self.path))
+ # remove the url_root from the url
+ if path[0:len(self.url_root)] == self.url_root:
+ path=path[len(self.url_root):]
# noinspection PyBroadException
try:
self._instance()
diff --git a/url_root_test.py b/url_root_test.py
new file mode 100644
index 00000000..588148bb
--- /dev/null
+++ b/url_root_test.py
@@ -0,0 +1,55 @@
+
+import sys
+sys.path=["/home/francois/Documents/Micro-entreprise/projets/chute_blocs/dev_tests/remi_rootdir/remi"]+sys.path
+
+from remi import server,gui
+gui.url_root="/foo"
+
+from remi import start, App
+
+
+############################## BEGIN REMI TWEAKS ###############################
+
+
+############################## END REMI TWEAKS ###############################
+
+
+class MyApp(App):
+ def __init__(self, *args):
+ super(MyApp, self).__init__(
+ *args,
+ url_root=gui.url_root,
+ static_file_path={"res2":"/home/francois/Documents/Micro-entreprise/projets/chute_blocs/PlatRock-WebUI/platrock_webui/res"}
+ )
+
+ def main(self):
+ #creating a container VBox type, vertical
+ wid = gui.VBox(width=300, height=200)
+
+ #creating a text label, "white-space":"pre" preserves newline
+ self.lbl = gui.Label('Hello\n test', width='80%', height='50%', style={"white-space":"pre"})
+
+ #a button for simple interaction
+ bt = gui.Button('Press me!', width=200, height=30)
+
+ #setting up the listener for the click event
+ bt.onclick.do(self.on_button_pressed)
+
+ #adding the widgets to the main container
+ wid.append(self.lbl)
+ wid.append(bt)
+ wid.append(gui.Image("/res:folder.png"))
+ # returning the root widget
+ self.page.children["head"].set_icon_file("/res:folder.png")
+ return wid
+
+ # listener function
+ def on_button_pressed(self, emitter):
+ self.lbl.set_text('Hello World!')
+
+
+if __name__ == "__main__":
+ # starts the webserver
+ # optional parameters
+ # start(MyApp,address='127.0.0.1', port=8081, multiple_instance=False,enable_file_cache=True, update_interval=0.1, start_browser=True)
+ start(MyApp, debug=True, address='0.0.0.0', port=8080, start_browser=False)