MelanoScopeAI / src /ui /components.py
dcavadia
update examples
bf08742
Raw
History Blame Contribute Delete
5.88 kB
"""UI components for MelanoScope AI."""
import os
import logging
from typing import List
import gradio as gr
from ..config.settings import UIConfig
from ..core.utils import create_empty_dataframe
from .styles import get_custom_css, create_theme, get_header_html, get_footer_html, get_model_info_html
logger = logging.getLogger(__name__)
class MelanoScopeUI:
"""Handles UI components and layout."""
def __init__(self, model_instance, classes: List[str]):
self.model = model_instance
self.classes = classes
self.theme = create_theme()
self.css = get_custom_css()
logger.info("UI initialized")
def create_interface(self) -> gr.Blocks:
"""Create complete Gradio interface."""
with gr.Blocks(theme=self.theme, css=self.css) as interface:
self._create_header()
with gr.Row(equal_height=True):
self._create_input_column()
self._create_results_column()
self._create_footer()
self._setup_event_handlers()
return interface
def _create_header(self) -> None:
"""Create header section."""
with gr.Row():
with gr.Column(scale=6):
gr.Markdown(get_header_html())
with gr.Column(scale=1, min_width=UIConfig.THEME_TOGGLE_MIN_WIDTH):
try:
self.dark_toggle = gr.ThemeMode(label="Theme", value="system")
except Exception:
gr.Markdown("")
def _create_input_column(self) -> None:
"""Create input column with image upload and controls."""
with gr.Column(scale=UIConfig.LEFT_COLUMN_SCALE):
self.image_input = gr.Image(
type="numpy",
label="Lesion Image",
height=UIConfig.IMAGE_HEIGHT,
sources=["upload", "clipboard"]
)
with gr.Row():
self.analyze_btn = gr.Button("Analyze", variant="primary")
self.clear_btn = gr.Button("Clear")
self._create_examples_section()
self.latency_output = gr.Label(label="Inference Time")
def _create_examples_section(self) -> None:
"""Create examples section if files exist."""
example_files = [
"melanoma.jpg", "vascular_lesion.jpg"
]
existing_examples = [f for f in example_files if os.path.exists(f)]
if existing_examples:
gr.Examples(
examples=existing_examples,
inputs=self.image_input,
label="Quick Examples",
cache_examples=False
)
def _create_results_column(self) -> None:
"""Create results column with predictions and info."""
with gr.Column(scale=UIConfig.RIGHT_COLUMN_SCALE):
self._create_prediction_results()
self._create_information_tabs()
def _create_prediction_results(self) -> None:
"""Create prediction results section."""
with gr.Group():
with gr.Row():
self.prediction_output = gr.Label(label="Primary Prediction", elem_classes=["pred-card"])
self.confidence_output = gr.Label(label="Confidence")
self.probability_plot = gr.BarPlot(
value=create_empty_dataframe(self.classes),
x="item", y="probability",
title="Probability Distribution (Top-k)",
x_title="Class", y_title="Probability",
vertical=False, tooltip=["item", "probability"],
width=UIConfig.PLOT_WIDTH, height=UIConfig.PLOT_HEIGHT,
)
def _create_information_tabs(self) -> None:
"""Create information tabs."""
with gr.Tabs():
with gr.TabItem("Medical Details"):
self._create_medical_details()
with gr.TabItem("About Model"):
gr.Markdown(get_model_info_html())
def _create_medical_details(self) -> None:
"""Create medical details accordions."""
with gr.Accordion("Description", open=True):
self.description_output = gr.Textbox(lines=UIConfig.TEXTBOX_LINES, interactive=False)
with gr.Accordion("Symptoms", open=False):
self.symptoms_output = gr.Textbox(lines=UIConfig.TEXTBOX_LINES, interactive=False)
with gr.Accordion("Causes", open=False):
self.causes_output = gr.Textbox(lines=UIConfig.TEXTBOX_LINES, interactive=False)
with gr.Accordion("Treatment", open=False):
self.treatment_output = gr.Textbox(lines=UIConfig.TEXTBOX_LINES, interactive=False)
def _create_footer(self) -> None:
"""Create footer section."""
gr.Markdown(get_footer_html())
def _setup_event_handlers(self) -> None:
"""Set up event handlers."""
outputs = [
self.prediction_output, self.confidence_output, self.description_output,
self.symptoms_output, self.causes_output, self.treatment_output,
self.probability_plot, self.latency_output
]
self.analyze_btn.click(
fn=self.model.predict, inputs=[self.image_input],
outputs=outputs, show_progress="full"
)
self.clear_btn.click(
fn=self._clear_all, inputs=[],
outputs=[self.image_input] + outputs
)
def _clear_all(self) -> tuple:
"""Clear all inputs and outputs."""
empty_df = create_empty_dataframe(self.classes)
return (None, "", "", "", "", "", "", empty_df, "")