Bravura SDK - Best Practices Guide

Back to Documentation Hub

Bravura SDK - Best Practices Guide

Architecture patterns, optimization techniques, and professional development practices

This guide covers the recommended patterns and practices for building production-quality applications with the Bravura SDK. Following these practices ensures maintainable, performant, and professional applications.

---

Table of Contents

  1. Architecture Overview
  2. Application Structure
  3. Component Management
  4. Performance Optimization
  5. Error Handling
  6. Testing Strategies
  7. Deployment Practices
  8. Maintenance Guidelines

---

Architecture Overview

Recommended Application Architecture


class ProfessionalApp:
    """
    Recommended application architecture using Bravura SDK.

    Features:
    - Clean separation of concerns
    - Proper component lifecycle management
    - Comprehensive error handling
    - Performance optimizations
    """

    def __init__(self):
        # 1. Initialize core components
        self.initialize_core()

        # 2. Setup UI framework
        self.initialize_ui()

        # 3. Create application components
        self.initialize_components()

        # 4. Setup event handlers
        self.initialize_handlers()

    def initialize_core(self):
        """Initialize core application components."""
        self.config = self.load_configuration()
        self.logger = self.setup_logging()
        self.worker = self.create_worker_thread()

    def initialize_ui(self):
        """Initialize UI framework and theming."""
        self.root = tk.Tk()
        self.theme_manager = ProfessionalThemeManager(self)
        self.apply_initial_theme()

    def initialize_components(self):
        """Create UI components."""
        self.create_menu_bar()
        self.create_main_interface()
        self.create_status_components()

    def initialize_handlers(self):
        """Setup event handlers and bindings."""
        self.setup_keyboard_shortcuts()
        self.setup_window_handlers()
        self.setup_error_handlers()

Component Lifecycle Management


class ComponentManager:
    """Manages component creation, updates, and cleanup."""

    def __init__(self, app):
        self.app = app
        self.components = {}
        self.active_components = set()

    def create_component(self, name, component_class, *args, **kwargs):
        """Create and register a component."""
        if name in self.components:
            self.destroy_component(name)

        component = component_class(*args, **kwargs)
        self.components[name] = component
        self.active_components.add(name)

        return component

    def get_component(self, name):
        """Get a registered component."""
        return self.components.get(name)

    def destroy_component(self, name):
        """Properly destroy a component."""
        if name in self.components:
            component = self.components[name]
            if hasattr(component, 'destroy'):
                component.destroy()
            elif hasattr(component, 'close'):
                component.close()

            del self.components[name]
            self.active_components.discard(name)

    def cleanup_all(self):
        """Cleanup all components on application exit."""
        for name in list(self.active_components):
            self.destroy_component(name)

---

Application Structure

File Organization


professional_app/
├── main.py                 # Application entry point
├── config.py              # Configuration management
├── ui/
│   ├── __init__.py
│   ├── main_window.py     # Main window class
│   ├── components/        # UI components
│   │   ├── __init__.py
│   │   ├── file_panel.py
│   │   ├── progress_panel.py
│   │   └── status_bar.py
│   └── themes/            # Custom themes
│       └── corporate_theme.py
├── core/
│   ├── __init__.py
│   ├── processor.py       # Business logic
│   ├── worker.py          # Background processing
│   └── validators.py      # Input validation
├── utils/
│   ├── __init__.py
│   ├── logging_utils.py
│   └── file_utils.py
└── tests/
    ├── __init__.py
    ├── test_main.py
    └── test_processor.py

Main Application Entry Point


# main.py
import sys
import traceback
from ui.main_window import MainWindow
from config import Config

def main():
    """Application entry point with comprehensive error handling."""
    try:
        # Load configuration
        config = Config()

        # Create and run application
        app = MainWindow(config)
        app.run()

    except KeyboardInterrupt:
        print("\nApplication interrupted by user")
        sys.exit(0)

    except Exception as e:
        print(f"Fatal error: {e}")
        traceback.print_exc()
        sys.exit(1)

if __name__ == "__main__":
    main()

Configuration Management


# config.py
import json
import os
from pathlib import Path
from typing import Dict, Any

class Config:
    """Centralized configuration management."""

    DEFAULT_CONFIG = {
        "theme": {
            "default_theme": "wigley_site",
            "auto_switch": False
        },
        "window": {
            "width": 1200,
            "height": 800,
            "remember_position": True,
            "stay_on_top": False
        },
        "performance": {
            "gpu_acceleration": True,
            "animation_speed": 1.0,
            "max_threads": 4
        },
        "logging": {
            "level": "INFO",
            "max_file_size": 10 * 1024 * 1024,  # 10MB
            "backup_count": 5
        }
    }

    def __init__(self, config_file: str = "app_config.json"):
        self.config_file = Path(config_file)
        self.data = self.load()

    def load(self) -> Dict[str, Any]:
        """Load configuration from file."""
        if self.config_file.exists():
            try:
                with open(self.config_file, 'r') as f:
                    return {**self.DEFAULT_CONFIG, **json.load(f)}
            except (json.JSONDecodeError, IOError) as e:
                print(f"Warning: Could not load config: {e}")
                return self.DEFAULT_CONFIG.copy()
        else:
            return self.DEFAULT_CONFIG.copy()

    def save(self):
        """Save configuration to file."""
        try:
            self.config_file.parent.mkdir(parents=True, exist_ok=True)
            with open(self.config_file, 'w') as f:
                json.dump(self.data, f, indent=2)
        except IOError as e:
            print(f"Warning: Could not save config: {e}")

    def get(self, key: str, default=None):
        """Get configuration value."""
        keys = key.split('.')
        value = self.data

        for k in keys:
            if isinstance(value, dict) and k in value:
                value = value[k]
            else:
                return default

        return value

    def set(self, key: str, value: Any):
        """Set configuration value."""
        keys = key.split('.')
        config = self.data

        for k in keys[:-1]:
            if k not in config or not isinstance(config[k], dict):
                config[k] = {}
            config = config[k]

        config[keys[-1]] = value
        self.save()

