TexteaInc / funix

Building web apps without manually creating widgets
http://funix.io
MIT License
89 stars 10 forks source link

feat: custom typing & new theme #63

Closed Yazawazi closed 1 year ago

Yazawazi commented 1 year ago
  1. Support custom typing:
from funix import funix, new_funix_type
from funix.widget import slider

# Define
@new_funix_type("slider", slider) # If your widget needs to be configured, you need to pass in the configuration function
class NewSlider(int):
    pass

# No config
@new_funix_type("password")
class NewPassword(str):
    pass

# Use
@funix()
def test(arg_slider: NewSlider(0, 100, 2), arg_password: NewPassword) -> str:
    return f"{arg_slider} and {arg_password}" 
  1. The theme configuration should now be defined in the Python script file, and types is no longer used, you should use widgets:
theme = {
    "widgets": {
        "type_name": "widget_name",
        ("type_name_1", "type_name_2"): "use_the_same_widget_name",
        "type_name_3": some_widget_config_function(args)
    }
}

And in theme, you can define the theme name with key: name, so you can call set_theme without the second parameter (the theme name) and let Funix automatically use the name defined in the theme.

  1. We have added a code editor widget (use Monaco editor), a simple example of which is shown below:
from funix import funix
from funix.widget.builtin import StrCode

@funix()
def test(box: StrCode("python")) -> str:
    return box

# Or...

@funix(
    widgets = {"box": ("code", {"language": "python"})}
)
def test(box: str) -> str:
    return box
  1. We support file uploads, which can be tried with BytesImage, BytesVideo, BytesAudio and BytesFile in builtin, and wrapping them with typing.List to support uploading multiple files, example:
from typing import List
from io import BytesIO
from PIL import Image as PILImage
from funix import funix
from funix.hint import Image
from funix.widget.builtin import BytesImage
import numpy as np

def rgb_image_to_gray_image(image: bytes) -> bytes:
    img = PILImage.open(BytesIO(image))
    width, height = img.width, img.height
    grays = np.zeros((height, width), dtype=np.uint8)
    for y in range(height):
        for x in range(width):
            if img.mode == "RGBA":
                r, g, b, _ = img.getpixel((x, y))
            elif img.mode == "RGB":
                r, g, b = img.getpixel((x, y))
            else:
                raise Exception("Unsupported image mode")
            gray = int(pow((r**2.2 + (1.5*g)**2.2 + (0.6*b)**2.2)/(1**2.2+1.5**2.2+0.6**2.2), 1/2.2))
            grays[y][x] = gray
    file = PILImage.fromarray(grays)
    output = BytesIO()
    file.save(output, format="PNG")
    return output.getvalue()

@funix(
    title="Convert colored images to gray images",
)
def gray_it(image: List[BytesImage]) -> List[Image]:
    return [rgb_image_to_gray_image(img) for img in image]