| import gradio as gr | |
| from app import demo as app | |
| import os | |
| _docs = {'PropertySheet': {'description': 'A Gradio component that renders a dynamic UI from a Python dataclass instance.', 'members': {'__init__': {'value': {'type': 'typing.Optional[typing.Any][Any, None]', 'default': 'None', 'description': 'The initial dataclass instance to render.'}, 'label': {'type': 'str | None', 'default': 'None', 'description': 'The main label for the component, displayed in the accordion header.'}, 'visible': {'type': 'bool', 'default': 'True', 'description': 'If False, the component will be hidden.'}, 'open': {'type': 'bool', 'default': 'True', 'description': 'If False, the accordion will be collapsed by default.'}, 'elem_id': {'type': 'str | None', 'default': 'None', 'description': 'An optional string that is assigned as the id of this component in the DOM.'}, 'scale': {'type': 'int | None', 'default': 'None', 'description': 'The relative size of the component in its container.'}, 'width': {'type': 'int | str | None', 'default': 'None', 'description': 'The width of the component in pixels.'}, 'height': {'type': 'int | str | None', 'default': 'None', 'description': "The maximum height of the component's content area in pixels before scrolling."}, 'min_width': {'type': 'int | None', 'default': 'None', 'description': 'The minimum width of the component in pixels.'}, 'container': {'type': 'bool', 'default': 'True', 'description': 'If True, wraps the component in a container with a background.'}, 'elem_classes': {'type': 'list[str] | str | None', 'default': 'None', 'description': 'An optional list of strings that are assigned as the classes of this component in the DOM.'}}, 'postprocess': {'value': {'type': 'Any', 'description': None}}, 'preprocess': {'return': {'type': 'Any', 'description': None}, 'value': None}}, 'events': {'change': {'type': None, 'default': None, 'description': ''}, 'input': {'type': None, 'default': None, 'description': ''}, 'expand': {'type': None, 'default': None, 'description': ''}, 'collapse': {'type': None, 'default': None, 'description': ''}}}, '__meta__': {'additional_interfaces': {}, 'user_fn_refs': {'PropertySheet': []}}} | |
| abs_path = os.path.join(os.path.dirname(__file__), "css.css") | |
| with gr.Blocks( | |
| css=abs_path, | |
| theme=gr.themes.Default( | |
| font_mono=[ | |
| gr.themes.GoogleFont("Inconsolata"), | |
| "monospace", | |
| ], | |
| ), | |
| ) as demo: | |
| gr.Markdown( | |
| """ | |
| # `gradio_propertysheet` | |
| <div style="display: flex; gap: 7px;"> | |
| <img alt="Static Badge" src="https://img.shields.io/badge/version%20-%200.0.1%20-%20orange"> | |
| </div> | |
| Property sheet | |
| """, elem_classes=["md-custom"], header_links=True) | |
| app.render() | |
| gr.Markdown( | |
| """ | |
| ## Installation | |
| ```bash | |
| pip install gradio_propertysheet | |
| ``` | |
| ## Usage | |
| ```python | |
| import gradio as gr | |
| from dataclasses import dataclass, field, asdict | |
| from typing import Literal | |
| from gradio_propertysheet import PropertySheet | |
| # --- Main Configuration Dataclasses for the "Render Settings" Sheet --- | |
| @dataclass | |
| class ModelSettings: | |
| \"\"\"Settings for loading models, VAEs, etc.\"\"\" | |
| model_type: Literal["SD 1.5", "SDXL", "Pony", "Custom"] = field( | |
| default="SDXL", | |
| metadata={"component": "dropdown", "label": "Base Model"} | |
| ) | |
| custom_model_path: str = field( | |
| default="/path/to/default.safetensors", | |
| metadata={"label": "Custom Model Path", "interactive_if": {"field": "model_type", "value": "Custom"}} | |
| ) | |
| vae_path: str = field( | |
| default="", | |
| metadata={"label": "VAE Path (optional)"} | |
| ) | |
| @dataclass | |
| class SamplingSettings: | |
| \"\"\"Settings for the image sampling process.\"\"\" | |
| sampler_name: Literal["Euler", "Euler a", "DPM++ 2M Karras", "UniPC"] = field( | |
| default="DPM++ 2M Karras", | |
| metadata={"component": "dropdown", "label": "Sampler", "help": "The algorithm for the diffusion process."} | |
| ) | |
| steps: int = field( | |
| default=25, | |
| metadata={"component": "slider", "minimum": 1, "maximum": 150, "step": 1, "label": "Sampling Steps", "help": "More steps can improve quality."} | |
| ) | |
| cfg_scale: float = field( | |
| default=7.0, | |
| metadata={"component": "slider", "minimum": 1.0, "maximum": 30.0, "step": 0.5, "label": "CFG Scale", "help": "How strongly the prompt is adhered to."} | |
| ) | |
| @dataclass | |
| class ImageSettings: | |
| \"\"\"Settings for image dimensions.\"\"\" | |
| width: int = field( | |
| default=1024, | |
| metadata={"component": "slider", "minimum": 512, "maximum": 2048, "step": 64, "label": "Image Width"} | |
| ) | |
| height: int = field( | |
| default=1024, | |
| metadata={"component": "slider", "minimum": 512, "maximum": 2048, "step": 64, "label": "Image Height"} | |
| ) | |
| @dataclass | |
| class PostprocessingSettings: | |
| \"\"\"Settings for image post-processing effects.\"\"\" | |
| restore_faces: bool = field( | |
| default=True, | |
| metadata={"label": "Restore Faces", "help": "Use a secondary model to fix distorted faces."} | |
| ) | |
| enable_hr: bool = field( | |
| default=False, | |
| metadata={"label": "Hires. fix", "help": "Enable a second pass at a higher resolution."} | |
| ) | |
| denoising_strength: float = field( | |
| default=0.45, | |
| metadata={"component": "slider", "minimum": 0.0, "maximum": 1.0, "step": 0.01, "label": "Denoising Strength", "interactive_if": {"field": "enable_hr", "value": True}} | |
| ) | |
| @dataclass | |
| class AdvancedSettings: | |
| \"\"\"Advanced and rarely changed settings.\"\"\" | |
| clip_skip: int = field( | |
| default=2, | |
| metadata={"component": "slider", "minimum": 1, "maximum": 12, "step": 1, "label": "CLIP Skip", "help": "Skip final layers of the text encoder."} | |
| ) | |
| noise_schedule: Literal["Default", "Karras", "Exponential"] = field( | |
| default="Karras", | |
| metadata={"component": "dropdown", "label": "Noise Schedule"} | |
| ) | |
| do_not_scale_cond_uncond: bool = field( | |
| default=False, | |
| metadata={"label": "Do not scale cond/uncond"} | |
| ) | |
| s_churn: int = field( | |
| default=1, | |
| metadata={"component": "number_integer", "minimum": 1, "maximum": 12, "label": "S_churn", "help": "S_churn value for generation."} | |
| ) | |
| @dataclass | |
| class ScriptSettings: | |
| \"\"\"Settings for automation scripts like X/Y/Z plots.\"\"\" | |
| script_name: Literal["None", "Prompt matrix", "X/Y/Z plot"] = field( | |
| default="None", | |
| metadata={"component": "dropdown", "label": "Script"} | |
| ) | |
| x_values: str = field( | |
| default="-1, 10, 20", | |
| metadata={"label": "X axis values", "interactive_if": {"field": "script_name", "value": "X/Y/Z plot"}} | |
| ) | |
| y_values: str = field( | |
| default="", | |
| metadata={"label": "Y axis values", "interactive_if": {"field": "script_name", "value": "X/Y/Z plot"}} | |
| ) | |
| @dataclass | |
| class RenderConfig: | |
| \"\"\"Main configuration object for rendering, grouping all settings.\"\"\" | |
| seed: int = field( | |
| default=-1, | |
| metadata={"component": "number_integer", "label": "Seed (-1 for random)", "help": "The random seed for generation."} | |
| ) | |
| batch_size: int = field( | |
| default=1, | |
| metadata={"component": "slider", "minimum": 1, "maximum": 8, "step": 1, "label": "Batch Size"} | |
| ) | |
| # Nested groups | |
| model: ModelSettings = field(default_factory=ModelSettings) | |
| sampling: SamplingSettings = field(default_factory=SamplingSettings) | |
| image: ImageSettings = field(default_factory=ImageSettings) | |
| postprocessing: PostprocessingSettings = field(default_factory=PostprocessingSettings) | |
| scripts: ScriptSettings = field(default_factory=ScriptSettings) | |
| advanced: AdvancedSettings = field(default_factory=AdvancedSettings) | |
| @dataclass | |
| class Lighting: | |
| \"\"\"Lighting settings for the environment.\"\"\" | |
| sun_intensity: float = field(default=1.0, metadata={"component": "slider", "minimum": 0, "maximum": 5, "step": 0.1}) | |
| ambient_occlusion: bool = field(default=True, metadata={"label": "Ambient Occlusion"}) | |
| color: str = field(default="#FFDDBB", metadata={"component": "colorpicker", "label": "Sun Color"}) | |
| @dataclass | |
| class EnvironmentConfig: | |
| \"\"\"Main configuration for the environment.\"\"\" | |
| background: Literal["Sky", "Color", "Image"] = field(default="Sky", metadata={"component": "dropdown"}) | |
| lighting: Lighting = field(default_factory=Lighting) | |
| # --- Initial Instances --- | |
| initial_render_config = RenderConfig() | |
| initial_env_config = EnvironmentConfig() | |
| # --- Gradio Application --- | |
| with gr.Blocks(title="PropertySheet Demo") as demo: | |
| gr.Markdown("# PropertySheet Component Demo") | |
| gr.Markdown("An example of a realistic application layout using the `PropertySheet` component as a sidebar for settings.") | |
| gr.Markdown("<span>💻 <a href='https://github.com/DEVAIEXP/gradio_component_propertysheet'>Component GitHub Code</a></span>") | |
| with gr.Row(): | |
| # Main content area on the left | |
| with gr.Column(scale=3): | |
| #gr.Image(label="Main Viewport", height=500, value=None) | |
| gr.Textbox(label="AI Prompt", lines=3, placeholder="Enter your prompt here...") | |
| gr.Button("Generate", variant="primary") | |
| with gr.Row(): | |
| output_render_json = gr.JSON(label="Live Render State") | |
| output_env_json = gr.JSON(label="Live Environment State") | |
| # Sidebar with Property Sheets on the right | |
| with gr.Column(scale=1): | |
| render_sheet = PropertySheet( | |
| value=initial_render_config, | |
| label="Render Settings", | |
| width=400, | |
| height=550 # Set a fixed height to demonstrate internal scrolling | |
| ) | |
| environment_sheet = PropertySheet( | |
| value=initial_env_config, | |
| label="Environment Settings", | |
| width=400, | |
| open=False # Start collapsed to show the accordion feature | |
| ) | |
| # --- Event Handlers --- | |
| def handle_render_change(updated_config: RenderConfig | None): | |
| \"\"\"Callback to process changes from the Render Settings sheet.\"\"\" | |
| if updated_config is None: | |
| return initial_render_config, asdict(initial_render_config) | |
| # Example of business logic: reset custom path if not in custom mode | |
| if updated_config.model.model_type != "Custom": | |
| updated_config.model.custom_model_path = "/path/to/default.safetensors" | |
| return updated_config, asdict(updated_config) | |
| def handle_env_change(updated_config: EnvironmentConfig | None): | |
| \"\"\"Callback to process changes from the Environment Settings sheet.\"\"\" | |
| if updated_config is None: | |
| return initial_env_config, asdict(initial_env_config) | |
| return updated_config, asdict(updated_config) | |
| render_sheet.change( | |
| fn=handle_render_change, | |
| inputs=[render_sheet], | |
| outputs=[render_sheet, output_render_json] | |
| ) | |
| environment_sheet.change( | |
| fn=handle_env_change, | |
| inputs=[environment_sheet], | |
| outputs=[environment_sheet, output_env_json] | |
| ) | |
| # Load initial state into JSON viewers on app load | |
| demo.load( | |
| fn=lambda: (asdict(initial_render_config), asdict(initial_env_config)), | |
| outputs=[output_render_json, output_env_json] | |
| ) | |
| if __name__ == "__main__": | |
| demo.launch() | |
| ``` | |
| """, elem_classes=["md-custom"], header_links=True) | |
| gr.Markdown(""" | |
| ## `PropertySheet` | |
| ### Initialization | |
| """, elem_classes=["md-custom"], header_links=True) | |
| gr.ParamViewer(value=_docs["PropertySheet"]["members"]["__init__"], linkify=[]) | |
| gr.Markdown("### Events") | |
| gr.ParamViewer(value=_docs["PropertySheet"]["events"], linkify=['Event']) | |
| gr.Markdown(""" | |
| ### User function | |
| The impact on the users predict function varies depending on whether the component is used as an input or output for an event (or both). | |
| - When used as an Input, the component only impacts the input signature of the user function. | |
| - When used as an output, the component only impacts the return signature of the user function. | |
| The code snippet below is accurate in cases where the component is used as both an input and an output. | |
| ```python | |
| def predict( | |
| value: Any | |
| ) -> Any: | |
| return value | |
| ``` | |
| """, elem_classes=["md-custom", "PropertySheet-user-fn"], header_links=True) | |
| demo.load(None, js=r"""function() { | |
| const refs = {}; | |
| const user_fn_refs = { | |
| PropertySheet: [], }; | |
| requestAnimationFrame(() => { | |
| Object.entries(user_fn_refs).forEach(([key, refs]) => { | |
| if (refs.length > 0) { | |
| const el = document.querySelector(`.${key}-user-fn`); | |
| if (!el) return; | |
| refs.forEach(ref => { | |
| el.innerHTML = el.innerHTML.replace( | |
| new RegExp("\\b"+ref+"\\b", "g"), | |
| `<a href="#h-${ref.toLowerCase()}">${ref}</a>` | |
| ); | |
| }) | |
| } | |
| }) | |
| Object.entries(refs).forEach(([key, refs]) => { | |
| if (refs.length > 0) { | |
| const el = document.querySelector(`.${key}`); | |
| if (!el) return; | |
| refs.forEach(ref => { | |
| el.innerHTML = el.innerHTML.replace( | |
| new RegExp("\\b"+ref+"\\b", "g"), | |
| `<a href="#h-${ref.toLowerCase()}">${ref}</a>` | |
| ); | |
| }) | |
| } | |
| }) | |
| }) | |
| } | |
| """) | |
| demo.launch() | |