---

Component Management

Premium Component Usage Patterns


class FileProcessingPanel(ttk.Frame):
    """Professional file processing panel using Bravura components."""

    def __init__(self, parent, theme_manager, **kwargs):
        super().__init__(parent, **kwargs)

        self.theme_manager = theme_manager
        self.worker = None

        self.create_ui()
        self.setup_bindings()

    def create_ui(self):
        """Create the user interface."""
        # File selection section
        self.create_file_section()

        # Processing controls
        self.create_control_section()

        # Progress section
        self.create_progress_section()

        # Results section
        self.create_results_section()

    def create_file_section(self):
        """Create file selection interface."""
        file_frame = ttk.LabelFrame(self, text="File Selection", padding=10)
        file_frame.pack(fill="x", pady=(0, 10))

        # File path entry with browse button
        ttk.Label(file_frame, text="Input File:").grid(row=0, column=0, sticky="w")

        self.file_path = tk.StringVar()
        entry = ttk.Entry(file_frame, textvariable=self.file_path, width=50)
        entry.grid(row=0, column=1, sticky="ew", padx=(5, 0))

        # Browse button
        from bravura.components import CompactPremiumButton
        CompactPremiumButton(
            file_frame,
            text="Browse...",
            command=self.browse_file,
            width=10
        ).grid(row=0, column=2, padx=(5, 0))

        file_frame.grid_columnconfigure(1, weight=1)

    def create_control_section(self):
        """Create processing controls."""
        control_frame = ttk.Frame(self)
        control_frame.pack(fill="x", pady=(0, 10))

        # Primary action button
        from bravura.components import PremiumButton
        self.process_btn = PremiumButton(
            control_frame,
            text="🚀 Process File",
            command=self.start_processing,
            style="primary",
            width=18,
            theme_tokens=self.theme_manager.get_current_theme_colors()
        )
        self.process_btn.pack(side="left", padx=(0, 10))

        # Cancel button
        self.cancel_btn = PremiumButton(
            control_frame,
            text="Cancel",
            command=self.cancel_processing,
            style="secondary",
            width=12,
            theme_tokens=self.theme_manager.get_current_theme_colors()
        )
        self.cancel_btn.pack(side="left")

    def create_progress_section(self):
        """Create progress display."""
        progress_frame = ttk.LabelFrame(self, text="Progress", padding=10)
        progress_frame.pack(fill="x", pady=(0, 10))

        # Progress bar
        from bravura.components import GlowingProgressBar
        self.progress_bar = GlowingProgressBar(
            progress_frame,
            label_text="Processing:",
            bar_type="rainbow",
            width=400
        )
        self.progress_bar.pack(fill="x")

        # Status label
        self.status_label = ttk.Label(progress_frame, text="Ready")
        self.status_label.pack(anchor="w", pady=(5, 0))

    def create_results_section(self):
        """Create results display."""
        results_frame = ttk.LabelFrame(self, text="Results", padding=10)
        results_frame.pack(fill="both", expand=True)

        # Results text area
        self.results_text = tk.Text(
            results_frame,
            height=10,
            wrap="word",
            font=("Consolas", 10)
        )
        scrollbar = ttk.Scrollbar(results_frame, command=self.results_text.yview)
        self.results_text.configure(yscrollcommand=scrollbar.set)

        self.results_text.pack(side="left", fill="both", expand=True)
        scrollbar.pack(side="right", fill="y")

        # Style the text widget
        self.apply_text_styling()

    def apply_text_styling(self):
        """Apply theme colors to text widget."""
        colors = self.theme_manager.get_current_theme_colors()
        self.results_text.configure(
            bg=colors.get("background_secondary", "#2a2a2a"),
            fg=colors.get("text_main", "#ffffff"),
            insertbackground=colors.get("text_main", "#ffffff"),
            selectbackground=colors.get("accent_color_1", "#007acc")
        )

    def browse_file(self):
        """Browse for input file."""
        from tkinter import filedialog
        filename = filedialog.askopenfilename(
            title="Select file to process",
            filetypes=[("All files", "*.*")]
        )
        if filename:
            self.file_path.set(filename)

    def start_processing(self):
        """Start file processing."""
        file_path = self.file_path.get()
        if not file_path:
            messagebox.showwarning("No File", "Please select a file first.")
            return

        # Disable buttons
        self.process_btn.set_loading(True, "Processing...")
        self.cancel_btn.set_enabled(False)

        # Start processing in background
        self.start_background_processing(file_path)

    def start_background_processing(self, file_path):
        """Start background processing task."""
        from bravura.core.worker import Worker

        def processing_task(emit, cancel):
            """Background processing task."""
            try:
                # Simulate processing steps
                total_steps = 100

                for step in range(total_steps):
                    if cancel.is_set():
                        emit("CANCELLED")
                        return

                    # Do processing work here
                    import time
                    time.sleep(0.1)  # Simulate work

                    # Update progress
                    emit("PROGRESS", step=step, total=total_steps,
                         message=f"Processing step {step + 1}/{total_steps}")

                emit("COMPLETED", result="Processing successful!")

            except Exception as e:
                emit("ERROR", error=str(e))

        # Create and start worker
        self.worker = Worker()
        self.worker.run(processing_task, on_message=self.on_worker_message)

    def on_worker_message(self, message_type, **kwargs):
        """Handle messages from background worker."""
        if message_type == "PROGRESS":
            step = kwargs.get("step", 0)
            total = kwargs.get("total", 100)
            message = kwargs.get("message", "")

            # Update progress bar
            progress = (step + 1) / total * 100
            self.progress_bar.set_value(progress)
            self.status_label.config(text=message)

        elif message_type == "COMPLETED":
            result = kwargs.get("result", "")
            self.processing_completed(result)

        elif message_type == "ERROR":
            error = kwargs.get("error", "Unknown error")
            self.processing_error(error)

        elif message_type == "CANCELLED":
            self.processing_cancelled()

    def processing_completed(self, result):
        """Handle successful processing completion."""
        self.reset_ui_state()
        self.results_text.insert("end", f"✓ {result}\n")
        self.results_text.see("end")

    def processing_error(self, error):
        """Handle processing error."""
        self.reset_ui_state()
        self.results_text.insert("end", f"✗ Error: {error}\n")
        self.results_text.see("end")
        messagebox.showerror("Processing Error", str(error))

    def processing_cancelled(self):
        """Handle processing cancellation."""
        self.reset_ui_state()
        self.results_text.insert("end", "⚠ Processing cancelled by user\n")
        self.results_text.see("end")

    def cancel_processing(self):
        """Cancel current processing."""
        if self.worker and self.worker.is_running():
            self.worker.cancel()
            self.cancel_btn.config(text="Cancelling...")

    def reset_ui_state(self):
        """Reset UI to ready state."""
        self.process_btn.set_loading(False)
        self.cancel_btn.set_enabled(True)
        self.cancel_btn.config(text="Cancel")
        self.progress_bar.set_value(0)
        self.status_label.config(text="Ready")

    def setup_bindings(self):
        """Setup keyboard shortcuts and event bindings."""
        # Ctrl+O to browse file
        self.bind("<Control-o>", lambda e: self.browse_file())

        # Ctrl+Enter to start processing
        self.bind("<Control-Return>", lambda e: self.start_processing())

        # Escape to cancel
        self.bind("<Escape>", lambda e: self.cancel_processing())

