import gradio as gr import matplotlib matplotlib.use('Agg') # Set backend before importing pyplot import matplotlib.pyplot as plt from matplotlib_venn import venn2, venn3 import io from PIL import Image import warnings warnings.filterwarnings('ignore') def create_venn_diagram_2(left_label, right_label, left_only, right_only, both, title, export_format): """Create a 2-circle Venn diagram""" left_label = left_label or "Set A" right_label = right_label or "Set B" # Parse terms terms_data = [ [t.strip() for t in left_only.replace('\n', ',').split(',') if t.strip()], [t.strip() for t in right_only.replace('\n', ',').split(',') if t.strip()], [t.strip() for t in both.replace('\n', ',').split(',') if t.strip()] ] # Calculate figure size based on content max_items = max(len(t) for t in terms_data) base_height = 8 height = max(base_height, base_height + (max_items - 3) * 0.5) fig, ax = plt.subplots(figsize=(12, height)) v = venn2(subsets=(1, 1, 1), set_labels=(left_label, right_label), ax=ax) colors = ['#ff9999', '#99ccff', '#ffcccc'] if v.patches: for i, patch in enumerate(v.patches): if patch: patch.set_alpha(0.4) if i < len(colors): patch.set_color(colors[i]) if v.set_labels: for text in v.set_labels: if text: text.set_fontsize(16) text.set_fontweight('bold') # Add custom text with bullets to each region if v.subset_labels: for i, (text_obj, items) in enumerate(zip(v.subset_labels, terms_data)): if text_obj and items: # Format with bullet points formatted_items = [f"• {item}" for item in items[:10]] if len(items) > 10: formatted_items.append(f"• +{len(items)-10} more") formatted_text = '\n'.join(formatted_items) text_obj.set_text(formatted_text) text_obj.set_fontsize(8) text_obj.set_verticalalignment('center') text_obj.set_horizontalalignment('center') text_obj.set_linespacing(1.5) elif text_obj: text_obj.set_text('') plt.title(title or "Venn Diagram (2 Circles)", fontsize=18, fontweight='bold', pad=20) if export_format == "PNG": buf = io.BytesIO() plt.savefig(buf, format='png', dpi=300, bbox_inches='tight', facecolor='white') buf.seek(0) img = Image.open(buf) plt.close(fig) return img else: svg_path = "/tmp/venn_diagram.svg" plt.savefig(svg_path, format='svg', bbox_inches='tight', facecolor='white') preview_buf = io.BytesIO() plt.savefig(preview_buf, format='png', dpi=150, bbox_inches='tight', facecolor='white') preview_buf.seek(0) img = Image.open(preview_buf) plt.close(fig) return img def create_venn_diagram_3(left_label, middle_label, right_label, left_only, middle_only, right_only, left_middle, left_right, middle_right, all_three, title, export_format): """Create a 3-circle Venn diagram""" left_label = left_label or "Set A" middle_label = middle_label or "Set B" right_label = right_label or "Set C" # Parse all terms terms_data = [ [t.strip() for t in left_only.replace('\n', ',').split(',') if t.strip()], [t.strip() for t in middle_only.replace('\n', ',').split(',') if t.strip()], [t.strip() for t in left_middle.replace('\n', ',').split(',') if t.strip()], [t.strip() for t in right_only.replace('\n', ',').split(',') if t.strip()], [t.strip() for t in left_right.replace('\n', ',').split(',') if t.strip()], [t.strip() for t in middle_right.replace('\n', ',').split(',') if t.strip()], [t.strip() for t in all_three.replace('\n', ',').split(',') if t.strip()] ] # Calculate figure size based on content max_items = max(len(t) for t in terms_data) base_height = 10 height = max(base_height, base_height + (max_items - 3) * 0.6) fig, ax = plt.subplots(figsize=(14, height)) v = venn3(subsets=(1, 1, 1, 1, 1, 1, 1), set_labels=(left_label, middle_label, right_label), ax=ax) colors = ['#ff9999', '#99ccff', '#99ff99', '#ffcccc', '#ccddff', '#ccffcc', '#ffffcc'] if v.patches: for i, patch in enumerate(v.patches): if patch: patch.set_alpha(0.4) if i < len(colors): patch.set_color(colors[i]) if v.set_labels: for text in v.set_labels: if text: text.set_fontsize(16) text.set_fontweight('bold') # Add custom text with bullets to each region if v.subset_labels: for i, (text_obj, items) in enumerate(zip(v.subset_labels, terms_data)): if text_obj and items: # Format with bullet points formatted_items = [f"• {item}" for item in items[:8]] if len(items) > 8: formatted_items.append(f"• +{len(items)-8} more") formatted_text = '\n'.join(formatted_items) text_obj.set_text(formatted_text) text_obj.set_fontsize(8) text_obj.set_fontfamily('monospace') text_obj.set_horizontalalignment('left') elif text_obj: text_obj.set_text('') plt.title(title or "Venn Diagram (3 Circles)", fontsize=18, fontweight='bold', pad=20) if export_format == "PNG": buf = io.BytesIO() plt.savefig(buf, format='png', dpi=300, bbox_inches='tight', facecolor='white') buf.seek(0) img = Image.open(buf) plt.close(fig) return img else: svg_path = "/tmp/venn_diagram.svg" plt.savefig(svg_path, format='svg', bbox_inches='tight', facecolor='white') preview_buf = io.BytesIO() plt.savefig(preview_buf, format='png', dpi=150, bbox_inches='tight', facecolor='white') preview_buf.seek(0) img = Image.open(preview_buf) plt.close(fig) return img # Create Gradio interface with gr.Blocks(title="Venn Diagram Generator") as demo: gr.Markdown("# 🔵 Venn Diagram Generator") gr.Markdown("Create a Venn diagram with 2 or 3 circles, custom labels, and fill each section with terms.") with gr.Row(): with gr.Column(): mode = gr.Radio( choices=["2 Circles", "3 Circles"], value="3 Circles", label="Diagram Mode" ) gr.Markdown("### Circle Labels") left_input = gr.Textbox(label="Left Circle Label", placeholder="e.g., Fruits", value="Set A") middle_input = gr.Textbox(label="Middle Circle Label", placeholder="e.g., Sweet", value="Set B", visible=True) right_input = gr.Textbox(label="Right Circle Label", placeholder="e.g., Red", value="Set C") title_input = gr.Textbox(label="Diagram Title (optional)", placeholder="e.g., Comparison of Food Items", value="") gr.Markdown("### Section Contents") gr.Markdown("*Enter terms separated by commas or new lines*") # 2-circle inputs two_circle_group = gr.Column(visible=False) with two_circle_group: with gr.Row(): left_only_2 = gr.Textbox( label="Left Only", placeholder="Items only in left", lines=4 ) right_only_2 = gr.Textbox( label="Right Only", placeholder="Items only in right", lines=4 ) both_2 = gr.Textbox( label="Both (Left ∩ Right)", placeholder="Items in both circles", lines=4 ) # 3-circle inputs three_circle_group = gr.Column(visible=True) with three_circle_group: with gr.Row(): left_only_input = gr.Textbox( label="Left Only", placeholder="Items only in left circle", lines=3 ) middle_only_input = gr.Textbox( label="Middle Only", placeholder="Items only in middle circle", lines=3 ) right_only_input = gr.Textbox( label="Right Only", placeholder="Items only in right circle", lines=3 ) with gr.Row(): left_middle_input = gr.Textbox( label="Left ∩ Middle", placeholder="Items in left AND middle", lines=3 ) left_right_input = gr.Textbox( label="Left ∩ Right", placeholder="Items in left AND right", lines=3 ) middle_right_input = gr.Textbox( label="Middle ∩ Right", placeholder="Items in middle AND right", lines=3 ) all_three_input = gr.Textbox( label="All Three (Left ∩ Middle ∩ Right)", placeholder="Items in all three circles", lines=3 ) format_dropdown = gr.Dropdown( choices=["PNG", "SVG"], value="PNG", label="Export Format" ) generate_btn = gr.Button("Generate Venn Diagram", variant="primary") with gr.Column(): output_image = gr.Image(label="Venn Diagram", type="pil", format="png") download_btn = gr.File(label="Download File") # Toggle visibility based on mode def update_visibility(mode_choice): is_three = mode_choice == "3 Circles" return { middle_input: gr.update(visible=is_three), two_circle_group: gr.update(visible=not is_three), three_circle_group: gr.update(visible=is_three) } mode.change( fn=update_visibility, inputs=[mode], outputs=[middle_input, two_circle_group, three_circle_group] ) # Event handler def generate_and_export(mode_choice, left, middle, right, title, # 2-circle left_only_2, right_only_2, both_2, # 3-circle left_only, middle_only, right_only, left_middle, left_right, middle_right, all_three, fmt): try: if mode_choice == "2 Circles": img = create_venn_diagram_2( left, right, left_only_2, right_only_2, both_2, title, fmt ) else: img = create_venn_diagram_3( left, middle, right, left_only, middle_only, right_only, left_middle, left_right, middle_right, all_three, title, fmt ) # Save file for download with correct extension if fmt == "PNG": path = "/tmp/venn_diagram.png" img.save(path, format='PNG') else: path = "/tmp/venn_diagram.svg" return img, gr.File(value=path, visible=True) except Exception as e: print(f"Error generating diagram: {e}") raise generate_btn.click( fn=generate_and_export, inputs=[ mode, left_input, middle_input, right_input, title_input, left_only_2, right_only_2, both_2, left_only_input, middle_only_input, right_only_input, left_middle_input, left_right_input, middle_right_input, all_three_input, format_dropdown ], outputs=[output_image, download_btn] ) gr.Markdown(""" ### Instructions: 1. Choose 2 or 3 circle mode 2. Enter labels for the circles 3. Fill in terms for each section (separate with commas or new lines) 4. Choose your export format (PNG or SVG) 5. Click "Generate Venn Diagram" 6. Download the file using the download button ### Examples: **2 Circles:** - Left: "Programming", Right: "Math" - Left Only: "HTML, CSS" - Both: "Algorithms, Data Structures" **3 Circles:** - Left: "Fruits", Middle: "Sweet", Right: "Red" - Left Only: "Lime, Lemon" - All Three: "Watermelon, Raspberry" """) if __name__ == "__main__": demo.launch(server_name="0.0.0.0", server_port=7860)