import gradio as gr from functools import partial NUM_LORAS = 5 LORA_LIST = [ {"image": "https://picsum.photos/seed/1/400/300", "title": "Cinematic Style"}, {"image": "https://picsum.photos/seed/2/400/300", "title": "Anime Style"}, {"image": "https://picsum.photos/seed/3/400/300", "title": "Portrait LoRA"}, {"image": "https://picsum.photos/seed/4/400/300", "title": "Landscape LoRA"}, {"image": "https://picsum.photos/seed/5/400/300", "title": "Sci-Fi Style"} ] def update_slider_state(val, state, idx): # state is list of tuples (image_index, scale) new_state = list(state) current_image = new_state[idx][0] new_state[idx] = (current_image, val) return new_state, f"State Updated: {new_state}" def remove_lora(state, idx): new_state = list(state) new_state[idx] = (None, 1.0) # Reset to default # Return updates for: global_state, radio, slider, markdown, delete_btn, output_text return ( new_state, # selected_loras gr.update(value=None), # radio gr.update(visible=False, value=1.0), # slider gr.update(value=""), # markdown gr.update(visible=False), # delete_btn f"State Updated: {new_state}" # output ) def get_selection(evt: gr.SelectData, *args): # args: radios (N) + scales (N) + mds (N) + selected_loras (1) + gallery (1) num_radios = NUM_LORAS radio_vals = args[:num_radios] scale_vals = args[num_radios:num_radios*2] md_vals = args[num_radios*2:num_radios*3] current_state = args[-2] gallery_val = args[-1] selected_val = next((val for val in radio_vals if val is not None), None) # Identify index of selected radio selected_index = -1 for i, val in enumerate(radio_vals): if val is not None: selected_index = i break # Prepare outputs: [output_text] + [md_0_update, ...] + [selected_loras_update] + [scale_0_update, ...] + [del_btn_0_update, ...] + [thumb_0_update, ...] md_updates = [gr.update() for _ in range(num_radios)] scale_updates = [gr.update() for _ in range(num_radios)] del_btn_updates = [gr.update() for _ in range(num_radios)] new_state = current_state selected_image_info = "" if selected_index != -1: # Get LoRA details lora = LORA_LIST[evt.index] lora_name = lora["title"] lora_image = lora["image"] # Update specific markdown with image and name selected_image_info = f"Selected LoRA: {lora_name}" markdown_content = f"\n\n**{lora_name}**" md_updates[selected_index] = gr.update(value=markdown_content) scale_updates[selected_index] = gr.update(visible=True) del_btn_updates[selected_index] = gr.update(visible=True) # Update state for this column: (image_index, scale) new_state = list(current_state) new_state[selected_index] = (evt.index, scale_vals[selected_index]) return [f"Selected Radio: {selected_val}. {selected_image_info}. State: {new_state}"] + md_updates + [new_state] + scale_updates + del_btn_updates def greet(*args): # args will contain: radio_0_val, radio_1_val, ..., radio_N-1_val, name, intensity radio_vals = args[:NUM_LORAS] name = args[-2] intensity = args[-1] selected = next((val for val in radio_vals if val is not None), None) return "Hola, qué tal " + name + "!" * int(intensity) + f". Selected: {selected}" css = """ .disabled { pointer-events: none; opacity: 0.5; } """ with gr.Blocks(css=css) as demo: selected_loras = gr.State([(None, 1.0)] * NUM_LORAS) radios = [] scales = [] mds = [] delete_btns = [] with gr.Row(): for i in range(NUM_LORAS): with gr.Column(): # Each radio has a single choice which is its column number r = gr.Radio([str(i + 1)], label=f"Option {i + 1}") radios.append(r) md = gr.Markdown("") mds.append(md) lora_scale = gr.Slider(label="LoRA Scale", minimum=0, maximum=3, step=0.1, value=1.0, interactive=True, visible=False) scales.append(lora_scale) del_btn = gr.Button("🗑️", visible=False) delete_btns.append(del_btn) name = gr.Textbox(label="Name") intensity = gr.Slider(label="Intensity", minimum=1, maximum=10, step=1) output = gr.Textbox(label="Output") gallery = gr.Gallery( label="Generated Images", value=[(item["image"], item["title"]) for item in LORA_LIST], columns=5, height="auto", interactive=False, allow_preview=False, elem_classes=["disabled"], elem_id="gallery" ) btn = gr.Button("Submit") btn.click(fn=greet, inputs=radios + [name, intensity], outputs=output) for i, r in enumerate(radios): others = radios[:i] + radios[i+1:] # JS: if val is selected (true), return nulls for all others. # Otherwise return current values (no change). js_code = f"(val, ...args) => val ? args.map(_ => null) : args" r.change(fn=None, inputs=[r] + others, outputs=others, js=js_code) # JS toggle for gallery class js_gallery_toggle = "(...args) => { const gallery = document.getElementById('gallery'); const anySelected = args.some(v => v !== null && v !== ''); if (gallery) { if (anySelected) gallery.classList.remove('disabled'); else gallery.classList.add('disabled'); } }" r.change(fn=None, inputs=radios, outputs=None, js=js_gallery_toggle) # Bind slider changes separately to ensure all inputs/outputs are available for i, scale in enumerate(scales): scale.change(fn=partial(update_slider_state, idx=i), inputs=[scale, selected_loras], outputs=[selected_loras, output]) # Bind delete buttons for i, del_btn in enumerate(delete_btns): del_btn.click( fn=partial(remove_lora, idx=i), inputs=[selected_loras], outputs=[selected_loras, radios[i], scales[i], mds[i], del_btn, output] ) gallery.select(fn=get_selection, inputs=radios + scales + mds + [selected_loras, gallery], outputs=[output] + mds + [selected_loras] + scales + delete_btns) demo.launch()