Background Processing Patterns


class BackgroundProcessor:
    """Professional background processing with proper error handling."""

    def __init__(self, app):
        self.app = app
        self.worker = None
        self.cancel_event = threading.Event()

    def process_file(self, file_path, options=None):
        """Process a file in the background."""
        if self.worker and self.worker.is_running():
            raise RuntimeError("Processing already in progress")

        self.cancel_event.clear()
        options = options or {}

        def processing_task(emit, cancel):
            """Background processing task."""
            try:
                # Validate inputs
                if not os.path.exists(file_path):
                    emit("ERROR", error=f"File not found: {file_path}")
                    return

                # Start processing
                emit("STARTED", message=f"Processing {os.path.basename(file_path)}")

                # Process file (replace with actual logic)
                result = self._process_file_content(file_path, options, emit, cancel)

                if not cancel.is_set():
                    emit("COMPLETED", result=result)

            except Exception as e:
                emit("ERROR", error=str(e))
                self.app.logger.error(f"Processing failed: {e}", exc_info=True)

        self.worker = Worker()
        self.worker.run(processing_task, on_message=self.app.on_processing_message)

    def _process_file_content(self, file_path, options, emit, cancel):
        """Process file content with progress updates."""
        # Get file size for progress calculation
        file_size = os.path.getsize(file_path)

        # Process file in chunks
        processed = 0
        chunk_size = options.get("chunk_size", 8192)

        with open(file_path, 'rb') as f:
            while not cancel.is_set():
                chunk = f.read(chunk_size)
                if not chunk:
                    break

                # Process chunk (replace with actual processing)
                processed_chunk = self._process_chunk(chunk, options)

                processed += len(chunk)

                # Update progress
                progress = (processed / file_size) * 100
                emit("PROGRESS",
                     progress=progress,
                     message=f"Processed {processed:,} of {file_size:,} bytes")

        return {"processed_bytes": processed, "file_size": file_size}

    def _process_chunk(self, chunk, options):
        """Process a chunk of data."""
        # Replace with actual processing logic
        import time
        time.sleep(0.001)  # Simulate processing time
        return chunk

    def cancel(self):
        """Cancel current processing."""
        if self.worker:
            self.worker.cancel()
            self.cancel_event.set()

    def is_processing(self):
        """Check if processing is in progress."""
        return self.worker and self.worker.is_running()

---

Performance Optimization

Memory Management


class MemoryManager:
    """Memory usage optimization for long-running applications."""

    def __init__(self, app):
        self.app = app
        self.gc_threshold = 1000  # Objects before GC
        self.memory_checks = []

    def optimize_memory_usage(self):
        """Perform memory optimization."""
        import gc
        import psutil

        # Force garbage collection
        collected = gc.collect()

        # Get memory usage
        process = psutil.Process()
        memory_mb = process.memory_info().rss / 1024 / 1024

        self.memory_checks.append({
            'timestamp': time.time(),
            'memory_mb': memory_mb,
            'objects_collected': collected
        })

        # Keep only last 100 checks
        if len(self.memory_checks) > 100:
            self.memory_checks = self.memory_checks[-100:]

        return memory_mb

    def get_memory_trend(self):
        """Get memory usage trend."""
        if len(self.memory_checks) < 2:
            return "insufficient_data"

        recent = self.memory_checks[-10:]
        avg_recent = sum(m['memory_mb'] for m in recent) / len(recent)
        avg_overall = sum(m['memory_mb'] for m in self.memory_checks) / len(self.memory_checks)

        if avg_recent > avg_overall * 1.2:
            return "increasing"
        elif avg_recent < avg_overall * 0.8:
            return "decreasing"
        else:
            return "stable"

UI Responsiveness

Ambient Background Performance

The AmbientBackground component demonstrates excellent UI responsiveness practices:


# Ambient backgrounds run at smooth 60 FPS with minimal CPU impact
from bravura.components import AmbientBackground

ambient_bg = AmbientBackground(
    root,
    theme_tokens=theme_manager.get_current_theme_colors(),
    enable_animation=True,
    particle_count=30,  # Smooth even with 30-40 particles
    animation_speed="medium"  # Fixed 60 FPS regardless of speed
)

