From 4e9726538c1d37c2580c2686c77876da24b5133e Mon Sep 17 00:00:00 2001 From: Chris Malone Date: Thu, 25 Sep 2025 22:51:41 +1000 Subject: [PATCH] made the queue system use a new html-based renderer --- wgp.py | 497 ++++++++++++++++++++++++++++----------------------------- 1 file changed, 242 insertions(+), 255 deletions(-) diff --git a/wgp.py b/wgp.py index 9331354..71c6ee3 100644 --- a/wgp.py +++ b/wgp.py @@ -338,8 +338,8 @@ def edit_task_in_queue( task_to_edit['params'] = original_params task_to_edit['prompt'] = new_inputs.get('prompt') task_to_edit['length'] = new_inputs.get('video_length') - task_to_edit['steps'] = new_inputs.get('num_inference_steps') - update_task_thumbnails(task_to_edit, original_params) + task_to_edit['steps'] = new_inputs.get('num_inference_steps') + update_task_thumbnails(task_to_edit, original_params) gr.Info(f"Task ID {task_to_edit['id']} has been updated successfully.") state["editing_task_index"] = None @@ -1454,50 +1454,87 @@ def finalize_generation_with_state(current_state): accordion_update = gr.Accordion(open=False) if len(get_gen_info(current_state).get("queue", [])) <= 1 else gr.update() return gallery_update, abort_btn_update, gen_btn_update, add_queue_btn_update, current_gen_col_update, gen_info_update, accordion_update, current_state -def get_queue_table(queue): - data = [] - if len(queue) == 1: - return data +def generate_queue_html(queue): + if len(queue) <= 1: + return "
Queue is empty.
" + table_header = """ + + + + + + + + + + + + + + + + + """ + + table_rows = [] for i, item in enumerate(queue): - if i==0: + if i == 0: continue + + row_index = i - 1 truncated_prompt = (item['prompt'][:97] + '...') if len(item['prompt']) > 100 else item['prompt'] full_prompt = item['prompt'].replace('"', '"') - prompt_cell = f'{truncated_prompt}' - start_img_uri =item.get('start_image_data_base64') - start_img_uri = start_img_uri[0] if start_img_uri !=None else None - start_img_labels =item.get('start_image_labels') - end_img_uri = item.get('end_image_data_base64') - end_img_uri = end_img_uri[0] if end_img_uri !=None else None - end_img_labels =item.get('end_image_labels') - thumbnail_size = "50px" + prompt_cell = f'
{truncated_prompt}
' + + start_img_data = item.get('start_image_data_base64') or [None] + start_img_uri = start_img_data[0] + start_img_labels = item.get('start_image_labels', ['']) + + end_img_data = item.get('end_image_data_base64') or [None] + end_img_uri = end_img_data[0] + end_img_labels = item.get('end_image_labels', ['']) + num_steps = item.get('steps') length = item.get('length') + start_img_md = "" - end_img_md = "" if start_img_uri: - start_img_md = f'
{start_img_labels[0]}{start_img_labels[0]}
' + start_img_md = f'
{start_img_labels[0]}
' + + end_img_md = "" if end_img_uri: - end_img_md = f'
{end_img_labels[0]}{end_img_labels[0]}
' + end_img_md = f'
{end_img_labels[0]}
' + + up_btn = f"""""" + down_btn = f"""""" + edit_btn = f"""""" + remove_btn = f"""""" + row_html = f""" + + + + + + + + + + + + + """ + table_rows.append(row_html) + + table_footer = "
QtyPromptLengthStepsStart/RefEnd
{item.get('repeats', "1")}{prompt_cell}{length}{num_steps}{start_img_md}{end_img_md}{up_btn}{down_btn}{edit_btn}{remove_btn}
" + + return table_header + "".join(table_rows) + table_footer - data.append([item.get('repeats', "1"), - prompt_cell, - length, - num_steps, - start_img_md, - end_img_md, - "↑", - "↓", - "✖" - ]) - return data def update_queue_data(queue, first_time_in_queue =False): update_global_queue_ref(queue) - data = get_queue_table(queue) - - return gr.DataFrame(value=data) + html_content = generate_queue_html(queue) + return gr.HTML(value=html_content) def create_html_progress_bar(percentage=0.0, text="Idle", is_idle=True): @@ -5153,7 +5190,9 @@ def generate_video( src_faces = torch.cat([src_faces, torch.full( (3, current_video_length - src_faces.shape[1], 512, 512 ), -1, dtype = src_faces.dtype, device= src_faces.device) ], dim=1) # Sparse Video to Video - sparse_video_image = get_video_frame(video_guide, aligned_guide_start_frame, return_last_if_missing = True, target_fps = fps, return_PIL = True) if "R" in video_prompt_type else None + sparse_video_image = None + if "R" in video_prompt_type: + sparse_video_image = get_video_frame(video_guide, aligned_guide_start_frame, return_last_if_missing = True, target_fps = fps, return_PIL = True) # Generic Video Preprocessing process_outside_mask = process_map_outside_mask.get(filter_letters(video_prompt_type, "YWX"), None) @@ -6904,63 +6943,42 @@ def download_loras(): return -def handle_celll_selection(state, evt: gr.SelectData): +def handle_queue_action(state, action_string): + if not action_string: + return gr.HTML(), gr.Tabs() + gen = get_gen_info(state) queue = gen.get("queue", []) - - if evt.index is None: - return gr.update(), gr.update(), gr.update(visible=False), gr.update() - row_index, col_index = evt.index - if col_index == 1: + try: + action, row_index_str = action_string.split('_') + row_index = int(row_index_str) + except (IndexError, ValueError): + return gr.HTML(), gr.Tabs() + + if action == "edit": state["editing_task_index"] = row_index task_to_edit_index = row_index + 1 if task_to_edit_index < len(queue): task_data = queue[task_to_edit_index] gr.Info(f"Loading task '{task_data['prompt'][:50]}...' for editing.") - return gr.update(), gr.update(), gr.update(visible=False), gr.Tabs(selected="edit") + return update_queue_data(queue), gr.Tabs(selected="edit") else: gr.Warning("Task index out of bounds.") - return gr.update(), gr.update(), gr.update(visible=False), gr.update() + return update_queue_data(queue), gr.Tabs() - if col_index in [6, 7, 8]: - if col_index == 6: cell_value = "↑" - elif col_index == 7: cell_value = "↓" - elif col_index == 8: cell_value = "✖" - if col_index == 6: - new_df_data = move_up(queue, [row_index]) - return new_df_data, gr.update(), gr.update(visible=False), gr.update() - elif col_index == 7: - new_df_data = move_down(queue, [row_index]) - return new_df_data, gr.update(), gr.update(visible=False), gr.update() - elif col_index == 8: - new_df_data = remove_task(queue, [row_index]) - gen["prompts_max"] = gen.get("prompts_max",0) - 1 - update_status(state) - return new_df_data, gr.update(), gr.update(visible=False), gr.update() - - start_img_col_idx = 4 - end_img_col_idx = 5 - image_data_to_show = None - names = [] - if col_index == start_img_col_idx: - with lock: - row_index += 1 - if row_index < len(queue): - image_data_to_show = queue[row_index].get('start_image_data_base64') - names = queue[row_index].get('start_image_labels') - elif col_index == end_img_col_idx: - with lock: - row_index += 1 - if row_index < len(queue): - image_data_to_show = queue[row_index].get('end_image_data_base64') - names = queue[row_index].get('end_image_labels') - if image_data_to_show: - value = get_modal_image( image_data_to_show[0], names[0]) - return gr.update(), gr.update(value=value), gr.update(visible=True), gr.update() - else: - return gr.update(), gr.update(), gr.update(visible=False), gr.update() + elif action == "up": + return move_up(queue, [row_index]), gr.Tabs() + elif action == "down": + return move_down(queue, [row_index]), gr.Tabs() + elif action == "remove": + new_queue_data = remove_task(queue, [row_index]) + gen["prompts_max"] = gen.get("prompts_max", 0) - 1 + update_status(state) + return new_queue_data, gr.Tabs() + + return update_queue_data(queue), gr.Tabs() def change_model(state, model_choice): if model_choice == None: @@ -7185,6 +7203,40 @@ def init_process_queue_if_any(state): def get_modal_image(image_base64, label): return "
" + label + "
" +def show_modal_image(state, action_string): + if not action_string: + return gr.HTML(), gr.Column(visible=False) + + try: + img_type, row_index_str = action_string.split('_') + row_index = int(row_index_str) + except (ValueError, IndexError): + return gr.HTML(), gr.Column(visible=False) + + gen = get_gen_info(state) + queue = gen.get("queue", []) + task_index = row_index + 1 + + if task_index >= len(queue): + return gr.HTML(), gr.Column(visible=False) + + task_item = queue[task_index] + image_data = None + label_data = None + + if img_type == 'start': + image_data = task_item.get('start_image_data_base64') + label_data = task_item.get('start_image_labels') + elif img_type == 'end': + image_data = task_item.get('end_image_data_base64') + label_data = task_item.get('end_image_labels') + + if not image_data or not label_data: + return gr.HTML(), gr.Column(visible=False) + + html_content = get_modal_image(image_data[0], label_data[0]) + return gr.HTML(value=html_content), gr.Column(visible=True) + def get_prompt_labels(multi_prompts_gen_type, image_outputs = False): new_line_text = "each new line of prompt will be used for a window" if multi_prompts_gen_type != 0 else "each new line of prompt will generate " + ("a new image" if image_outputs else "a new video") return "Prompts (" + new_line_text + ", # lines = comments, ! lines = macros)", "Prompts (" + new_line_text + ", # lines = comments)" @@ -7196,28 +7248,6 @@ def refresh_prompt_labels(multi_prompts_gen_type, image_mode): prompt_label, wizard_prompt_label = get_prompt_labels(multi_prompts_gen_type, image_mode > 0) return gr.update(label=prompt_label), gr.update(label = wizard_prompt_label), gr.update(label=get_image_end_label(multi_prompts_gen_type)) -def show_preview_column_modal(state, column_no): - column_no = int(column_no) - if column_no == -1: - return gr.update(), gr.update(), gr.update() - gen = get_gen_info(state) - queue = gen.get("queue", []) - task = queue[0] - list_uri = [] - names = [] - start_img_uri = task.get('start_image_data_base64') - if start_img_uri != None: - list_uri += start_img_uri - names += task.get('start_image_labels') - end_img_uri = task.get('end_image_data_base64') - if end_img_uri != None: - list_uri += end_img_uri - names += task.get('end_image_labels') - - value = get_modal_image( list_uri[column_no],names[column_no] ) - - return -1, gr.update(value=value), gr.update(visible=True) - def update_video_guide_outpainting(video_guide_outpainting_value, value, pos): if len(video_guide_outpainting_value) <= 1: video_guide_outpainting_list = ["0"] * 4 @@ -7491,11 +7521,11 @@ def generate_video_tab(update_form = False, state_dict = None, ui_defaults = Non with gr.Row(): with gr.Column(): with gr.Column(visible=False, elem_id="image-modal-container") as modal_container: - with gr.Row(elem_id="image-modal-close-button-row"): # + with gr.Row(elem_id="image-modal-close-button-row"): close_modal_button = gr.Button("❌", size="sm", scale=1) - # modal_image_display = gr.Image(label="Full Resolution Image", interactive=False, show_label=False) - modal_image_display = gr.HTML(label="Full Resolution Image") - preview_column_no = gr.Text(visible=False, value=-1, elem_id="preview_column_no") + modal_html_display = gr.HTML() + modal_action_input = gr.Text(elem_id="modal_action_input", visible=False) + modal_action_trigger = gr.Button(elem_id="modal_action_trigger", visible=False) with gr.Row(visible= True): #len(loras)>0) as presets_column: lset_choices = compute_lset_choices(loras_presets) + [(get_new_preset_msg(advanced_ui), "")] with gr.Column(scale=6): @@ -8399,21 +8429,13 @@ def generate_video_tab(update_form = False, state_dict = None, ui_defaults = Non onemorewindow_btn = gr.Button("Extend this Sample Please !", visible = False) abort_btn = gr.Button("Abort", visible = True) with gr.Accordion("Queue Management", open=False) as queue_accordion: - with gr.Row( ): - queue_df = gr.DataFrame( - headers=["Qty","Prompt", "Length","Steps","", "", "", "", ""], - datatype=[ "str","markdown","str", "markdown", "markdown", "markdown", "str", "str", "str"], - column_widths= ["5%", None, "7%", "7%", "10%", "10%", "3%", "3%", "34"], - interactive=False, - col_count=(9, "fixed"), - wrap=True, - value=[], - line_breaks= True, - visible= True, - elem_id="queue_df", - max_height= 1000 - + with gr.Row(): + queue_html = gr.HTML( + value=generate_queue_html(state_dict["gen"]["queue"]), + elem_id="queue_html_container" ) + queue_action_input = gr.Text(elem_id="queue_action_input", visible=False) + queue_action_trigger = gr.Button(elem_id="queue_action_trigger", visible=False) with gr.Row(visible= True): queue_zip_base64_output = gr.Text(visible=False) save_queue_btn = gr.DownloadButton("Save Queue", size="sm") @@ -8471,7 +8493,7 @@ def generate_video_tab(update_form = False, state_dict = None, ui_defaults = Non video_guide_outpainting_checkbox.input(fn=refresh_video_guide_outpainting_row, inputs=[video_guide_outpainting_checkbox, video_guide_outpainting], outputs= [video_guide_outpainting_row,video_guide_outpainting]) show_advanced.change(fn=switch_advanced, inputs=[state, show_advanced, lset_name], outputs=[advanced_row, preset_buttons_rows, refresh_lora_btn, refresh2_row ,lset_name]).then( fn=switch_prompt_type, inputs = [state, wizard_prompt_activated_var, wizard_variables_var, prompt, wizard_prompt, *prompt_vars], outputs = [wizard_prompt_activated_var, wizard_variables_var, prompt, wizard_prompt, prompt_column_advanced, prompt_column_wizard, prompt_column_wizard_vars, *prompt_vars]) - queue_df.select( fn=handle_celll_selection, inputs=state, outputs=[queue_df, modal_image_display, modal_container, main_tabs]) + queue_action_trigger.click(fn=handle_queue_action, inputs=[state, queue_action_input], outputs=[queue_html, main_tabs], show_progress="hidden") gr.on( triggers=[output.change, output.select], fn=select_video, inputs=[state, output], outputs=[last_choice, video_info, video_buttons_row, image_buttons_row, video_postprocessing_tab, audio_remuxing_tab], show_progress="hidden") preview_trigger.change(refresh_preview, inputs= [state], outputs= [preview], show_progress="hidden") PP_MMAudio_setting.change(fn = lambda value : [gr.update(visible = value == 1), gr.update(visible = value == 0)] , inputs = [PP_MMAudio_setting], outputs = [PP_MMAudio_row, PP_custom_audio_row] ) @@ -8506,13 +8528,24 @@ def generate_video_tab(update_form = False, state_dict = None, ui_defaults = Non if tab_id == 'generate': output_trigger.change(refresh_gallery, inputs = [state], - outputs = [output, gen_info, generate_btn, add_to_queue_btn, current_gen_column, current_gen_buttons_row, queue_df, abort_btn, onemorewindow_btn], + outputs = [output, gen_info, generate_btn, add_to_queue_btn, current_gen_column, current_gen_buttons_row, queue_html, abort_btn, onemorewindow_btn], show_progress="hidden" ) - preview_column_no.input(show_preview_column_modal, inputs=[state, preview_column_no], outputs=[preview_column_no, modal_image_display, modal_container]) - abort_btn.click(abort_generation, [state], [ abort_btn] ) #.then(refresh_gallery, inputs = [state, gen_info], outputs = [output, gen_info, queue_df] ) + modal_action_trigger.click( + fn=show_modal_image, + inputs=[state, modal_action_input], + outputs=[modal_html_display, modal_container], + show_progress="hidden" + ) + close_modal_button.click( + fn=lambda: gr.Column(visible=False), + inputs=[], + outputs=[modal_container], + show_progress="hidden" + ) + abort_btn.click(abort_generation, [state], [ abort_btn] ) #.then(refresh_gallery, inputs = [state, gen_info], outputs = [output, gen_info, queue_html] ) onemoresample_btn.click(fn=one_more_sample,inputs=[state], outputs= [state]) onemorewindow_btn.click(fn=one_more_window,inputs=[state], outputs= [state]) @@ -8630,7 +8663,7 @@ def generate_video_tab(update_form = False, state_dict = None, ui_defaults = Non ).then( fn=edit_task_in_queue, inputs=edit_inputs_components + [state], - outputs=[queue_df, main_tabs] + outputs=[queue_html, main_tabs] ) cancel_btn.click( fn=cancel_edit, @@ -8681,7 +8714,7 @@ def generate_video_tab(update_form = False, state_dict = None, ui_defaults = Non outputs= None ).then(fn=process_prompt_and_add_tasks, inputs = [state, model_choice], - outputs=[queue_df, queue_accordion], + outputs=[queue_html, queue_accordion], show_progress="hidden", ).then( fn=update_status, @@ -8697,7 +8730,7 @@ def generate_video_tab(update_form = False, state_dict = None, ui_defaults = Non outputs= None ).then(fn=process_prompt_and_add_tasks, inputs = [state, model_choice], - outputs= [queue_df, queue_accordion], + outputs= [queue_html, queue_accordion], show_progress="hidden", ).then(fn=prepare_generate_video, inputs= [state], @@ -8724,7 +8757,7 @@ def generate_video_tab(update_form = False, state_dict = None, ui_defaults = Non gr.on(triggers=[load_queue_btn.upload, main.load], fn=load_queue_action, inputs=[load_queue_btn, state], - outputs=[queue_df] + outputs=[queue_html] ).then( fn=lambda s: (gr.update(visible=bool(get_gen_info(s).get("queue",[]))), gr.Accordion(open=True)) if bool(get_gen_info(s).get("queue",[])) else (gr.update(visible=False), gr.update()), inputs=[state], @@ -8803,19 +8836,13 @@ def generate_video_tab(update_form = False, state_dict = None, ui_defaults = Non clear_queue_btn.click( fn=clear_queue_action, inputs=[state], - outputs=[queue_df] + outputs=[queue_html] ).then( fn=lambda: (gr.update(visible=False), gr.Accordion(open=False)), inputs=None, outputs=[current_gen_column, queue_accordion] ) - close_modal_button.click( - lambda: gr.update(visible=False), - inputs=[], - outputs=[modal_container] - ) - if tab_id == 'edit': locals_dict = locals() gen_inputs = [locals_dict[k] for k in inputs_names] + [state] + extra_inputs @@ -9488,134 +9515,73 @@ def create_ui(): margin: 0 20px; white-space: nowrap; } - .queue-item { - border: 1px solid #ccc; - padding: 10px; - margin: 5px 0; - border-radius: 5px; + #queue_html_container table { + width: 100%; + border-collapse: collapse; + font-size: 14px; + table-layout: fixed; } - .current { - background: #f8f9fa; - border-left: 4px solid #007bff; + #queue_html_container th { + text-align: left; + padding: 10px 8px; + border-bottom: 2px solid #4a5568; + font-weight: bold; + font-size: 11px; + text-transform: uppercase; + color: #a0aec0; + white-space: nowrap; } - .task-header { - display: flex; - justify-content: space-between; - margin-bottom: 5px; + #queue_html_container td { + padding: 8px; + border-bottom: 1px solid #2d3748; + vertical-align: middle; } - .progress-container { - height: 10px; - background: #e9ecef; - border-radius: 5px; - overflow: hidden; + #queue_html_container tr:hover td { + background-color: rgba(255, 255, 255, 0.04); } - .progress-bar { - height: 100%; - background: #007bff; - transition: width 0.3s ease; - } - .task-details { - display: flex; - justify-content: space-between; - font-size: 0.9em; - color: #6c757d; - margin-top: 5px; - } - .task-prompt { - font-size: 0.8em; - color: #868e96; - margin-top: 5px; + #queue_html_container .prompt-cell { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } - #queue_df th { - pointer-events: none; + #queue_html_container .action-button { + background: none; + border: none; + cursor: pointer; + font-size: 1.3em; + padding: 0; + color: #718096; + transition: color 0.2s; + line-height: 1; + } + #queue_html_container .action-button:hover { + color: #e2e8f0; + } + #queue_html_container .center-align { text-align: center; - vertical-align: middle; - font-size:11px; } - #xqueue_df table { - width: 100%; - overflow: hidden !important; + #queue_html_container .text-left { + text-align: left; } - #xqueue_df::-webkit-scrollbar { - display: none !important; - } - #xqueue_df { - scrollbar-width: none !important; - -ms-overflow-style: none !important; - } - .selection-button { - display: none; - } - .cell-selected { - --ring-color: none; - } - #queue_df th:nth-child(1), - #queue_df td:nth-child(1) { - width: 60px; - text-align: center; - vertical-align: middle; - cursor: default !important; - pointer-events: none; - } - #xqueue_df th:nth-child(2), - #queue_df td:nth-child(2) { - text-align: center; - vertical-align: middle; - white-space: normal; - } - #queue_df td:nth-child(2) { - cursor: default !important; - } - #queue_df th:nth-child(3), - #queue_df td:nth-child(3) { - width: 60px; - text-align: center; - vertical-align: middle; - cursor: default !important; - pointer-events: none; - } - #queue_df th:nth-child(4), - #queue_df td:nth-child(4) { - width: 60px; - text-align: center; - white-space: nowrap; - cursor: default !important; - pointer-events: none; - } - #queue_df th:nth-child(5), #queue_df td:nth-child(7), - #queue_df th:nth-child(6), #queue_df td:nth-child(8) { - width: 60px; - text-align: center; - vertical-align: middle; - } - #queue_df td:nth-child(5) img, - #queue_df td:nth-child(6) img { + #queue_html_container .hover-image img { max-width: 50px; max-height: 50px; object-fit: contain; display: block; margin: auto; - cursor: pointer; } - #queue_df th:nth-child(7), #queue_df td:nth-child(9), - #queue_df th:nth-child(8), #queue_df td:nth-child(10), - #queue_df th:nth-child(9), #queue_df td:nth-child(11) { - width: 20px; - padding: 2px !important; - cursor: pointer; - text-align: center; - font-weight: bold; - vertical-align: middle; - } - #queue_df td:nth-child(5):hover, - #queue_df td:nth-child(6):hover, - #queue_df td:nth-child(7):hover, - #queue_df td:nth-child(8):hover, - #queue_df td:nth-child(9):hover { - background-color: #e0e0e0; + #image-modal-container { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: rgba(0, 0, 0, 0.7); + justify-content: center; + align-items: center; + z-index: 1000; + padding: 20px; + box-sizing: border-box; } #image-modal-container { position: fixed; @@ -9759,18 +9725,39 @@ def create_ui(): js = """ function() { - // Attach function to window object to make it globally accessible - window.sendColIndex = function(index) { - const input= document.querySelector('#preview_column_no textarea'); - if (input) { - input.value = index; - input.dispatchEvent(new Event("input", { bubbles: true })); - input.focus(); - input.blur(); - console.log('Events dispatched for column:', index); - } + window.updateAndTrigger = function(action) { + const hiddenTextbox = document.querySelector('#queue_action_input textarea'); + const hiddenButton = document.querySelector('#queue_action_trigger'); + + if (hiddenTextbox && hiddenButton) { + hiddenTextbox.value = action; + const inputEvent = new Event('input', { bubbles: true }); + hiddenTextbox.dispatchEvent(inputEvent); + hiddenButton.click(); + } else { + console.error("Could not find hidden queue action elements."); + } + }; + console.log('updateAndTrigger function for queue is ready.'); + + window.showImageModal = function(action) { + const hiddenTextbox = document.querySelector('#modal_action_input textarea'); + const hiddenButton = document.querySelector('#modal_action_trigger'); + if (hiddenTextbox && hiddenButton) { + hiddenTextbox.value = action; + hiddenTextbox.dispatchEvent(new Event('input', { bubbles: true })); + hiddenButton.click(); + } else { + console.error("Could not find hidden modal action elements."); + } + }; + + window.closeImageModal = function() { + const modal = document.querySelector('#image-modal-container'); + if (modal) { + modal.style.display = 'none'; + } }; - console.log('sendColIndex function attached to window'); // cancel wheel usage inside image editor const hit = n => n?.id === "img_editor" || n?.classList?.contains("wheel-pass");