Skip to content

Commit

Permalink
Fix and improvements
Browse files Browse the repository at this point in the history
- Better error handling
- You can now get the calling element itself as an argument to a Python function
- Sync textarea value property with its attribute
- Add `Page.get_element_from_selector` method
  • Loading branch information
mubarakalmehairbi committed May 21, 2023
1 parent 44f01b8 commit e54507c
Show file tree
Hide file tree
Showing 16 changed files with 128 additions and 10 deletions.
8 changes: 7 additions & 1 deletion docs/how_it_works.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,17 @@ The most important JSON object. It contains the following keys:
{"type": "page",
"func": ...,
"args": ...,
"selector-to-element": ...,
"url": ...,
"html": ...,
"uid": ...}
```
`type` is the type of JSON object, and it has the value 'page' when JavaScript sends the HTML document as data. `func` contains the name of the Python function that should be called, `args` are the arguments of this function, `url` is the URL of the HTML page that sent the data, `html` is the HTML document itself as a string. `uid` is the id of the window when creating desktop apps.
`type` is the type of JSON object, and it has the value 'page' when JavaScript sends the HTML document as data. `func` contains the name of the Python function that should be called, `args` are the arguments of this function, `selector-to-element` is a boolean that is only true if one of the arguments is an HTML element, `url` is the URL of the HTML page that sent the data, `html` is the HTML document itself as a string. `uid` is the id of the window when creating desktop apps.
If one of the argument is an HTML element, it will be converted to a JSON that contains its CSS selector:
```json
{"type": "element",
"selector": ...,
```

#### Files information JSON
Another type of JSON object is a JSON that contains uploaded files:
Expand Down
2 changes: 1 addition & 1 deletion docs/index.rst
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
ToUI
====

Version: v2.2.0
Version: v2.3.0

.. include:: ../README.md
:parser: myst_parser.sphinx_
Expand Down
4 changes: 4 additions & 0 deletions docs/toui.elements.Element.get_selector.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Element.get_selector
--------------------

.. automethod:: toui.elements.Element.get_selector
1 change: 1 addition & 0 deletions docs/toui.elements.Element.rst
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ Methods
toui.elements.Element.get_height_property
toui.elements.Element.get_id
toui.elements.Element.get_selected
toui.elements.Element.get_selector
toui.elements.Element.get_style_property
toui.elements.Element.get_value
toui.elements.Element.get_width_property
Expand Down
4 changes: 4 additions & 0 deletions docs/toui.elements.IFrameElement.get_selector.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
IFrameElement.get_selector
--------------------------

.. automethod:: toui.elements.IFrameElement.get_selector
1 change: 1 addition & 0 deletions docs/toui.elements.IFrameElement.rst
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ Methods
toui.elements.IFrameElement.get_height_property
toui.elements.IFrameElement.get_id
toui.elements.IFrameElement.get_selected
toui.elements.IFrameElement.get_selector
toui.elements.IFrameElement.get_style_property
toui.elements.IFrameElement.get_value
toui.elements.IFrameElement.get_width_property
Expand Down
4 changes: 4 additions & 0 deletions docs/toui.pages.Page.get_element_from_selector.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Page.get_element_from_selector
------------------------------

.. automethod:: toui.pages.Page.get_element_from_selector
1 change: 1 addition & 0 deletions docs/toui.pages.Page.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ Methods
toui.pages.Page.from_str
toui.pages.Page.get_body_element
toui.pages.Page.get_element
toui.pages.Page.get_element_from_selector
toui.pages.Page.get_elements
toui.pages.Page.get_html_element
toui.pages.Page.get_window
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
small = False
if "--small" in sys.argv:
small = True
sys.argv.remove('--small')
sys.argv.remove('--small')

name = "ToUI"
version = __version__
Expand Down
5 changes: 5 additions & 0 deletions tests/assets/full_app.html
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@ <h1>All apps</h1>
</select>
<p id="selected"></p>
</div>
<div style="border: 1px solid">
<button id="get-selector">Get selector</button>
<p id="display-selector"></p>
</div>
<button id="get-itself">Get this button as an arg</button>
<h1>Desktop apps only</h1>
<button id="resize">Resize window</button><br>
</body>
Expand Down
12 changes: 12 additions & 0 deletions tests/full_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,16 @@ def change_selected():
pg.get_element("selected").set_content(f"Selected color is {color}")


def get_element_selector():
pg = app.get_user_page()
element_selector = pg.get_element("get-selector").get_selector()
pg.get_element("display-selector").set_content(element_selector)


def get_itself(element):
element.set_content("success")


def resize(w, h):
window = app.get_user_page().get_window()
if window:
Expand All @@ -142,6 +152,8 @@ def resize(w, h):
home_page.get_element("display-user-vars").onclick(display_user_vars)
home_page.get_element("file-upload").on("change", upload_file)
home_page.get_element("colors").on("change", change_selected)
home_page.get_element("get-selector").onclick(get_element_selector)
home_page.get_element("get-itself").onclick(get_itself, return_itself=True)
home_page.get_element("resize").onclick(resize, 1000, 1000, quotes=False)


Expand Down
2 changes: 1 addition & 1 deletion toui/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@
from .structure import ToUIBlueprint
from . import exceptions

__version__ = "v2.2.0"
__version__ = "v2.3.0"
16 changes: 16 additions & 0 deletions toui/_javascript_templates.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,20 @@
if (_appType === "DesktopApp" && _pywebviewIsLoaded == false) {
await _waitForPywebview()
}
var selector_to_element = false
for (var i = 0; i < args.length; i++) {
if (args[i] instanceof HTMLElement) {
args[i] = {type: "element",
selector: _getElementSelector(args[i])}
selector_to_element = true
}
}
var json = {type: "page",
func: func,
args: args,
"selector-to-element": selector_to_element,
url: urlPath}
_manageProperties()
json['html'] = _getDoc()
Expand Down Expand Up @@ -87,6 +98,11 @@
}
}
var textarea_elements = document.getElementsByTagName("textarea")
for (var textarea_element of textarea_elements) {
textarea_element.setAttribute("value", textarea_element.value)
}
var select_elements = document.getElementsByTagName("select")
for (var select_element of select_elements) {
select_element.setAttribute("value", select_element.value)
Expand Down
16 changes: 13 additions & 3 deletions toui/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -594,12 +594,22 @@ def _communicate(self, ws):
new_page._signal_mode = True
new_page._ws = ws
new_page._inherit_functions()
selector_to_element = data_dict['selector-to-element']
if selector_to_element:
for index, arg in enumerate(args):
if type(arg) is dict:
if arg.get('type') == "element":
args[index] = new_page.get_element_from_selector(arg['selector'])
if "uid" in data_dict:
new_page._uid = data_dict['uid']
session['user page'] = new_page
if new_page._func_exists(func):
new_page._call_func(func, *args)
del session['user page']
try:
if new_page._func_exists(func):
new_page._call_func(func, *args)
del session['user page']
except Exception as e:
del session['user page']
raise e
e = time.time()
debug(f"TIME: {e - s}s")

Expand Down
29 changes: 26 additions & 3 deletions toui/elements.py
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,21 @@ def get_elements(self, tag_name=None, class_name=None, name=None, do_copy=False,
"parent": self._selector}
elements_list.append(element)
return elements_list

def get_selector(self) -> str:
"""
Gets the CSS selector of an element.
Returns
-------
str
None:
If the element is not part of a page.
"""
selector = self._element.name + ''.join([f'[{attr}="{value}"]' for attr, value in self._element.attrs.items()])
return selector

def get_attr(self, name):
"""
Expand Down Expand Up @@ -593,7 +608,7 @@ def _manage_content_functions(self, content):
else:
self._functions[func.__name__] = func

def on(self, event, func_or_name, *func_args, quotes=True):
def on(self, event, func_or_name, *func_args, quotes=True, return_itself=False):
"""
Creates an HTML event attribute and adds a Python function to it.
Expand Down Expand Up @@ -621,6 +636,9 @@ def on(self, event, func_or_name, *func_args, quotes=True):
quotes: bool, default = True
If ``True``, each argument will be surrounded by double quotes.
return_itsef: bool, default=False
If ``True``, the first argument of the function will be the element itself.
Examples
--------
Expand Down Expand Up @@ -655,6 +673,8 @@ def on(self, event, func_or_name, *func_args, quotes=True):
args = ",".join([f'"{arg}"' for arg in func_args])
else:
args = ",".join([f'{obj_converter(arg)}' for arg in func_args])
if return_itself:
args = "this, " + args
if callable(func_or_name):
name = func_or_name.__name__
if self._parent_page:
Expand All @@ -666,7 +686,7 @@ def on(self, event, func_or_name, *func_args, quotes=True):
value = f"{name}({args})"
self.set_attr(name=f"on{event}", value=value)

def onclick(self, func_or_name, *func_args, quotes=True):
def onclick(self, func_or_name, *func_args, quotes=True, return_itself=False):
"""
Creates the HTML event attribute ``onclick`` and adds a Python function to it.
Expand All @@ -690,12 +710,15 @@ def onclick(self, func_or_name, *func_args, quotes=True):
quotes: bool, default = True
If ``True``, each argument will be surrounded by double quotes.
return_itsef: bool, default=False
If ``True``, the first argument of the function will be the element itself.
See Also
--------
Element.on
"""
self.on('click', func_or_name, *func_args, quotes=quotes)
self.on('click', func_or_name, *func_args, quotes=quotes, return_itself=return_itself)

def _from_bs4_tag_no_copy(self, bs4_tag):
self._element = bs4_tag
Expand Down
31 changes: 31 additions & 0 deletions toui/pages.py
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,37 @@ def get_elements(self, tag_name=None, class_name=None, name=None, do_copy=False,
"number": tag_num}
elements_list.append(element)
return elements_list

def get_element_from_selector(self, selector, do_copy=False):
"""
Gets an element from its CSS selector.
Parameters
----------
selector: str
do_copy: bool, default = False
If ``True``, the element will be copied.
Returns
-------
element: Element
If the element was found, an `Element` object will be returned. Otherwise ``None``
will be returned.
"""
bs4_tag = self._html.select_one(selector=selector)
if bs4_tag is None:
return None
element = Element()
if do_copy:
element.from_bs4_tag(bs4_tag)
else:
element._from_bs4_tag_no_copy(bs4_tag)
element._parent_page = self
element._signal_mode = self._signal_mode
element._selector = {"selector": selector}
return element

def get_html_element(self) -> Element:
"""
Expand Down

0 comments on commit e54507c

Please sign in to comment.