# Performance characteristics:
# - 60 FPS animation (16.67ms frame time)
# - <2% CPU usage on modern systems
# - Harmonic easing for organic movement
# - No UI blocking or stuttering

Key Performance Features:

Ambient Background Theme Updates

Best Practice: Use set_theme_tokens() for Theme Changes


class ProfessionalApp:
    def __init__(self):
        self.root = tk.Tk()
        self.theme_manager = ProfessionalThemeManager(self)
        self.theme_manager.apply_theme("wigley_site")

        # Create ambient background
        self.ambient_bg = AmbientBackground(
            self.root,
            theme_tokens=self.theme_manager.get_current_theme_colors(),
            enable_animation=True,
            particle_count=30
        )

    def change_theme(self, theme_name):
        """Change theme with optimized ambient background update."""
        # Apply new theme
        self.theme_manager.apply_theme(theme_name)

        # Update ambient background instantly (NEW in v1.0.0)
        if self.ambient_bg:
            self.ambient_bg.set_theme_tokens(
                self.theme_manager.get_current_theme_colors()
            )

        # Update other UI components as needed
        self.update_ui_colors()

Performance Comparison:

Method Time Visual Quality Memory
`set_theme_tokens()` ~10ms Smooth, no flicker No allocation
Recreation (old way) ~100ms Visual "pop" New allocation

When to Use Each Method:

Use set_theme_tokens() (Recommended):

⚠️ Use Recreation (Only When Needed):

General UI Updates


class UIUpdater:
    """Ensures UI updates don't block the main thread."""

    def __init__(self, root):
        self.root = root
        self.update_queue = queue.Queue()
        self.running = True

        # Start update processing thread
        self.update_thread = threading.Thread(target=self._process_updates, daemon=True)
        self.update_thread.start()

    def schedule_update(self, callback, *args, **kwargs):
        """Schedule a UI update to run on main thread."""
        self.update_queue.put((callback, args, kwargs))

    def _process_updates(self):
        """Process UI updates on main thread."""
        while self.running:
            try:
                callback, args, kwargs = self.update_queue.get(timeout=0.1)

                # Schedule callback on main thread
                self.root.after(0, callback, *args, **kwargs)

            except queue.Empty:
                continue
            except Exception as e:
                print(f"UI update error: {e}")

    def shutdown(self):
        """Shutdown the UI updater."""
        self.running = False
        if self.update_thread.is_alive():
            self.update_thread.join(timeout=1.0)

Component Caching


class ComponentCache:
    """Cache frequently used UI components."""

    def __init__(self):
        self.cache = {}
        self.access_times = {}

    def get_component(self, key, factory_func, *args, **kwargs):
        """Get cached component or create new one."""
        if key not in self.cache:
            self.cache[key] = factory_func(*args, **kwargs)

        self.access_times[key] = time.time()
        return self.cache[key]

    def cleanup_old_components(self, max_age_seconds=300):
        """Remove components that haven't been accessed recently."""
        current_time = time.time()
        keys_to_remove = []

        for key, access_time in self.access_times.items():
            if current_time - access_time > max_age_seconds:
                keys_to_remove.append(key)

        for key in keys_to_remove:
            if key in self.cache:
                component = self.cache[key]
                if hasattr(component, 'destroy'):
                    component.destroy()
                del self.cache[key]
                del self.access_times[key]

    def clear_cache(self):
        """Clear all cached components."""
        for component in self.cache.values():
            if hasattr(component, 'destroy'):
                component.destroy()

        self.cache.clear()
        self.access_times.clear()

---

Error Handling

Comprehensive Error Handling


