Spaces:
Sleeping
Sleeping
| import gradio as gr | |
| import matplotlib.pyplot as plt | |
| from Bio import SeqIO | |
| from Bio.Seq import Seq # Though not directly used in final logic, good for context | |
| from Bio.Restriction import RestrictionBatch, AllEnzymes, Analysis | |
| import os # For getting filename | |
| # Ensure matplotlib uses a non-interactive backend for Gradio | |
| import matplotlib | |
| matplotlib.use('Agg') | |
| # Define paths for example files | |
| EXAMPLE_DIR = "eg_files" | |
| EXAMPLE_PLASMID1_PATH = os.path.join(EXAMPLE_DIR, "plasmid1_example.gb") | |
| EXAMPLE_PLASMID2_PATH = os.path.join(EXAMPLE_DIR, "plasmid2_example.gb") | |
| # --- Core BioPython and Plotting Functions --- | |
| def simulate_digest_and_plot_gradio(plasmid_seq_record, enzyme_name, plasmid_label): | |
| """ | |
| Simulates restriction digest and plots a virtual agarose gel. | |
| Uses enzyme.catalyse() for robust fragment generation. | |
| """ | |
| fig, ax = plt.subplots(figsize=(6, 8)) # Adjusted size for better readability | |
| if plasmid_seq_record is None: | |
| ax.text(0.5, 0.5, f"Error: Plasmid data for '{plasmid_label}' is missing.", | |
| ha='center', va='center', wrap=True, color='red') | |
| ax.set_xticks([]); ax.set_yticks([]) | |
| ax.set_title(f"Virtual Gel: {plasmid_label} - Error", fontsize=10) | |
| plt.tight_layout() | |
| return fig | |
| try: | |
| enzyme = AllEnzymes.get(str(enzyme_name)) | |
| if not enzyme: | |
| raise ValueError(f"Enzyme '{enzyme_name}' not found in Biopython's AllEnzymes.") | |
| except Exception as e: | |
| ax.text(0.5, 0.5, f"Error: Could not load enzyme '{enzyme_name}'.\n{e}", | |
| ha='center', va='center', wrap=True, color='red') | |
| ax.set_xticks([]); ax.set_yticks([]) | |
| ax.set_title(f"Virtual Gel: {plasmid_label} - Error", fontsize=10) | |
| plt.tight_layout() | |
| return fig | |
| fragments_seqs = enzyme.catalyse(plasmid_seq_record.seq) | |
| is_uncut = False | |
| if len(fragments_seqs) == 1 and len(fragments_seqs[0]) == len(plasmid_seq_record.seq): | |
| if not enzyme.search(plasmid_seq_record.seq): | |
| is_uncut = True | |
| if is_uncut: | |
| ax.text(0.5, 0.5, f"Enzyme {enzyme_name} does not cut {plasmid_label}", | |
| ha='center', va='center', wrap=True) | |
| ax.set_title(f"Virtual Gel: {plasmid_label} + {enzyme_name} (No Sites)", fontsize=10) | |
| lengths = [len(plasmid_seq_record.seq)] | |
| else: | |
| lengths = sorted([len(f) for f in fragments_seqs], reverse=True) | |
| ax.set_yscale("log") | |
| min_display_size = 10 | |
| plasmid_len_for_scale = max(len(plasmid_seq_record.seq), min_display_size * 10) | |
| max_display_size = max(plasmid_len_for_scale * 1.1, min_display_size * 2) | |
| ax.set_ylim(min_display_size, max_display_size) | |
| band_width = 0.6 | |
| lane_center = 0.5 | |
| if not lengths: | |
| ax.text(0.5, 0.5, "No fragments to display.", ha='center', va='center') | |
| else: | |
| for i, size in enumerate(lengths): | |
| if size < min_display_size: | |
| ax.text(lane_center, min_display_size * 1.1 , f"(+ {len(lengths) - i} small fragments < {min_display_size}bp not shown)", | |
| ha='center', va='top', fontsize=7, color='gray') | |
| break | |
| ax.plot([lane_center - band_width/2, lane_center + band_width/2], [size, size], | |
| linewidth=6, color='royalblue', solid_capstyle='butt') | |
| ax.text(lane_center + band_width/2 + 0.05, size, f"{size} bp", | |
| va='center', ha='left', fontsize=8) | |
| ax.invert_yaxis() | |
| ax.set_title(f"Virtual Gel: {plasmid_label} digested with {enzyme_name}", fontsize=10) | |
| ax.set_ylabel("Fragment Size (bp)", fontsize=9) | |
| ax.set_xlabel("Lane 1", fontsize=9) | |
| ax.set_xticks([]) | |
| ax.tick_params(axis='y', labelsize=8) | |
| well_top_y = ax.get_ylim()[0] | |
| well_line_y = well_top_y * 1.01 | |
| well_depth_y = well_top_y * 0.98 | |
| ax.plot([lane_center - band_width/1.5, lane_center + band_width/1.5], [well_line_y, well_line_y], | |
| linewidth=1.5, color='black') | |
| ax.plot([lane_center - band_width/1.5, lane_center - band_width/1.5], [well_line_y, well_depth_y], | |
| linewidth=1.5, color='black') | |
| ax.plot([lane_center + band_width/1.5, lane_center + band_width/1.5], [well_line_y, well_depth_y], | |
| linewidth=1.5, color='black') | |
| plt.tight_layout(pad=1.5) | |
| return fig | |
| def analyze_plasmids_gradio(file1_path, file2_path, current_plasmid_choice_for_plot): | |
| """ | |
| Analyzes two plasmid files to find unique restriction enzymes and | |
| enzymes that cut both plasmids but with different fragmentation patterns. | |
| Returns status messages, plasmid data, lists of enzyme names, | |
| and an update for the enzyme selection dropdown. | |
| """ | |
| initial_enzyme_dd_update = gr.update(choices=["Analyze plasmids first"], value="Analyze plasmids first", interactive=False) | |
| empty_return_for_error = ["", "", "", None, None, [], [], [], initial_enzyme_dd_update] | |
| # Check if example files exist if paths match example paths | |
| example_file_error_msg = "" | |
| if file1_path == EXAMPLE_PLASMID1_PATH and not os.path.exists(EXAMPLE_PLASMID1_PATH): | |
| example_file_error_msg += f"Example file not found: {EXAMPLE_PLASMID1_PATH}. Please create it in the '{EXAMPLE_DIR}' directory.\n" | |
| if file2_path == EXAMPLE_PLASMID2_PATH and not os.path.exists(EXAMPLE_PLASMID2_PATH): | |
| example_file_error_msg += f"Example file not found: {EXAMPLE_PLASMID2_PATH}. Please create it in the '{EXAMPLE_DIR}' directory.\n" | |
| if example_file_error_msg: | |
| return example_file_error_msg, *empty_return_for_error | |
| if file1_path is None or file2_path is None: | |
| return "Error: Please upload or load both plasmid files.", *empty_return_for_error | |
| try: | |
| def read_plasmid(filepath, filename_for_error): | |
| try: | |
| return SeqIO.read(filepath, "genbank") | |
| except Exception: | |
| try: | |
| return SeqIO.read(filepath, "fasta") | |
| except Exception as e_fasta: | |
| raise ValueError(f"Could not parse '{filename_for_error}'. Ensure it's a valid GenBank or FASTA file. Last error: {e_fasta}") | |
| p1_orig_filename = os.path.basename(file1_path) | |
| p2_orig_filename = os.path.basename(file2_path) | |
| plasmid1_seq_rec = read_plasmid(file1_path, p1_orig_filename) | |
| plasmid2_seq_rec = read_plasmid(file2_path, p2_orig_filename) | |
| except Exception as e: | |
| return str(e), *empty_return_for_error | |
| valid_enzyme_objects = [] | |
| for enz_name in AllEnzymes.elements(): | |
| enzyme_obj = AllEnzymes.get(enz_name) | |
| if enzyme_obj and hasattr(enzyme_obj, 'site') and enzyme_obj.site is not None: | |
| if hasattr(enzyme_obj, 'is_restriction') and enzyme_obj.is_restriction(): | |
| valid_enzyme_objects.append(enzyme_obj) | |
| elif not hasattr(enzyme_obj, 'is_restriction'): | |
| valid_enzyme_objects.append(enzyme_obj) | |
| if not valid_enzyme_objects: | |
| return "Error: Could not load any restriction enzymes from Biopython.", *empty_return_for_error | |
| enzymes_batch = RestrictionBatch(valid_enzyme_objects) | |
| analysis1 = Analysis(enzymes_batch, plasmid1_seq_rec.seq, linear=False) | |
| analysis2 = Analysis(enzymes_batch, plasmid2_seq_rec.seq, linear=False) | |
| enzymes_cutting_p1_obj = set(analysis1.with_sites().keys()) | |
| enzymes_cutting_p2_obj = set(analysis2.with_sites().keys()) | |
| unique_to_1_obj = sorted(list(enzymes_cutting_p1_obj - enzymes_cutting_p2_obj), key=lambda e: str(e)) | |
| unique_to_2_obj = sorted(list(enzymes_cutting_p2_obj - enzymes_cutting_p1_obj), key=lambda e: str(e)) | |
| unique_to_1_names = [str(e) for e in unique_to_1_obj] | |
| unique_to_2_names = [str(e) for e in unique_to_2_obj] | |
| p1_display_label = f"Plasmid 1 ({p1_orig_filename})" | |
| p2_display_label = f"Plasmid 2 ({p2_orig_filename})" | |
| msg1 = f"Enzymes cutting only {p1_display_label} ({len(unique_to_1_names)}):\n" + ", ".join(unique_to_1_names) if unique_to_1_names else f"No unique enzymes found for {p1_display_label}." | |
| msg2 = f"Enzymes cutting only {p2_display_label} ({len(unique_to_2_names)}):\n" + ", ".join(unique_to_2_names) if unique_to_2_names else f"No unique enzymes found for {p2_display_label}." | |
| # New: Find enzymes cutting both but with different fragments | |
| common_enzymes_obj = enzymes_cutting_p1_obj.intersection(enzymes_cutting_p2_obj) | |
| common_diff_fragments_enzymes_names = [] | |
| for enzyme_obj in common_enzymes_obj: | |
| try: | |
| fragments1_seqs = enzyme_obj.catalyse(plasmid1_seq_rec.seq) | |
| fragments2_seqs = enzyme_obj.catalyse(plasmid2_seq_rec.seq) | |
| lengths1 = sorted([len(f) for f in fragments1_seqs]) | |
| lengths2 = sorted([len(f) for f in fragments2_seqs]) | |
| if lengths1 != lengths2: | |
| common_diff_fragments_enzymes_names.append(str(enzyme_obj)) | |
| except Exception as e_cat_common: | |
| print(f"Warning: Error during catalysis comparison for common enzyme {str(enzyme_obj)}: {e_cat_common}") | |
| # Optionally skip this enzyme or log more formally | |
| common_diff_fragments_enzymes_names = sorted(list(set(common_diff_fragments_enzymes_names))) | |
| msg_common_diff = f"Enzymes cutting BOTH plasmids with DIFFERENT fragments ({len(common_diff_fragments_enzymes_names)}):\n" + \ | |
| ", ".join(common_diff_fragments_enzymes_names) if common_diff_fragments_enzymes_names \ | |
| else "No enzymes found that cut both plasmids with different fragment patterns." | |
| status = "Analysis complete." | |
| if not unique_to_1_names and not unique_to_2_names and not common_diff_fragments_enzymes_names: | |
| status += " No differentiating enzymes found." | |
| dd_choices = [] | |
| if current_plasmid_choice_for_plot == "Plasmid 1": | |
| dd_choices = unique_to_1_names if unique_to_1_names else [f"No unique enzymes for {p1_display_label}"] | |
| else: | |
| dd_choices = unique_to_2_names if unique_to_2_names else [f"No unique enzymes for {p2_display_label}"] | |
| if (current_plasmid_choice_for_plot == "Plasmid 1" and unique_to_1_names) or \ | |
| (current_plasmid_choice_for_plot == "Plasmid 2" and unique_to_2_names): | |
| current_dd_update = gr.update(choices=["Select an enzyme"] + dd_choices, value="Select an enzyme", interactive=True) | |
| else: | |
| current_dd_update = gr.update(choices=dd_choices, value=dd_choices[0], interactive=False if not dd_choices or "No unique" in dd_choices[0] else True) | |
| return status, msg1, msg2, msg_common_diff, plasmid1_seq_rec, plasmid2_seq_rec, \ | |
| unique_to_1_names, unique_to_2_names, common_diff_fragments_enzymes_names, current_dd_update | |
| def plot_selected_digest_controller(plasmid_choice_label, enzyme_name, p1_data, p2_data): | |
| """ | |
| Controller to select the correct plasmid data and call the plotting function. | |
| """ | |
| fig_placeholder, ax_placeholder = plt.subplots(figsize=(6, 8)) | |
| ax_placeholder.text(0.5, 0.5, "Plot will appear here.", ha='center', va='center') | |
| ax_placeholder.set_xticks([]); ax_placeholder.set_yticks([]) | |
| plt.tight_layout() | |
| if not enzyme_name or enzyme_name == "Select an enzyme" or "No unique enzymes" in enzyme_name or "Analyze plasmids first" in enzyme_name: | |
| ax_placeholder.clear() | |
| ax_placeholder.text(0.5, 0.5, "Please select a valid plasmid and enzyme after analysis.", ha='center', va='center', wrap=True) | |
| plt.tight_layout() | |
| return fig_placeholder | |
| target_plasmid_rec = None | |
| target_label = "" | |
| if plasmid_choice_label == "Plasmid 1": | |
| if p1_data is None: | |
| ax_placeholder.clear() | |
| ax_placeholder.text(0.5, 0.5, "Plasmid 1 data not loaded. Please re-analyze.", ha='center', va='center', wrap=True, color='red') | |
| plt.tight_layout() | |
| return fig_placeholder | |
| target_plasmid_rec = p1_data | |
| target_label = "Plasmid 1" | |
| if hasattr(p1_data, 'name') and p1_data.name and p1_data.name !="<unknown name>": target_label += f" ({p1_data.name})" | |
| elif hasattr(p1_data, 'id') and p1_data.id and p1_data.id !="<unknown id>": target_label += f" ({p1_data.id})" | |
| elif plasmid_choice_label == "Plasmid 2": | |
| if p2_data is None: | |
| ax_placeholder.clear() | |
| ax_placeholder.text(0.5, 0.5, "Plasmid 2 data not loaded. Please re-analyze.", ha='center', va='center', wrap=True, color='red') | |
| plt.tight_layout() | |
| return fig_placeholder | |
| target_plasmid_rec = p2_data | |
| target_label = "Plasmid 2" | |
| if hasattr(p2_data, 'name') and p2_data.name and p2_data.name !="<unknown name>": target_label += f" ({p2_data.name})" | |
| elif hasattr(p2_data, 'id') and p2_data.id and p2_data.id !="<unknown id>": target_label += f" ({p2_data.id})" | |
| else: | |
| ax_placeholder.clear() | |
| ax_placeholder.text(0.5, 0.5, "Invalid plasmid selection.", ha='center', va='center', wrap=True, color='red') | |
| plt.tight_layout() | |
| return fig_placeholder | |
| return simulate_digest_and_plot_gradio(target_plasmid_rec, enzyme_name, target_label) | |
| def update_enzyme_dropdown_choices_on_radio_change(plasmid_choice_label, p1_enzyme_names, p2_enzyme_names): | |
| if plasmid_choice_label == "Plasmid 1": | |
| choices = p1_enzyme_names if p1_enzyme_names else ["No unique enzymes for P1"] | |
| if p1_enzyme_names: | |
| return gr.update(choices=["Select an enzyme"] + choices, value="Select an enzyme", interactive=True) | |
| return gr.update(choices=choices, value=choices[0], interactive=False) | |
| elif plasmid_choice_label == "Plasmid 2": | |
| choices = p2_enzyme_names if p2_enzyme_names else ["No unique enzymes for P2"] | |
| if p2_enzyme_names: | |
| return gr.update(choices=["Select an enzyme"] + choices, value="Select an enzyme", interactive=True) | |
| return gr.update(choices=choices, value=choices[0], interactive=False) | |
| return gr.update(choices=[], value=None, interactive=False) | |
| def load_examples_and_auto_process(): | |
| """ | |
| Loads example files, triggers analysis, and then attempts to auto-plot. | |
| """ | |
| # Step 1: Perform analysis with example files | |
| status, msg1, msg2, msg_common_diff, p1_rec, p2_rec, p1_enz_names, p2_enz_names, \ | |
| _common_diff_list_ignore, enz_dd_update_from_analysis = \ | |
| analyze_plasmids_gradio(EXAMPLE_PLASMID1_PATH, EXAMPLE_PLASMID2_PATH, "Plasmid 1") | |
| # If analysis failed (e.g., files not found), p1_rec or p2_rec might be None | |
| if p1_rec is None or p2_rec is None : | |
| fig_error, ax_error = plt.subplots(figsize=(6,8)) | |
| ax_error.text(0.5, 0.5, f"Error during example analysis:\n{status}", ha='center', va='center', color='red', wrap=True) # Display actual error | |
| ax_error.set_xticks([]); ax_error.set_yticks([]) | |
| plt.tight_layout() | |
| # Ensure all expected output values are provided | |
| return status, msg1, msg2, msg_common_diff, None, None, [], [], \ | |
| gr.update(choices=["Error"], value="Error", interactive=False), \ | |
| gr.update(value="Plasmid 1"), fig_error | |
| # Step 2: Determine auto-plot parameters and update dropdown based on analysis | |
| auto_plot_plasmid_label = None | |
| auto_plot_enzyme_name = None | |
| auto_plot_plasmid_data = None | |
| final_radio_choice = "Plasmid 1" # Default | |
| final_enz_dd_update = enz_dd_update_from_analysis # Use directly from analysis initially | |
| if p1_enz_names: | |
| auto_plot_plasmid_label = "Plasmid 1" | |
| auto_plot_enzyme_name = p1_enz_names[0] | |
| auto_plot_plasmid_data = p1_rec | |
| final_radio_choice = "Plasmid 1" | |
| final_enz_dd_update = gr.update(choices=["Select an enzyme"] + p1_enz_names, value=auto_plot_enzyme_name, interactive=True) | |
| elif p2_enz_names: | |
| auto_plot_plasmid_label = "Plasmid 2" | |
| auto_plot_enzyme_name = p2_enz_names[0] | |
| auto_plot_plasmid_data = p2_rec | |
| final_radio_choice = "Plasmid 2" | |
| final_enz_dd_update = gr.update(choices=["Select an enzyme"] + p2_enz_names, value=auto_plot_enzyme_name, interactive=True) | |
| else: # No unique enzymes for either, dropdown should reflect current radio choice (P1 default from analyze call) | |
| if final_radio_choice == "Plasmid 1": | |
| final_enz_dd_update = gr.update(choices=[f"No unique enzymes for P1 ({os.path.basename(EXAMPLE_PLASMID1_PATH)})"], | |
| value=f"No unique enzymes for P1 ({os.path.basename(EXAMPLE_PLASMID1_PATH)})", interactive=False) | |
| # No explicit else for P2 needed here as P1 is the default for initial dropdown population | |
| # Step 3: Generate plot if possible | |
| if auto_plot_enzyme_name and auto_plot_plasmid_data: | |
| # Use the actual name from p1_data or p2_data for the label if available | |
| plot_label_detail = "" | |
| if auto_plot_plasmid_label == "Plasmid 1": | |
| if hasattr(p1_rec, 'name') and p1_rec.name and p1_rec.name != "<unknown name>": plot_label_detail = f" ({p1_rec.name})" | |
| elif hasattr(p1_rec, 'id') and p1_rec.id and p1_rec.id != "<unknown id>": plot_label_detail = f" ({p1_rec.id})" | |
| elif auto_plot_plasmid_label == "Plasmid 2": | |
| if hasattr(p2_rec, 'name') and p2_rec.name and p2_rec.name != "<unknown name>": plot_label_detail = f" ({p2_rec.name})" | |
| elif hasattr(p2_rec, 'id') and p2_rec.id and p2_rec.id != "<unknown id>": plot_label_detail = f" ({p2_rec.id})" | |
| gel_fig = simulate_digest_and_plot_gradio(auto_plot_plasmid_data, auto_plot_enzyme_name, f"{auto_plot_plasmid_label}{plot_label_detail}") | |
| else: | |
| fig_placeholder, ax_placeholder = plt.subplots(figsize=(6, 8)) | |
| ax_placeholder.text(0.5, 0.5, "No unique enzymes found for automatic plotting.", ha='center', va='center', wrap=True) | |
| ax_placeholder.set_xticks([]); ax_placeholder.set_yticks([]) | |
| plt.tight_layout() | |
| gel_fig = fig_placeholder | |
| if not p1_enz_names and not p2_enz_names: | |
| final_enz_dd_update = gr.update(choices=["No unique enzymes found"], value="No unique enzymes found", interactive=False) | |
| # Return all updates | |
| return status, msg1, msg2, msg_common_diff, p1_rec, p2_rec, p1_enz_names, p2_enz_names, \ | |
| final_enz_dd_update, gr.update(value=final_radio_choice), gel_fig | |
| # --- Gradio Interface Definition --- | |
| with gr.Blocks(theme=gr.themes.Default()) as demo: | |
| gr.Markdown("# Plasmid Restriction Digest Analyzer & Virtual Gel") | |
| gr.Markdown( | |
| "**Instructions:**\n" | |
| "1. Upload two plasmid sequence files (GenBank `.gb`/`.gbk` or FASTA `.fasta`/`.fna`/`.fa` format) OR click 'Load Example Files'.\n" | |
| "2. If uploading manually, click `Analyze Uploaded Plasmids`. Results will show:\n" | |
| " a. Enzymes that uniquely cut only Plasmid 1.\n" | |
| " b. Enzymes that uniquely cut only Plasmid 2.\n" | |
| " c. Enzymes that cut **both** plasmids but produce different fragment patterns.\n" | |
| "3. Select which plasmid's **unique** enzymes you want to consider for plotting.\n" | |
| "4. Choose a specific enzyme from the dropdown list.\n" | |
| "5. Click `Generate Gel Plot` to visualize the digestion pattern for the selected plasmid and enzyme.\n" | |
| f"Note: For 'Load Example Files', ensure `plasmid1_example.gb` and `plasmid2_example.gb` are in a folder named `{EXAMPLE_DIR}` next to this script." | |
| ) | |
| plasmid1_data_state = gr.State() | |
| plasmid2_data_state = gr.State() | |
| p1_unique_enzymes_list_state = gr.State([]) | |
| p2_unique_enzymes_list_state = gr.State([]) | |
| # common_diff_enzymes_list_state = gr.State([]) # Not strictly needed as state if only displayed | |
| with gr.Row(): | |
| with gr.Column(scale=1): | |
| gr.Markdown("### 1. Upload Plasmids & Analyze") | |
| file_p1 = gr.File(label="Plasmid 1 File (e.g., .gb, .fasta)", type="filepath", file_types=[".gb", ".gbk", ".fasta", ".fna", ".fa"]) | |
| file_p2 = gr.File(label="Plasmid 2 File (e.g., .gb, .fasta)", type="filepath", file_types=[".gb", ".gbk", ".fasta", ".fna", ".fa"]) | |
| _current_plasmid_choice_for_plot_hidden = gr.Textbox(value="Plasmid 1", visible=False) # For analysis logic with dropdown | |
| analyze_btn = gr.Button("Analyze Uploaded Plasmids", variant="secondary") | |
| example_btn = gr.Button("Load Example Files & Auto-Analyze/Plot", variant="primary", elem_id="example_button") | |
| with gr.Column(scale=2): | |
| gr.Markdown("### Analysis Results") | |
| status_message_txt = gr.Textbox(label="Status", interactive=False, lines=1, max_lines=3) | |
| unique_enzymes_p1_txt = gr.Textbox(label="Enzymes cutting only Plasmid 1", interactive=False, lines=3, max_lines=6) | |
| unique_enzymes_p2_txt = gr.Textbox(label="Enzymes cutting only Plasmid 2", interactive=False, lines=3, max_lines=6) | |
| common_diff_enzymes_txt = gr.Textbox(label="Enzymes cutting BOTH plasmids (different fragments)", interactive=False, lines=3, max_lines=6) # New Textbox | |
| gr.Markdown("---") | |
| gr.Markdown("### 2. Visualize Digestion on Virtual Gel") | |
| with gr.Row(): | |
| with gr.Column(scale=1): | |
| plasmid_to_plot_choice_radio = gr.Radio( | |
| choices=["Plasmid 1", "Plasmid 2"], | |
| label="Select Plasmid for Gel Visualization (of its unique enzymes)", | |
| value="Plasmid 1", | |
| interactive=True | |
| ) | |
| enzyme_for_plot_dropdown = gr.Dropdown( | |
| label="Select Unique Enzyme for Chosen Plasmid", | |
| choices=["Analyze plasmids first"], | |
| value="Analyze plasmids first", | |
| interactive=False | |
| ) | |
| plot_btn = gr.Button("Generate Gel Plot for Selection", variant="secondary", elem_id="plot_button") | |
| with gr.Column(scale=2): | |
| gel_plot_output = gr.Plot(label="Virtual Agarose Gel") | |
| gr.Markdown("---") | |
| gr.Markdown("Developed using Biopython, Matplotlib, and Gradio.") | |
| gr.Markdown("Note: Large plasmid files or complex analyses might take a few moments.") | |
| # --- Event Handlers --- | |
| plasmid_to_plot_choice_radio.change( | |
| fn=lambda x: x, | |
| inputs=[plasmid_to_plot_choice_radio], | |
| outputs=[_current_plasmid_choice_for_plot_hidden] # Keep this to inform analyze_plasmids_gradio logic for initial dd | |
| ) | |
| analyze_btn.click( | |
| fn=analyze_plasmids_gradio, | |
| inputs=[file_p1, file_p2, _current_plasmid_choice_for_plot_hidden], # Pass the hidden value | |
| outputs=[ | |
| status_message_txt, unique_enzymes_p1_txt, unique_enzymes_p2_txt, | |
| common_diff_enzymes_txt, # New output for the new textbox | |
| plasmid1_data_state, plasmid2_data_state, | |
| p1_unique_enzymes_list_state, p2_unique_enzymes_list_state, | |
| gr.State(), # Placeholder for common_diff_fragments_enzymes_names list (returned but not stored in state) | |
| enzyme_for_plot_dropdown | |
| ] | |
| ) | |
| example_btn.click( | |
| fn=load_examples_and_auto_process, | |
| inputs=[], | |
| outputs=[ | |
| status_message_txt, unique_enzymes_p1_txt, unique_enzymes_p2_txt, | |
| common_diff_enzymes_txt, # New output | |
| plasmid1_data_state, plasmid2_data_state, | |
| p1_unique_enzymes_list_state, p2_unique_enzymes_list_state, | |
| enzyme_for_plot_dropdown, | |
| plasmid_to_plot_choice_radio, | |
| gel_plot_output | |
| ] | |
| ) | |
| plasmid_to_plot_choice_radio.change( | |
| fn=update_enzyme_dropdown_choices_on_radio_change, | |
| inputs=[plasmid_to_plot_choice_radio, p1_unique_enzymes_list_state, p2_unique_enzymes_list_state], | |
| outputs=[enzyme_for_plot_dropdown] | |
| ) | |
| plot_btn.click( | |
| fn=plot_selected_digest_controller, | |
| inputs=[plasmid_to_plot_choice_radio, enzyme_for_plot_dropdown, plasmid1_data_state, plasmid2_data_state], | |
| outputs=[gel_plot_output] | |
| ) | |
| if __name__ == '__main__': | |
| if not os.path.exists(EXAMPLE_DIR): | |
| os.makedirs(EXAMPLE_DIR) | |
| print(f"Created directory: {EXAMPLE_DIR}. Please add example plasmid files (plasmid1_example.gb, plasmid2_example.gb) to it for the example button to work.") | |
| # Check for example files and print a message if they are missing | |
| if not os.path.exists(EXAMPLE_PLASMID1_PATH) or not os.path.exists(EXAMPLE_PLASMID2_PATH): | |
| print(f"Warning: Example files (plasmid1_example.gb, plasmid2_example.gb) not found in '{EXAMPLE_DIR}'. The 'Load Example Files' button might not work as expected.") | |
| print("You can create dummy GenBank or FASTA files with these names for testing if needed.") | |
| demo.launch() |