justpy-org / justpy Goto Github PK
View Code? Open in Web Editor NEWAn object oriented high-level Python Web Framework that requires no frontend programming
Home Page: https://justpy.io
License: Apache License 2.0
An object oriented high-level Python Web Framework that requires no frontend programming
Home Page: https://justpy.io
License: Apache License 2.0
This framework has introduced me to quasar. I'm currently building out a dashboard and it seems like the way to make a SPA with this is to have QRouteTabs switch between internal pages. Is there a way to support this currently?
When event handler methods fail, they seem to fail silently. No exceptions are raised to the user, the stdout or stderr of the application, nor the browser log. Obviously I could decorate each method to log errors, but I'd think this is more appropriate on framework level. Not sure if oversight?
Reproduction script: 'After' is not printed, nor is tthe AttributeError reported anywhere
import justpy as jp
def main_route():
wp = jp.WebPage()
input_elem = jp.Input(a=wp, type='number', value=5)
input_elem.on('input', faulty_handler)
return wp
def faulty_handler(elem, msg):
print("Before")
_ = elem.invalid_attribute
print("After")
jp.justpy(main_route)
Hello, I am attempting to create components that are drag- and drop-able. However, this proves to be difficult without the actual on_drag_start and on_drag_end events. Will those events be implemented in the future?
So far, I have been able to accomplish a workaround solution:
Task / Problem:
Rearrange the pictures from
to
Solution:
import justpy as jp
import time
from typing import List
t = 0
elem = None
def on_mouse_enter(self, msg):
global elem, t
if (
time.time() - t < 0.001
# Dont swap if source and target element are the same
and elem != self
):
# Execute drag drop by rearranging elements in the components list: move the source element to the position of the target element
components: List[jp.Img] = elem.a.components
# Check if elements have the same parent, only then allow the swap
if self in components:
index1: int = components.index(elem)
e: jp.Img = components.pop(index1)
index2: int = components.index(self)
components.insert(index2, e)
# Without the following, the background colors do not change, so the drag and drop event would not be visible if you are using the same image - is there a better way to do this?
for i in components:
i.style = "background: ;"
elem = self
def on_mouse_out(self, msg):
global t
# mouse_out event is fired shortly before mouse_enter of the new element, mark time
t = time.time()
def drag_drop_test():
wp = jp.WebPage()
parent = jp.Div(a=wp, classes="container flex flex-wrap")
background_colors = ["red", "green", "blue", "orange", "yellow"]
papillons = [
"https://i.imgur.com/0FBbeev.jpg",
"https://i.imgur.com/I9Sgzuy.jpg",
"https://i.imgur.com/PXtHaBk.jpg",
"https://i.imgur.com/bKq0HtS.jpg",
"https://i.imgur.com/tdppr9Z.jpg",
]
for i in range(5):
jp.Img(
a=parent,
classes=f"m-1 p-1 h-48 w-48 bg-{background_colors[i]}-600",
draggable="true",
src=f"{papillons[i]}",
mouseenter=on_mouse_enter,
mouseout=on_mouse_out,
)
return wp
jp.justpy(drag_drop_test)
Sideeffects: if you simply move your mouse too quickly over the images (without any actual clicks), they will get rearranged in an unpredictable order.
Would it be possible to add the Quasar File Picker as a component?
I tried to add it as a child of the QInput component (see below), but when I select a file, the file name is not shown in the File Picker field.
class QFile(jp.QInput):
html_tag = 'q-file'
slots = ['default_slot', 'prepend_slot', 'append_slot', 'before_slot', 'after_slot', 'error_slot', 'hint_slot',
'counter_slot', 'loading_slot', 'file_slot', 'selected_slot']
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.type = 'object'
self.value = kwargs.get('value', None)
self.prop_list = [
'name', 'multiple', 'accept', 'max-file-size', 'max-total-size', 'filter', 'error',
'rules', 'lazy-rules', 'loading', 'clearable', 'autofocus', 'for', 'max-files', 'counter-label',
'error-message', 'no-error-icon', 'label', 'stack-label', 'hint', 'hide-hint', 'prefix', 'suffix', 'loading',
'clearable', 'clear-icon', 'bottom-slots', 'counter',
'tabindex',
'value',
'display-value', 'use-chips',
'readonly', 'disable',
'label-color', 'color', 'bg-color', 'dark', 'filled', 'outlined', 'borderless', 'standout', 'hide-bottom-space',
'rounded','square', 'dense', 'item-aligned', 'input-class', 'input-style'
]
self.allowed_events = ['input', 'clear']
Hi There,
First of all great project/idea. Would love to get involve in it. Found today itself, so was trying to explore the examples and trying it out. While doing that notice the example from link: https://justpy.io/tutorial/input/ which basically shows how to handle the input events - focus in and focus out, setting the focus etc is not working.
Below is the exact program which I tried, and it does prints the lines where I suspected whether control is going or not. But still, on pressing the 'Enter' key the focus is not going to other input control. I checked on Chrome and Edge (I dont have other browser currently) and on both pressing Enter is not doing any action.
Let me know how I can help.
import justpy as jp
def my_blur(self, msg):
self.focus = False
def key_down(self, msg):
# print(msg.key_data)
key = msg.key_data.key
if key=='Escape':
self.value=''
return
if key=='Enter':
self.focus = False
try:
print("This line prints!")
next_to_focus = self.input_list[self.num + 1]
except:
next_to_focus = self.input_list[0]
print("And this line too prints!")
next_to_focus.focus = True
return
return True # Don't update the page
def focus_test():
wp = jp.WebPage()
d = jp.Div(classes='flex flex-col m-2', a=wp, style='width: 600 px')
input_list = []
number_of_fields = 5
for i in range(1, number_of_fields + 1):
label = jp.Label( a=d, classes='m-2 p-2')
jp.Span(text=f'Field {i}', a=label)
in1 = jp.Input(classes=jp.Styles.input_classes, placeholder=f'{i} Type here', a=label, keydown=key_down, spellcheck="false")
in1.on('blur', my_blur)
in1.input_list = input_list
in1.num = i - 1
input_list.append(in1)
return wp
jp.justpy(focus_test)
I’d like to watch the project for release. Do you mind creating releases so that I am notified when new versions are available?
First off, thanks for the great python module! I've been testing it a lot and I'm starting to try to integrate it with Django to render forms.
When I define hint for QInput or QSelect it does not show. I also tried using the hint_slot, but it doesn't work either. I have tried the after_slot and it works, but isn't in the right place.
I tried digging trough to see what is happening, but I don't really see much mention of this in the code, so I'm not sure how to fix this.
Below is an example
# Using hint
input1 = jp.QInput(a=wp, model=[wp, 'field'], label='My Field', hint="This is my hint.")
# Using hint_slot
input2 = jp.QInput(a=wp, model=[wp, 'field'], label='My Field')
input2.hint_slot = jp.Label(text="This is my hint.", classes="text-red")
Hi
I have deployed a simple Justpy app to Azure App Service. Locally it works great, but in Azure there is significant lag when using input fields. If I type something into a field, characters get erased, selections get reverted etc. I suspect this is due to network lag. The clicked UI components also get this red thin border around them which doesn't happen on local host.
I had to revert to AJAX as the App Service websocket support doesn't seem to work with Justpy.
My app is an Eventhubs test data generator and it uses Justpy functionality to start a background process to generate events. As said, it works beautifully on my local machine.
Are there any tweaks to disable some AJAX functionality to diminish some of the lag? I tried setting "debounce" to 3000 on all input components, but it didn't really help.
I discovered this library today and have been enjoying the ag-Grid examples.
One can use pandas with SQL queries and in turn use JustPy to show these dataframes as tables. This is hugely useful given the small amount of code required and my programming abilities.
I note that I can grid.options.columnDefs[1].editable = True
(here for column 1) to allow the table rows to be edited in place. Is it possible to pass these updates back into the dataframe?
More generally I am in need of a CRUD application which can allow end users to edit various database tables.
I thought perhaps the QButtons example could be modified such that each button represented a DB table but I am unsure if Quasar and ag-Grid can be combined in such a way. As best I can tell ag-Grid is focussed on presenting a single table. Perhaps QTabs where each tab reflects a DB table? I note you mention QTabPanels is not working.
Any advice on how to approach this with JustPy would be very welcome.
Many thanks.
JustPy allows you to write custom components as classes.
However, the way it implements event handlers doesn't work with OOP style.
from justpy import Div, WebPage
import justpy
class CustomDiv(Div):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.style = "width: 100px; height: 100px; background: black;"
self.on('click', self.clicked)
def clicked(self, event):
print(event)
def main():
page = WebPage()
page.add(CustomDiv())
return page
if __name__ == '__main__':
justpy.justpy(main)
Clicking the div gives the following error: INFO: Event result: Attempting to run event handler:clicked() takes 2 positional arguments but 3 were given
This is because the self
attribute of the method clicked
is already bound to the class instance. Justpy tries to supply the value for the self
argument as well, which is not needed if you use a OOP style.
E. g. if I want to insert Google Adsense or Analytics — can I do it somehow?
I've tried using inner_html for that but with no luck, it breaks the whole page.
It would be awesome if there was a better way to have the QStepper without the Carousel.
Below is a workaround using css on QDivs:
steps=[
{"status": "done", "step": 1, "title": "Submitted"},
{"status": "done", "step": 2, "title": Confirmed"},
{"status": "active", "step": 3, "title": "In Process"},
{"status": "", "step": 4, "title": "Completed"}
]
Stepper(steps=steps, a=whatever_you_want)
class Stepper(jp.QDiv):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.steps = kwargs.get('steps', None)
stepper_base = jp.QDiv(
classes='q-stepper q-stepper--horizontal',
color="primary",
a=self,
style='max-width:900px'
)
self.stepper_header = jp.QDiv(
classes='q-stepper__header row items-stretch justify-between
q-stepper__header--alternative-labels',
style='text-align:left',
a=stepper_base
)
for step in self.steps:
self.get_step(step['status'], step['step'], step['title'])
def get_step(self, status, step_number, caption):
step_status = ''
if status == 'active':
step_status = 'q-stepper__tab--active'
if status == 'done':
step_status = 'q-stepper__tab--done'
step = jp.QDiv(
classes=f'q-stepper__tab col-grow flex items-left no-wrap
relative-position {step_status}',
a=self.stepper_header
)
step_container = jp.QDiv(
classes='q-stepper__dot row flex-center q-stepper__line relative-position',
style='margin-left:40px',
a=step
)
if status == 'done':
done_span = jp.Span(a=step_container)
jp.QIcon(name='check', a=done_span)
else:
jp.Span(text=f"{step_number}", a=step_container)
jp.Div(
text=caption.upper(),
style='margin-top:10px;max-width:100px;text-align:center;
font-weight:bold;color: #666',
a=step
)
In order to compose pages one can parse html or one can manually create instances of JustPy classes and connect them together with the add method or with the a=comp_ref construct (the docs prefer the latter).
eg:
c3 = jp.QItem(clickable=True, v_ripple=True, a=c2, click=button_click)
c3.user_name = u['name']
c4 = jp.QItemSection(avatar=True, a=c3)
c5 = jp.QAvatar(size='100px',
font_size='52px',
color=color_from_state(u['state']),
text_color='white',
icon=icon_from_state(u['state']),
a=c4)
c3.avatar = c5
c6 = jp.QItemSection(a=c3)
c7 = jp.QItemLabel(a=c6, classes=['text-h3'], text=u['name'])
c8 = jp.QItemLabel(caption=True, a=c6, text=u['state'])
c3.state_label = c8
c9 = jp.QSeparator(spaced=True, inset=True, a=c2)
When using this manual construction method one gives up the visual nesting that is clear in HTML code. This makes it harder to see the page's structure (IMHO).
Maybe an alternative construction method could be useful:
c3 = jp.QItem([
jp.QItemSection(avatar=True),
jp.QAvatar(size='100px',
font_size='52px',
color=color_from_state(u['state']),
text_color='white',
icon=icon_from_state(u['state'])),
jp.QItemSection( [
jp.QItemLabel(classes=['text-h3'], text=u['name'])
jp.QItemLabel(caption=True, a=c6, text=u['state'])),
],
jp.QSeparator(spaced=True, inset=True, a=c2)
],
clickable=True, v_ripple=True, a=c2, click=button_click)
)
or with a kwarg 'children=[]'.
I agree that this could get messy as well (just look at JS code), but it does make the structure clearer.
What do you think?
Hi,
I'm interested in accessing the full MouseEvent information for a mouse click event.
What is the canonical way to do that? I've poked around inside the msg
parameter from the general event template, but it appears to be missing from there (or I'm overlooking it).
Thanks in advance, and many thanks for making justpy!
here is my code
import justpy as jp
menu = '''
<q-toolbar class="bg-grey-3">
<q-btn flat round dense>
<q-icon name="menu" />
<q-menu>
<q-list style="min-width: 100px">
<q-item clickable v-close-popup>
<q-item-section><a href='/about' style="text-decoration: none">about</a>
</q-item-section>
</q-item>
<q-separator />
<q-item clickable v-close-popup>
<q-item-section><a href='/tool' style="text-decoration: none">help</a>
</q-item-section>
</q-item>
</q-list>
</q-menu>
</q-btn>
<q-toolbar-title>
Toolbar
</q-toolbar-title>
<q-btn flat round dense>
<q-icon name="more_vert" />
</q-btn>
</q-toolbar>
'''
@jp.SetRoute('/')
def index():
wp = jp.QuasarPage()
c = jp.parse_html(menu, a=wp)
wp.add(jp.P(text='Index page', classes='text-5xl m-2'))
return wp
@jp.SetRoute('/about')
def about():
wp = jp.QuasarPage()
c = jp.parse_html(menu, a=wp)
wp.add(jp.P(text='Hello there!', classes='text-5xl m-2'))
return wp
jp.justpy()
Instead of hardcoding, Is there better way to put toolbar of every pages?
Hope the html template inheritance like flask or django.
Up front, love what I've seen of the framework so far, thanks!
I'm going through the tutorial, and I'm at the Input Type paragraph here. I'm running the exact script copy-pasted from the tutorial there.
When I test that page in Firefox 73.0.1 64-bit (no issue in Chrome/Edge), it behaves as expected for the first 16 characters. Then, starting when I type the 17th character in the input box, it changes the number in the input box after I've typed it.
For example, I input 1234567890123456 and it works as expected: I have 1234567890123456 in the input box, and in the text box below. When I type the 17th character, 7, as soon as the debounce expires, the input box changes from "12345678901234567" to "12345678901234568".
Worth noting that the input box appears to populate correctly (at least until the next digit is input after the expiration of the debounce cycle, at which point it populates with the updated contents of the input field). That makes me suspect there is type checking of the input field itself going on that contains some kind of typing issue. I'm not a web dev, though, so that's purely conjecture.
Hi,
thanks for this awesome project. I'm currently working my way through the tutorial and it's great and easy to follow.
I found one smaller problem: Since port 8000 is in use on my machine, I needed to set a different port.
If you pass a host or port to the justpy function to start a server like this
jp.justpy(hello_world, port=5100)
an incorrect port is displayed.
@app.on_event('startup')
async def justpy_startup():
# ....
print(f'JustPy ready to go on http://{HOST}:{PORT}')
At the point of the print command, the host and port values from the startup are not accessable, so either the start function should change the globals or the host and port numbers should note get printed on startup.
I'm trying to combine a couple of the examples in the Tutorial: the 'Clock' example in Pushing Data to Web Pages with the Table component example in Creating Your Own Components. In other words, I want to present a table and update the values periodically, so as to have a sort of dashboard of some data I want to display.
It's not working: I am trying to update the .values property of the table (and then calling update() on the Table instance) but the browser does not update. If I refresh the browser, it has appended the new data to the old data. If call WebPage.update() instead of Table.update() then it is same as the manual refresh browser and again I see the table expand with each update.
I'm just getting started with this so I could use a little guidance. I'm really impressed with the concept and hope to use it for a project I am working on. Thanks.
Currently, to be able to handle a keypress event on the entire page, we need to have an input element somewhere. Normally, in the absence of any input element, the keypress event gets triggered on document.body
Currently, JustPy provides no way of adding event handlers on the document body.
Maybe I'm going about this in the wrong way, but I'm trying to change the text on a button while waiting for the click event to finish. Is this possible? The button doesn't render until the click event is finished, I tried writing a before event, but that didn't work either.
Below is an example of what I'm trying to do. Thanks!
import justpy as jp
btn_classes = jp.Styles.button_outline + ' m-2'
async def btn_click(self, msg):
print('click')
await jp.asyncio.sleep(5)
async def btn_before(self, msg):
print('before')
self.text = 'I was clicked.'
def after_demo():
wp = jp.WebPage()
jp.Button(text=f'My Button', a=wp,
click=btn_click,
before=btn_before,
classes=btn_classes
)
return wp
jp.justpy(after_demo)
Hello, project is really fun to play around, congrats!
Found a discrepancy in one of the Quasar classes:
https://github.com/elimintz/justpy/blob/186aeaacc92fe42f29bc56798e208d5b100fa5a9/justpy/quasarcomponents.py#L413
applies as well for QToolBarTitle
(should be rather QToolbarTitle
).
Cheers!
import justpy as jp
button_classes = 'bg-transparent hover:bg-blue-500 text-blue-700 font-semibold hover:text-white py-2 px-4 border border-blue-500 hover:border-transparent rounded m-2'
input_classes = 'border m-2 p-2'
session_data = {}
def form_test():
wp = jp.WebPage()
wp.display_url = '/fill_form'
form1 = jp.Form(a=wp, classes='border m-1 p-1 w-64')
user_label = jp.Label(text='User Name', classes='block uppercase tracking-wide text-gray-700 text-xs font-bold mb-2', a=form1)
in1 = jp.Input(placeholder='User Name', a=form1, classes='form-input')
user_label.for_component = in1
password_label = jp.Label(classes='block uppercase tracking-wide text-gray-700 text-xs font-bold mb-2 mt-2', a=form1)
jp.Div(text='Password', classes='block uppercase tracking-wide text-gray-700 text-xs font-bold mb-2', a=password_label)
jp.Input(placeholder='Password', a=password_label, classes='form-input', type='password')
check_label = jp.Label(classes='text-sm block', a=form1)
jp.Input(type='checkbox', a=check_label, classes='form-checkbox text-blue-500')
jp.Span(text='Send me stuff', a=check_label, classes= 'ml-2')
submit_button = jp.Input(value='Submit Form', type='submit', a=form1, classes=button_classes)
def submit_form(self, msg):
print(msg)
msg.page.redirect = '/form_submitted'
session_data[msg.session_id] = msg.form_data
form1.on('submit', submit_form)
return wp
@jp.SetRoute('/form_submitted')
def form_submitted(request):
wp = jp.WebPage()
wp.display_url = '/thanks'
jp.Div(text='Thank you for submitting the form', a=wp, classes='text-xl m-2 p-2')
for field in session_data[request.session_id]:
if field.type in ['text', 'password']:
jp.Div(text=f'{field.placeholder}: {field.value}', a=wp, classes='text-lg m-1 p-1')
elif field.type == 'checkbox' and field.checked:
jp.Div(text='We will send you stuff', a=wp, classes='text-lg m-1 p-1')
return wp
jp.justpy(form_test, websockets=False)
I noticed that when disabling websockets the .open
and .redirect
page methods has no effect, am i missing extra step ?
There is a typo in quasarcomponents.py file. Slots value of QBtnGroup must be default_slot (not defualt_slot).
Seems like there's an error in this branch of code:
�[31mERROR�[0m: Exception in ASGI application
Traceback (most recent call last):
File "C:\Users\youngta1\AppData\Local\Continuum\anaconda3\lib\site-packages\uvicorn\protocols\websockets\websockets_impl.py", line 153, in run_asgi
result = await self.app(self.scope, self.asgi_receive, self.asgi_send)
File "C:\Users\youngta1\AppData\Local\Continuum\anaconda3\lib\site-packages\uvicorn\middleware\proxy_headers.py", line 45, in call
return await self.app(scope, receive, send)
File "C:\Users\youngta1\AppData\Local\Continuum\anaconda3\lib\site-packages\starlette\applications.py", line 102, in call
await self.middleware_stack(scope, receive, send)
File "C:\Users\youngta1\AppData\Local\Continuum\anaconda3\lib\site-packages\starlette\middleware\errors.py", line 143, in call
await self.app(scope, receive, send)
File "C:\Users\youngta1\AppData\Local\Continuum\anaconda3\lib\site-packages\starlette\middleware\gzip.py", line 20, in call
await self.app(scope, receive, send)
File "C:\Users\youngta1\AppData\Local\Continuum\anaconda3\lib\site-packages\starlette\exceptions.py", line 58, in call
await self.app(scope, receive, send)
File "C:\Users\youngta1\AppData\Local\Continuum\anaconda3\lib\site-packages\starlette\routing.py", line 550, in call
await route.handle(scope, receive, send)
File "C:\Users\youngta1\AppData\Local\Continuum\anaconda3\lib\site-packages\starlette\routing.py", line 283, in handle
await self.app(scope, receive, send)
File "C:\Users\youngta1\AppData\Local\Continuum\anaconda3\lib\site-packages\starlette\endpoints.py", line 76, in dispatch
await self.on_disconnect(websocket, close_code)
File "C:\Users\youngta1\AppData\Local\Continuum\anaconda3\lib\site-packages\justpy\justpy.py", line 220, in on_disconnect
pid = websocket.page_id
AttributeError: 'WebSocket' object has no attribute 'page_id'
A majority of the applications I build are intranet facing and require uptime even when the Internet connection goes down. Last week I had a connection go down for an entire day.
Would it be possible to add an option to specify a custom template so that I can serve the css and javascript files locally incase the Internet connection goes down?
Thank you for this consideration.
I'm trying to embed the Ace Editor in my application.
https://ace.c9.io/#nav=embedding
with the following script in head_html
, I just need to call the create_editor
and destroy_editor
functions with the appropriate strings.
Is there a way to invoke a JS function from the python backend?
<script src="/static/ace-builds/src-noconflict/ace.js" type="text/javascript" charset="utf-8"></script>
<script>
window.all_editors = {};
function create_editor(element_id) {
var editor = ace.edit(element_id);
editor.setTheme("ace/theme/monokai");
editor.session.setMode("ace/mode/markdown");
window.all_editors[element_id] = editor;
}
function destroy_editor(element_id) {
window.all_editors[element_id].destroy();
delete window.all_editors[element_id];
}
</script>
Hi. Self explanatory. At the top of the rendered web page, it shows JustPy with some nice icon.
It'd be good to be able to edit that.
I'd made a programming error in a button handler. However no exception appeared and the program kept running (without the intended behaviour obviously). Also in the PyCharm no exception was caught.
BTW JustPy is a godsend. Please promote it more!
Awesome project, thanks.
A nice feature would be to set components of a Div during initialisation, e.g.
def hello_world():
wp = jp.WebPage()
jp.Div(
components=[
jp.P(text="hello")
],
a=wp
)
return wp
Hello,
Steps to reproduce:
async def on_click(item, msg): print(f'Step 0: {perf_counter()} - {item}') item.text = 'Waiting...' print(f'Step 1: {perf_counter()} - {item}') await msg.page.update() print(f'Step 2: {perf_counter()} - {item}') await jp.asyncio.sleep(5) print(f'Step 3: {perf_counter()} - {item}') item.text = 'Done' print(f'Step 4: {perf_counter()} - {item}') await msg.page.update() print(f'Step 5: {perf_counter()} - {item}')
Expected behavior:
Actual behavior:
Since the client events are send to the server for processing, JustPy doesn't seem like a framework which can scale.
Building GUI applications in the browser is where JustPy shines. Unlike other frameworks like Qt which give you only a watered-down version of a web browser and painful APIs and ugly components, JustPy allows you to use the full power of CSS and JS.
Also, for desktop applications, since no request is being sent out of your system (server is on localhost), it is secure and fast!
If JustPy is remodeled/advertized as a GUI framework for Python applications instead of as a webserver, it can gain a lot of traction! :)
Hi,
Thank you for this amazing module!
I was wondering if you can provide an example of a Qtable with a body-cell-slot like the example here: https://quasar.dev/vue-components/table#Example--Body-cell-%5Bname%5D-slot
What I would really like to do is add some buttons at the end of a table row, but I can't seem to figure out how to use these slots. It looks like part of the problem is that there isn't a Qtr and Qtd classes in justpy? But it's likely I'm just missing something very basic.
Thanks!
Below code (modified from the justpy homepage) results in the head_html string being shown on the web page. The html tags seem to be escaped. This can be solved by adding the safe keyword to the Jinja tag of the quasar.html file.
On line 30, {{ page_options.head_html }}
should become {{ page_options.head_html | safe }}
- if I'm not mistaken?
import justpy as jp
def test_head():
wp = jp.QuasarPage()
wp.head_html = '<script>console.log("Hello there from head");</script>'
wp.body_html = '<script>console.log("Hello there from body");</script>'
jp.Div(text='Testing...', a=wp)
return wp
jp.justpy(test_head)
Hi
I find the parse_html method useful for one time conversion of existing html to code. But even then, it requires quite some work with adding id's and then using name_dict to hook up event handlers. Would it be possible to parse event handlers (like @click) in the html and hook them automatically to event handlers in code?
'''
html='<div @click="do_something/>'
def so_something(...):
...
c = jp.parse_html(html)
'''
Is it possible to enable Fontawesome Pro if one has a license?
I'd like to make simple web app for square calculator.
here is my code
import justpy as jp
async def square_input(request):
wp = jp.WebPage(data={'result':''})
result_div = jp.Div(text='Result will show up here',
classes=p_classes,
model=[wp,'result'])
in1 = jp.Input(type='number', a=wp)
in2 = jp.Input(type='number', a=wp)
wp.data['result'] = in1*in2
wp.add(reuslt_div)
return wp
jp.justpy(square_input)
I got the error msg like this.
TypeError: unsupported operand type(s) for *: 'Input' and 'Input'
I hope there is more example in official documents. Please help me out.
One of the apps I'm replacing with JustPy requires a signature pad component. I noticed that there is already a Vue Signature Pad Component (https://github.com/neighborhood999/vue-signature-pad). Is this something you would be interested in having within the JustPy framework?
Currently, if a route/component raises a sys.exit()
call (which simply raises the SystemExit
exception), the exception is caught in some umbrella except Exception
block.
Thus, one cannot have a route that terminates the app.
I'm not sure if a webserver framework should allow routes that take down the entire server.
My particular usecase is that my code needs root
to run and a non-root PyCharm is unable to terminate root's processes. So I need an endpoint that terminates the server completely.
import justpy
import sys
@justpy.SetRoute('/exit/')
def exit():
sys.exit()
if __name__ == '__main__':
justpy.justpy()
/Users/pragyagarwal/MW/mindwiki/trial.py
INFO: Started server process [60138]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
ERROR: Exception in ASGI application
Traceback (most recent call last):
File "/Users/pragyagarwal/MW/venv/lib/python3.8/site-packages/uvicorn/protocols/http/httptools_impl.py", line 385, in run_asgi
Module directory: /Users/pragyagarwal/MW/venv/lib/python3.8/site-packages/justpy, Application directory: /Users/pragyagarwal/MW/mindwiki
JustPy ready to go on http://127.0.0.1:8000
INFO: 127.0.0.1:62350 - "GET /exit/ HTTP/1.1" 500 Internal Server Error
result = await app(self.scope, self.receive, self.send)
File "/Users/pragyagarwal/MW/venv/lib/python3.8/site-packages/uvicorn/middleware/proxy_headers.py", line 45, in __call__
return await self.app(scope, receive, send)
File "/Users/pragyagarwal/MW/venv/lib/python3.8/site-packages/starlette/applications.py", line 102, in __call__
await self.middleware_stack(scope, receive, send)
File "/Users/pragyagarwal/MW/venv/lib/python3.8/site-packages/starlette/middleware/errors.py", line 159, in __call__
await self.app(scope, receive, _send)
File "/Users/pragyagarwal/MW/venv/lib/python3.8/site-packages/starlette/middleware/gzip.py", line 18, in __call__
await responder(scope, receive, send)
File "/Users/pragyagarwal/MW/venv/lib/python3.8/site-packages/starlette/middleware/gzip.py", line 35, in __call__
await self.app(scope, receive, self.send_with_gzip)
File "/Users/pragyagarwal/MW/venv/lib/python3.8/site-packages/starlette/exceptions.py", line 71, in __call__
await self.app(scope, receive, sender)
File "/Users/pragyagarwal/MW/venv/lib/python3.8/site-packages/starlette/routing.py", line 550, in __call__
await route.handle(scope, receive, send)
File "/Users/pragyagarwal/MW/venv/lib/python3.8/site-packages/starlette/routing.py", line 227, in handle
await self.app(scope, receive, send)
File "/Users/pragyagarwal/MW/venv/lib/python3.8/site-packages/starlette/endpoints.py", line 30, in dispatch
response = await handler(request)
File "/Users/pragyagarwal/MW/venv/lib/python3.8/site-packages/justpy/justpy.py", line 131, in get
load_page = func_to_run()
File "/Users/pragyagarwal/MW/mindwiki/trial.py", line 6, in exit
sys.exit()
SystemExit
INFO: 127.0.0.1:62351 - "GET /favicon.ico HTTP/1.1" 200 OK
This framework looks promising, but it seems to be highly opinionated.
We can turn off the addons by setting the justpy.env file, but it seems that even with HIGHCHARTS, AGGRID, TAILWIND and QUASAR disabled, the JS and CSS for them are still loaded in the frontend.
Furthermore, fontawesome, animae, hover and simplemde CSS is loaded after the page's css, which makes overriding styles a pain.
It would be nice to be able to configure justpy in a manner that leaves all these addons out.
QTable class have "losding" instead of "loading" between attributes
justpy/justpy.py
@app.on_event('startup')
async def justpy_startup():
WebPage.loop = asyncio.get_event_loop()
JustPy.loop = WebPage.loop
Now that asyncio.get_event_loop()
is guaranteed to always give the currently running event loop (if one is running), we no longer need to save these values.
This also allows us to elegantly execute async functions from sync functions if we do not care about the result immediately.
For example:
def run_javascript(self, javascript_string: str) -> None:
asyncio.get_event_loop().create_task(
self.container_page.run_javascript(
javascript_string=javascript_string
)
)
I was trying to use justpy in order to generate interactive HTML reports. Following the documentation i wanted to use the .to_html
method on the generated webpage object as the following
def grid_test():
wm_df = pd.read_csv('https://elimintz.github.io/women_majors.csv').round(2)
wp = jp.WebPage()
grid = jp.AgGrid(a=wp)
grid.load_pandas_frame(wm_df)
return wp.to_html()
But the following error is raised .to_html {AttributeError}'AgGrid' object has no attribute 'to_html'
Is this expected ?
Hi,
Thanks for making justpy! I'm having a lot of fun working through the excellent tutorial.
I ran the following example from the tutorial with a slightly modified DataFrame containing a datetime column:
import justpy as jp
import pandas as pd
wm_df = pd.DataFrame({'year':pd.to_datetime(['2019', '2020']), 'num':[3, 1]})
def grid_test():
wp = jp.WebPage()
grid = jp.AgGrid(a=wp)
grid.load_pandas_frame(wm_df)
return wp
jp.justpy(grid_test)
The example fails with an error of
500 Server Error
TypeError: Object of type 'Timestamp' is not JSON serializable
and a traceback of
Traceback
File /opt/anaconda/envs/py36/lib/python3.6/json/encoder.py, line 180, in default‒
177.
178. """
179. raise TypeError("Object of type '%s' is not JSON serializable" %
180. o.__class__.__name__)
181.
182. def encode(self, o):
183. """Return a JSON string representation of a Python data structure.
File /opt/anaconda/envs/py36/lib/python3.6/json/encoder.py, line 257, in iterencode+
File /opt/anaconda/envs/py36/lib/python3.6/json/encoder.py, line 199, in encode+
File /opt/anaconda/envs/py36/lib/python3.6/json/__init__.py, line 231, in dumps+
File /opt/anaconda/envs/py36/lib/python3.6/site-packages/justpy/justpy.py, line 144, in get+
File /opt/anaconda/envs/py36/lib/python3.6/site-packages/starlette/endpoints.py, line 30, in dispatch+
File /opt/anaconda/envs/py36/lib/python3.6/site-packages/starlette/routing.py, line 227, in handle+
File /opt/anaconda/envs/py36/lib/python3.6/site-packages/starlette/routing.py, line 550, in __call__+
File /opt/anaconda/envs/py36/lib/python3.6/site-packages/starlette/exceptions.py, line 71, in __call__+
File /opt/anaconda/envs/py36/lib/python3.6/site-packages/starlette/exceptions.py, line 82, in __call__+
File /opt/anaconda/envs/py36/lib/python3.6/site-packages/starlette/middleware/gzip.py, line 35, in __call__+
File /opt/anaconda/envs/py36/lib/python3.6/site-packages/starlette/middleware/gzip.py, line 18, in __call__+
File /opt/anaconda/envs/py36/lib/python3.6/site-packages/starlette/middleware/errors.py, line 156, in __call__
The dtypes for the columns of wm_df
are
year datetime64[ns]
num int64
Absolutely loving JustPy!!! About to go live with my first justpy application using apache and gunicorn and I'm curious if you know of a way where I can update my application without my clients losing the websocket connection?
Is this feature on the roadmap? To be able to have localized css/js/etc resources for implementations on internet-restricted company environments.
If not, I'm happy to pull request:
Nice framework built on nice idea. It abstracts state management away from request/response cycle and makes it easy like developing a desktop application like in Qt.
I am trying to understand how I would enable/prevent an event from propagating once handled.
For example, in the enter_handler
function below, I want the keyup
event to be propagated without any disturbance, for all other keys except Enter
. Upon receiving Enter
I want to handle it specially and do the equivalent of event.preventDefault()
.
import justpy as jp
input_classes = (
"m-2 bg-gray-200 border-2 border-gray-200 rounded w-64 py-2 px-4 "
"text-gray-700 focus:outline-none focus:bg-white focus:border-purple-500"
)
p_classes = "m-2 p-2 h-32 text-xl border-2"
async def enter_handler(self, event):
if event.key_data.key == "Enter":
self.div.text = "\n" + self.value
self.value = ""
async def input_demo(request):
wp = jp.WebPage()
in1 = jp.Input(a=wp, classes=input_classes, placeholder="Please type here")
in1.div = jp.Div(
text="What you type will show up here", classes=p_classes, a=wp
)
in1.on("keyup", enter_handler)
return wp
jp.justpy(input_demo)
At present, the classes property is a space-separated list of strings, just like it is in HTML. This makes adding and removing classes cumbersome.
If the classes is a Set[str]
, one can use the set.add()
and set.remove()
methods for the job. Finally, when the classes need to be applied to the elements, JustPy could simply do ' '.join(classes)
I have deployed a web page on my intranet using Justpy (which is great by the way!), but I have a couple of clients that do not have access internet access, and they cannot display the web page, as soon as I enable internet access they are able to display the page correctly, but I cannot leave internet access enabled.
Any work around or setting I can modify to make a justpy page work without internet access?
Loving JustPy! However, when I add an input_style argument to a QSelect it doesn't honor the input style, it overrides it with the default color.
For example:
jp.QSelect(input_style='color:#0088cc !important;text-decoration: underline dotted')
With a slight tweak to the tutorial, I'm noticing it's not rendering the leading-whitepace on many of the lines in my browser.
Example code:
import justpy as jp
string = [
'JustPy',
'',
' What is JustPy?',
' Introduction',
' Hello World!',
' Under the Hood',
' News and Updates',
' License',
' Tutorial',
' Getting Started',
' Basic Concepts',
' Handling Events',
' HTML Components',
]
def my_click(self, msg):
self.text = 'I was clicked!'
def hello_world():
wp = jp.WebPage()
for sub_str in string:
print(sub_str)
d = jp.Div(text=sub_str)
d.on('click', my_click)
wp.add(d)
return wp
jp.justpy(hello_world)
I comes out as:
JustPy
I was clicked!
Introduction
I was clicked!
Under the Hood
News and Updates
License
Tutorial
Getting Started
Basic Concepts
Handling Events
HTML Components
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.