class ErrorHandler:
    """Centralized error handling for the application."""

    def __init__(self, app):
        self.app = app
        self.error_log = []
        self.setup_global_exception_handling()

    def setup_global_exception_handling(self):
        """Setup global exception handlers."""

        def handle_exception(exc_type, exc_value, exc_traceback):
            """Handle uncaught exceptions."""
            error_info = {
                'type': exc_type.__name__,
                'message': str(exc_value),
                'traceback': traceback.format_exception(exc_type, exc_value, exc_traceback),
                'timestamp': time.time()
            }

            self.error_log.append(error_info)

            # Log the error
            self.app.logger.error(
                f"Uncaught exception: {error_info['message']}",
                exc_info=(exc_type, exc_value, exc_traceback)
            )

            # Show user-friendly error dialog
            self.show_error_dialog(error_info)

        # Set global exception handler
        sys.excepthook = handle_exception

        # Handle Tkinter errors
        def tkinter_error_handler(error):
            self.handle_tkinter_error(error)

        # Override Tkinter's error reporting
        tk.Tk.report_callback_exception = tkinter_error_handler

    def handle_error(self, error, context=""):
        """Handle an application error."""
        error_info = {
            'error': str(error),
            'context': context,
            'timestamp': time.time(),
            'traceback': traceback.format_exc()
        }

        self.error_log.append(error_info)

        # Log error
        self.app.logger.error(f"Error in {context}: {error}", exc_info=True)

        # Determine error severity
        if self._is_critical_error(error):
            self.handle_critical_error(error_info)
        else:
            self.handle_recoverable_error(error_info)

    def _is_critical_error(self, error):
        """Determine if an error is critical."""
        critical_patterns = [
            "out of memory",
            "disk full",
            "permission denied",
            "connection failed"
        ]

        error_str = str(error).lower()
        return any(pattern in error_str for pattern in critical_patterns)

    def handle_critical_error(self, error_info):
        """Handle critical application errors."""
        message = (
            "A critical error has occurred. The application may be in an "
            "unstable state.\n\n"
            f"Error: {error_info['error']}\n\n"
            "Please save your work and restart the application."
        )

        messagebox.showerror("Critical Error", message)

        # Offer to save error report
        if messagebox.askyesno("Save Error Report",
                              "Would you like to save an error report?"):
            self.save_error_report(error_info)

    def handle_recoverable_error(self, error_info):
        """Handle recoverable application errors."""
        message = f"An error occurred: {error_info['error']}"

        if error_info['context']:
            message += f"\n\nContext: {error_info['context']}"

        messagebox.showwarning("Error", message)

    def handle_tkinter_error(self, error):
        """Handle Tkinter-specific errors."""
        # Extract useful information from Tkinter error
        error_str = str(error)

        # Log Tkinter error
        self.app.logger.error(f"Tkinter error: {error_str}")

        # Some Tkinter errors can be ignored (like callback errors during shutdown)
        if "application has been destroyed" in error_str.lower():
            return

        # Show error dialog for serious Tkinter errors
        messagebox.showerror("Interface Error",
                           f"A user interface error occurred:\n\n{error_str}")

    def show_error_dialog(self, error_info):
        """Show error dialog for uncaught exceptions."""
        message = (
            "An unexpected error has occurred. The application will continue "
            "running, but some features may not work correctly.\n\n"
            f"Error: {error_info['message']}\n\n"
            "Please report this error if it persists."
        )

        result = messagebox.askyesno("Unexpected Error", message + "\n\nShow details?")

        if result:
            # Show detailed error information
            details = "\n".join(error_info['traceback'])
            self.show_error_details(error_info['message'], details)

    def show_error_details(self, message, details):
        """Show detailed error information."""
        dialog = tk.Toplevel()
        dialog.title("Error Details")
        dialog.geometry("600x400")

        # Error message
        ttk.Label(dialog, text="Error:", font=("Arial", 10, "bold")).pack(anchor="w", padx=10, pady=5)
        msg_label = ttk.Label(dialog, text=message, wraplength=580)
        msg_label.pack(anchor="w", padx=10)

        # Details
        ttk.Label(dialog, text="Details:", font=("Arial", 10, "bold")).pack(anchor="w", padx=10, pady=(20, 5))

        # Scrollable text area for traceback
        text_frame = ttk.Frame(dialog)
        text_frame.pack(fill="both", expand=True, padx=10, pady=(0, 10))

        text_widget = tk.Text(text_frame, wrap="word", font=("Consolas", 9))
        scrollbar = ttk.Scrollbar(text_frame, command=text_widget.yview)
        text_widget.configure(yscrollcommand=scrollbar.set)

        text_widget.insert("1.0", details)
        text_widget.config(state="disabled")

        text_widget.pack(side="left", fill="both", expand=True)
        scrollbar.pack(side="right", fill="y")

        # Close button
        ttk.Button(dialog, text="Close", command=dialog.destroy).pack(pady=10)

    def save_error_report(self, error_info):
        """Save error report to file."""
        from datetime import datetime

        timestamp = datetime.fromtimestamp(error_info['timestamp']).strftime("%Y%m%d_%H%M%S")
        filename = f"error_report_{timestamp}.txt"

        try:
            with open(filename, 'w') as f:
                f.write("Error Report\n")
                f.write("=" * 50 + "\n\n")
                f.write(f"Timestamp: {datetime.fromtimestamp(error_info['timestamp'])}\n")
                f.write(f"Error Type: {error_info.get('type', 'Unknown')}\n")
                f.write(f"Message: {error_info['message']}\n\n")

                if 'context' in error_info:
                    f.write(f"Context: {error_info['context']}\n\n")

                f.write("Traceback:\n")
                f.write("-" * 30 + "\n")
                if 'traceback' in error_info:
                    if isinstance(error_info['traceback'], list):
                        f.write("".join(error_info['traceback']))
                    else:
                        f.write(error_info['traceback'])

            messagebox.showinfo("Report Saved",
                              f"Error report saved as: {filename}")

        except Exception as e:
            messagebox.showerror("Save Failed",
                               f"Could not save error report: {e}")

    def get_error_summary(self):
        """Get summary of recent errors."""
        recent_errors = [e for e in self.error_log
                        if time.time() - e['timestamp'] < 3600]  # Last hour

        return {
            'total_errors': len(self.error_log),
            'recent_errors': len(recent_errors),
            'last_error': self.error_log[-1] if self.error_log else None
        }

---

Testing Strategies

Unit Testing Framework


# tests/test_main.py
import unittest
import tkinter as tk
from unittest.mock import Mock, patch
from main_window import MainWindow

class TestMainWindow(unittest.TestCase):
    """Test cases for main application window."""

    def setUp(self):
        """Set up test fixtures."""
        self.root = tk.Tk()
        self.app = MainWindow(self.root)

    def tearDown(self):
        """Clean up after tests."""
        if self.root:
            self.root.destroy()

    def test_initialization(self):
        """Test that application initializes correctly."""
        self.assertIsNotNone(self.app.theme_manager)
        self.assertIsNotNone(self.app.worker)
        self.assertEqual(self.app.title, "Professional App")

    def test_theme_application(self):
        """Test theme application."""
        initial_theme = self.app.theme_manager.get_current_theme_name()

        self.app.theme_manager.apply_theme("dark")
        self.assertEqual(self.app.theme_manager.get_current_theme_name(), "dark")

        # Restore original theme
        self.app.theme_manager.apply_theme(initial_theme)

    @patch('main_window.messagebox.showinfo')
    def test_file_processing(self, mock_messagebox):
        """Test file processing functionality."""
        # Mock file selection
        with patch('tkinter.filedialog.askopenfilename', return_value='/test/file.txt'):
            self.app.browse_file()
            self.assertEqual(self.app.file_path.get(), '/test/file.txt')

    def test_error_handling(self):
        """Test error handling."""
        with self.assertRaises(ValueError):
            self.app.process_invalid_input()

    def test_ui_components(self):
        """Test that UI components are created."""
        self.assertIsNotNone(self.app.process_btn)
        self.assertIsNotNone(self.app.progress_bar)
        self.assertIsNotNone(self.app.status_label)

