Coder Social home page Coder Social logo

justpy-org / justpy Goto Github PK

View Code? Open in Web Editor NEW
1.2K 1.2K 93.0 13.28 MB

An object oriented high-level Python Web Framework that requires no frontend programming

Home Page: https://justpy.io

License: Apache License 2.0

Python 4.09% HTML 0.21% JavaScript 95.33% CSS 0.25% Shell 0.11% Dockerfile 0.01% Procfile 0.01%
ag-grid asgi asynchronous highcharts nicegui python quasar-framework starlette web-framework

justpy's People

Contributors

azazel75 avatar bapowell avatar byzakyz avatar elimintz avatar eudoxos avatar falkoschindler avatar fjbay avatar florianwittkamp avatar freemant2000 avatar haroldo-bonette avatar hroemer avatar kimgiftww avatar platinops avatar rodja avatar talamuyal avatar tholzheim avatar wolfgangfahl avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

justpy's Issues

QRouteTab support

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?

Event handlers exceptions are silent

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)

Feature Request: Support for drag and drop events

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
From
to
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.

Feature request: Add Quasar File Picker

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']

Setting focus example not working

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)

Use github releases

I’d like to watch the project for release. Do you mind creating releases so that I am notified when new versions are available?

hint and hint_slot does not work for QInput/QSelect

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")

How to reduce UI lag

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.

Combining Quasar and ag-Grid for CRUD?

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.

Event Handlers don't work with bound methods

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.

QStepper without the Carousel

Screen Shot 2020-03-12 at 12 45 38 AM
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
        )

Code nesting

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?

How can I put menu on top?

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.

Bug with number input box in Firefox 73.0.1 (64-bit)

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.

Startup message may report wrong HOST and PORT

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.

Is there a way to update Table values?

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.

Feature Request: Support for handling keypress event on body.

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.

Changing button text while waiting on click event

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)

Form example not redirecting when using websockets=False

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 ?

Typo in QBtnGroup

There is a typo in quasarcomponents.py file. Slots value of QBtnGroup must be default_slot (not defualt_slot).

'WebSocket' object has no attribute 'page_id'

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'

Custom Template with no Internet Dependencies

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.

Calling a javascript function from python code?

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>

To be able to edit head title

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.

Justpy seems to swallow exceptions in event handlers

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!

Components in Div

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

Framework can't process frontend events in an asynchronous way

Hello,

Steps to reproduce:

  1. Create page with several buttons
  2. Each button should have it's own async on_click handler. For example:
    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}')
  3. Start server
  4. Try to click on each button on a page without a delay

Expected behavior:

  1. Each clicked button should get a "Waiting..." text immediately after a click. Server log should have several 0 - 2 Steps logs from an each of the clkicked buttons.
  2. Approximately after 5 seconds, an each clicked button will get a "Done" text. Server log should have several 3 - 5 Steps logs from an each of the clkicked buttons.

Actual behavior:

  1. Only the first clicked button is getting "Waiting..." text. Server log has 0 - 2 Steps logs from only the first clicked button.
  2. After 5 seconds, only the first clicked button is getting "Done" text. Server log has 3 - 5 Steps logs from only the first clicked button.
  3. Only the second clicked button is getting "Waiting..." text. Server log has additional 0 - 2 Steps logs: from only the second clicked button.
  4. After 5 seconds, only the second clicked button is getting "Done" text. Server log has additional 3 - 5 Steps logs: from only the secondclicked button.
  5. ... third clicked button ...
    ... and so on.

Use of JustPy as a GUI framework for desktop applications made using Python

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! :)

Table body-cell-slot

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!

For a QuasarPage the head_html is not working properly

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)

Make .parse_html more useful?

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)

'''

silly question from noob

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.

JustPy doesn't allow the SystemExit exception to end the process

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

Decouple justpy from the addons

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.

WebPage.loop, JustPy.loop are now redundant

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
        )
    )

.to_html {AttributeError}'AgGrid' object has no attribute 'to_html'

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 ?

ag-grid fails with Pandas DataFrame with a datetime column

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

Websocket Auto-Reconnect After Code Changes

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?

deployment with local assets

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:

  • Flag on the page class itself that specifies if local/cdn is desired.
  • Separate base html template to identify local assets

How to preventDefault() in event handlers?

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)

Make `HTMLComponent.classes` a set of strings

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)

Internet access required for client?

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?

QSelect not honoring input_style argument

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')

Leading whitespace not rendered

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

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.