pywebio / PyWebIO

Write interactive web app in script way.
https://pywebio.readthedocs.io
MIT License
4.54k stars 383 forks source link

server stuck and failed to start when trying to load image from the very beginning #31

Closed lanyusea closed 3 years ago

lanyusea commented 3 years ago

BUG描述 The server got stuck when I trying to create an output before start_server

The original purpose is to make pywebio display an image, and once a button gets clicked the image will change into another. But I found if I declare the output before the start server callback function, the program will be stuck.

The bad code:

import ...

image = open('image/url/')
image_output = put_image(image)

def main():
    put_table([image_output])

if __main__ == '__main__':
    start_server(main,port=9090)

after python3 server.py it shows nothing as the program got stuck.

The error message when I ctrl^C is image

seems it's trying to acquire a lock.

and it can be fixed if I declare the image_output inside the main loop as follows

import ...

def main():
    image = open('image/url/')
    image_output = put_image(image)
    put_table([image_output])

if __main__ == '__main__':
    start_server(main,port=9090)

环境信息


With this bug, I'm wondering how could I implement my original design? I decided to declare theimage_output as a global variable, which the button callback will update after a click.

image_output = put_image(original_image)

def callback:
    image_output.reset(put_image(new_image))

def main:
    put_table([put_buttons(['button'], onclick=callback), image_output])

if __main__ == '__main__':
    start_server(main,port=9000)

Is there any alternative approaching?

wang0618 commented 3 years ago

In server mode, the functions in input, output and session modules can only be called in the context of task functions. So you can't declare the put_image outside the main task function. [Doc]

Here are the methods to implement your original design:

Way 1, using clear in use_scope() to clear original content of scope

def update_img():
    with use_scope('img', clear=True):
        put_image('https://raw.githubusercontent.com/wang0618/PyWebIO/dev/docs/assets/demo.gif')

def main():
    with use_scope('img'):
        put_image('https://www.python.org/static/img/python-logo.png')

    put_buttons(['Update image'], onclick=[update_img])

    hold()

if __name__ == '__main__':
    start_server(main, port=9000)

If the image is in table or other combined output widgets, you can use output() :

def main():
    img = output(put_image('https://www.python.org/static/img/python-logo.png'))
    put_table([
        ['Image'],
        [img]
    ])

    url = 'https://raw.githubusercontent.com/wang0618/PyWebIO/dev/docs/assets/demo.gif'
    put_buttons(['Update image'], onclick=[lambda :img.reset(put_image(url))])

    hold()

if __name__ == '__main__':
    start_server(main, port=9000)
lanyusea commented 3 years ago

thanks for the clarification!

lanyusea commented 3 years ago

@wang0618
one more problem: I found there is an obvious loading procedure(original image turn white first and then back to the new image) when changing image source from one to another.

I think changing the css from "hidden" to "visiable" may be better but it seems the style object cannot be updated after creation. Is there any way to do this?

wang0618 commented 3 years ago

one more problem: I found there is an obvious loading procedure(original image turn white first and then back to the new image) when changing image source from one to another.

You can use the html preload mechanism to preloading the image resources. Add this to the beginning of your task function: put_html('<link rel="preload" href="the-url-of-image.jpg" as="image">')