Integration Testing


# tests/test_integration.py
import unittest
import time
from main_window import MainWindow

class TestIntegration(unittest.TestCase):
    """Integration tests for complete workflows."""

    def setUp(self):
        """Set up integration test environment."""
        self.app = MainWindow()
        self.app.root.withdraw()  # Hide window during testing

    def tearDown(self):
        """Clean up after integration tests."""
        if self.app.root:
            self.app.root.destroy()

    def test_complete_processing_workflow(self):
        """Test complete file processing workflow."""
        # Setup test file
        test_file = self.create_test_file("test_data.txt", "sample content")

        try:
            # Set file path
            self.app.file_path.set(test_file)

            # Start processing
            self.app.start_processing()

            # Wait for completion (with timeout)
            timeout = 30
            start_time = time.time()

            while self.app.worker.is_running() and (time.time() - start_time) < timeout:
                time.sleep(0.1)
                self.app.root.update()

            # Verify completion
            self.assertFalse(self.app.worker.is_running())
            self.assertIn("completed", self.app.status_label.cget("text").lower())

        finally:
            # Cleanup
            os.remove(test_file)

    def test_error_recovery(self):
        """Test error recovery and user feedback."""
        # Set invalid file path
        self.app.file_path.set("/nonexistent/file.txt")

        # Attempt processing
        self.app.start_processing()

        # Wait for error handling
        time.sleep(1)
        self.app.root.update()

        # Verify error handling
        self.assertIn("error", self.app.status_label.cget("text").lower())

    def create_test_file(self, filename, content):
        """Create a temporary test file."""
        with open(filename, 'w') as f:
            f.write(content)
        return filename

Performance Testing


# tests/test_performance.py
import unittest
import time
import psutil
from main_window import MainWindow

class TestPerformance(unittest.TestCase):
    """Performance tests for application."""

    def setUp(self):
        """Set up performance test."""
        self.app = MainWindow()
        self.app.root.withdraw()

    def tearDown(self):
        """Clean up after performance tests."""
        if self.app.root:
            self.app.root.destroy()

    def test_startup_time(self):
        """Test application startup time."""
        start_time = time.time()

        # Create new instance (simulating startup)
        test_app = MainWindow()
        test_app.root.withdraw()

        startup_time = time.time() - start_time

        # Should start up in under 2 seconds
        self.assertLess(startup_time, 2.0,
                       f"Startup took {startup_time:.2f}s, expected < 2.0s")

        test_app.root.destroy()

    def test_memory_usage(self):
        """Test memory usage during operation."""
        process = psutil.Process()

        initial_memory = process.memory_info().rss / 1024 / 1024  # MB

        # Perform some operations
        self.app.theme_manager.apply_theme("dark")
        self.app.theme_manager.apply_theme("light")
        self.app.theme_manager.apply_theme("wigley_site")

        final_memory = process.memory_info().rss / 1024 / 1024  # MB

        memory_increase = final_memory - initial_memory

        # Memory increase should be reasonable (< 50MB)
        self.assertLess(memory_increase, 50.0,
                       f"Memory increased by {memory_increase:.1f}MB, expected < 50MB")

    def test_ui_responsiveness(self):
        """Test UI responsiveness during operations."""
        # Start a background operation
        self.app.start_processing()

        # Measure UI response time
        start_time = time.time()

        # Perform UI operations
        self.app.root.update()
        self.app.status_label.config(text="Test update")

        response_time = time.time() - start_time

        # UI should respond within 100ms
        self.assertLess(response_time, 0.1,
                       f"UI response took {response_time:.3f}s, expected < 0.1s")

---

Deployment Practices

Build Configuration


# build_config.py
import os
from pathlib import Path

class BuildConfig:
    """Configuration for application builds."""

    # Application metadata
    APP_NAME = "Professional App"
    APP_VERSION = "1.0.0"
    AUTHOR = "Your Company"
    DESCRIPTION = "Professional desktop application built with Bravura SDK"

    # Build settings
    BUILD_DIR = Path("build")
    DIST_DIR = Path("dist")

    # Include files
    INCLUDE_FILES = [
        "gui_main.py",
        "glowing_progress_bar.py",
        "components/",
        "themes/",
        "config.json"
    ]

    # Exclude patterns
    EXCLUDE_PATTERNS = [
        "*.pyc",
        "__pycache__",
        "*.log",
        ".git/",
        "tests/",
        "docs/"
    ]

    # PyInstaller settings
    PYINSTALLER_CONFIG = {
        "name": APP_NAME,
        "version": APP_VERSION,
        "author": AUTHOR,
        "description": DESCRIPTION,
        "console": False,  # Hide console window
        "windowed": True,  # Create GUI app
        "onefile": True,   # Single executable
        "clean": True,     # Clean cache
        "noupx": False,    # Use UPX compression
    }

    @classmethod
    def get_includes(cls):
        """Get list of files to include in build."""
        includes = []

        for pattern in cls.INCLUDE_FILES:
            if os.path.isfile(pattern):
                includes.append(pattern)
            elif os.path.isdir(pattern):
                for root, dirs, files in os.walk(pattern):
                    for file in files:
                        if not any(file.endswith(ext) for ext in ['.pyc', '.pyo']):
                            includes.append(os.path.join(root, file))

        return includes

    @classmethod
    def validate_build(cls):
        """Validate build configuration."""
        errors = []

        # Check required files exist
        for file in cls.INCLUDE_FILES:
            if os.path.exists(file):
                continue
            elif file.endswith('/'):  # Directory
                if not os.path.isdir(file.rstrip('/')):
                    errors.append(f"Required directory not found: {file}")
            else:
                errors.append(f"Required file not found: {file}")

        # Check for conflicting settings
        if cls.PYINSTALLER_CONFIG["onefile"] and cls.PYINSTALLER_CONFIG["onedir"]:
            errors.append("Cannot use both 'onefile' and 'onedir' PyInstaller options")

        return errors

Automated Build Script


# build.py
#!/usr/bin/env python3
"""
Automated build script for Professional App.
"""

import os
import sys
import shutil
import subprocess
from pathlib import Path
from build_config import BuildConfig

def clean_build():
    """Clean previous build artifacts."""
    print("🧹 Cleaning previous build...")

    dirs_to_clean = [BuildConfig.BUILD_DIR, BuildConfig.DIST_DIR]

    for dir_path in dirs_to_clean:
        if dir_path.exists():
            shutil.rmtree(dir_path)
            print(f"  Removed {dir_path}")

def create_build_environment():
    """Create build environment."""
    print("🏗️ Creating build environment...")

    BuildConfig.BUILD_DIR.mkdir(parents=True, exist_ok=True)
    BuildConfig.DIST_DIR.mkdir(parents=True, exist_ok=True)

    # Validate configuration
    errors = BuildConfig.validate_build()
    if errors:
        print("❌ Build configuration errors:")
        for error in errors:
            print(f"  {error}")
        sys.exit(1)

    print("  Build environment ready")

def install_dependencies():
    """Install build dependencies."""
    print("📦 Installing dependencies...")

    try:
        subprocess.check_call([
            sys.executable, "-m", "pip", "install",
            "--upgrade", "pip", "setuptools", "wheel"
        ])

        # Install PyInstaller if not present
        subprocess.check_call([
            sys.executable, "-m", "pip", "install", "PyInstaller"
        ])

        print("  Dependencies installed")
    except subprocess.CalledProcessError as e:
        print(f"❌ Failed to install dependencies: {e}")
        sys.exit(1)

def build_executable():
    """Build executable using PyInstaller."""
    print("🔨 Building executable...")

    try:
        cmd = [
            sys.executable, "-m", "PyInstaller",
            "--name", BuildConfig.APP_NAME,
            "--version", BuildConfig.APP_VERSION,
            "--noconsole",  # Hide console
            "--onefile",    # Single executable
            "--clean",      # Clean cache
            "--distpath", str(BuildConfig.DIST_DIR),
            "--workpath", str(BuildConfig.BUILD_DIR),
        ]

        # Add includes
        for include in BuildConfig.get_includes():
            cmd.extend(["--add-data", f"{include};{include}"])

        # Add main script
        cmd.append("main.py")

        subprocess.check_call(cmd)
        print("  Executable built successfully")

    except subprocess.CalledProcessError as e:
        print(f"❌ Build failed: {e}")
        sys.exit(1)

def create_installer():
    """Create installer package."""
    print("📦 Creating installer...")

    # This would integrate with NSIS, Inno Setup, or similar
    # For now, just copy additional files
    try:
        # Copy documentation
        doc_dir = BuildConfig.DIST_DIR / "docs"
        if Path("docs").exists():
            shutil.copytree("docs", doc_dir)

        # Copy examples
        example_dir = BuildConfig.DIST_DIR / "examples"
        if Path("examples").exists():
            shutil.copytree("examples", example_dir)

        # Create README for distribution
        readme_content = f"""
{BuildConfig.APP_NAME} v{BuildConfig.APP_VERSION}

{BuildConfig.DESCRIPTION}

Created with Bravura SDK - Professional GUI Framework

Installation:
1. Extract all files to a folder
2. Run {BuildConfig.APP_NAME}.exe

For support: contact {BuildConfig.AUTHOR}
"""

        readme_path = BuildConfig.DIST_DIR / "README.txt"
        with open(readme_path, 'w') as f:
            f.write(readme_content)

        print("  Installer package created")

    except Exception as e:
        print(f"❌ Failed to create installer: {e}")
        sys.exit(1)

def run_tests():
    """Run test suite before building."""
    print("🧪 Running tests...")

    try:
        result = subprocess.run([
            sys.executable, "-m", "pytest", "tests/",
            "-v", "--tb=short"
        ], capture_output=True, text=True)

        if result.returncode != 0:
            print("❌ Tests failed:")
            print(result.stdout)
            print(result.stderr)
            sys.exit(1)

        print("  All tests passed")

    except FileNotFoundError:
        print("⚠️ pytest not found, skipping tests")
    except subprocess.CalledProcessError as e:
        print(f"❌ Test execution failed: {e}")
        sys.exit(1)

def main():
    """Main build process."""
    print(f"🚀 Building {BuildConfig.APP_NAME} v{BuildConfig.APP_VERSION}")
    print("=" * 50)

    # Run build steps
    run_tests()
    clean_build()
    create_build_environment()
    install_dependencies()
    build_executable()
    create_installer()

    # Success message
    exe_path = BuildConfig.DIST_DIR / f"{BuildConfig.APP_NAME}.exe"
    size_mb = exe_path.stat().st_size / 1024 / 1024

    print("\n" + "=" * 50)
    print("✅ Build completed successfully!")
    print(f"📁 Output: {BuildConfig.DIST_DIR}")
    print(".1f")
    print(f"🎯 Ready for distribution")

if __name__ == "__main__":
    main()

---

Maintenance Guidelines

Code Quality Standards


# .pre-commit-config.yaml
repos:
  - repo: https://github.com/pre-commit/pre-commit-hooks
    rev: v4.4.0
    hooks:
      - id: trailing-whitespace
      - id: end-of-file-fixer
      - id: check-yaml
      - id: check-added-large-files

  - repo: https://github.com/psf/black
    rev: 22.3.0
    hooks:
      - id: black
        language_version: python3.8

  - repo: https://github.com/pycqa/isort
    rev: 5.10.1
    hooks:
      - id: isort
        args: ["--profile", "black"]

  - repo: https://github.com/pycqa/flake8
    rev: 4.0.1
    hooks:
      - id: flake8
        args: ["--max-line-length=100", "--extend-ignore=E203,W503"]

Documentation Maintenance


# docs_maintenance.py
"""
Documentation maintenance utilities.
"""

import os
import re
from pathlib import Path

class DocsMaintainer:
    """Maintain documentation quality and consistency."""

    def __init__(self, docs_dir="docs"):
        self.docs_dir = Path(docs_dir)

    def check_broken_links(self):
        """Check for broken internal links in documentation."""
        broken_links = []

        for md_file in self.docs_dir.glob("**/*.md"):
            content = md_file.read_text()

            # Find markdown links
            link_pattern = r'\[([^\]]+)\]\(([^)]+)\)'
            links = re.findall(link_pattern, content)

            for link_text, link_url in links:
                if link_url.startswith('./') or link_url.startswith('../'):
                    # Relative link - check if target exists
                    target_path = (md_file.parent / link_url).resolve()

                    if not target_path.exists():
                        broken_links.append({
                            'file': str(md_file),
                            'link_text': link_text,
                            'link_url': link_url,
                            'target_path': str(target_path)
                        })

        return broken_links

    def check_consistency(self):
        """Check documentation consistency."""
        issues = []

        # Check version consistency
        version_pattern = r'Version:\s*([\d.]+)'
        versions = {}

        for md_file in self.docs_dir.glob("**/*.md"):
            content = md_file.read_text()
            matches = re.findall(version_pattern, content, re.IGNORECASE)

            if matches:
                versions[str(md_file)] = matches

        # Check for version inconsistencies
        if versions:
            all_versions = [v for versions_list in versions.values() for v in versions_list]
            unique_versions = set(all_versions)

            if len(unique_versions) > 1:
                issues.append({
                    'type': 'version_inconsistency',
                    'message': f'Multiple versions found: {unique_versions}',
                    'files': versions
                })

        return issues

    def update_timestamps(self):
        """Update last modified timestamps in documentation."""
        import datetime

        for md_file in self.docs_dir.glob("**/*.md"):
            stat = md_file.stat()
            modified_time = datetime.datetime.fromtimestamp(stat.st_mtime)

            content = md_file.read_text()

            # Update timestamp pattern
            timestamp_pattern = r'(\* Last Updated:).*'
            new_timestamp = f'* Last Updated: {modified_time.strftime("%B %d, %Y")}'

            if re.search(timestamp_pattern, content):
                new_content = re.sub(timestamp_pattern, new_timestamp, content)
                md_file.write_text(new_content)
                print(f"Updated timestamp in {md_file}")

    def validate_examples(self):
        """Validate code examples in documentation."""
        validation_issues = []

        for md_file in self.docs_dir.glob("**/*.md"):
            content = md_file.read_text()

            # Find Python code blocks
            code_block_pattern = r'```python\s*(.*?)\s*```'
            code_blocks = re.findall(code_block_pattern, content, re.DOTALL)

            for i, code_block in enumerate(code_blocks):
                # Basic syntax check
                try:
                    compile(code_block, f'<{md_file.name}:block_{i+1}>', 'exec')
                except SyntaxError as e:
                    validation_issues.append({
                        'file': str(md_file),
                        'block': i + 1,
                        'error': str(e),
                        'code': code_block[:200] + '...' if len(code_block) > 200 else code_block
                    })

        return validation_issues

    def generate_toc(self, md_file):
        """Generate table of contents for a markdown file."""
        content = md_file.read_text()

        # Find headers
        header_pattern = r'^(#{1,6})\s+(.+){{RenderedMarkdown}}#39;
        headers = []

        for line in content.split('\n'):
            match = re.match(header_pattern, line)
            if match:
                level = len(match.group(1))
                title = match.group(2)
                headers.append((level, title))

        # Generate TOC
        toc_lines = ['## Table of Contents', '']

        for level, title in headers:
            indent = '  ' * (level - 2) if level > 2 else ''
            link = re.sub(r'[^\w\s-]', '', title).replace(' ', '-').lower()
            toc_lines.append(f'{indent}- [{title}](#{link})')

        toc = '\n'.join(toc_lines)

        # Insert TOC after title
        lines = content.split('\n')
        insert_index = 1

        # Find end of frontmatter/title
        for i, line in enumerate(lines):
            if line.startswith('---') or line.strip() == '':
                continue
            elif line.startswith('#') and i > 0:
                insert_index = i
                break

        # Insert TOC
        new_content = '\n'.join(lines[:insert_index]) + '\n\n' + toc + '\n\n' + '\n'.join(lines[insert_index:])

        md_file.write_text(new_content)
        print(f"Generated TOC for {md_file}")

def main():
    """Run documentation maintenance."""
    maintainer = DocsMaintainer()

    print("🔍 Checking documentation...")

    # Check broken links
    broken_links = maintainer.check_broken_links()
    if broken_links:
        print(f"❌ Found {len(broken_links)} broken links:")
        for link in broken_links:
            print(f"  {link['file']}: {link['link_text']} -> {link['link_url']}")

    # Check consistency
    consistency_issues = maintainer.check_consistency()
    if consistency_issues:
        print(f"⚠️ Found {len(consistency_issues)} consistency issues:")
        for issue in consistency_issues:
            print(f"  {issue['type']}: {issue['message']}")

    # Validate examples
    validation_issues = maintainer.validate_examples()
    if validation_issues:
        print(f"❌ Found {len(validation_issues)} code validation issues:")
        for issue in validation_issues:
            print(f"  {issue['file']} block {issue['block']}: {issue['error']}")

    # Update timestamps
    maintainer.update_timestamps()

    print("✅ Documentation maintenance complete")

if __name__ == "__main__":
    main()

---

This comprehensive best practices guide covers the essential patterns and techniques for building production-quality applications with the Bravura SDK. Following these practices ensures maintainable, performant, and professional applications that scale effectively.