Best Practices

This guide provides best practices for building robust, maintainable, and user-friendly applications with ThreePaneWindows.

Architecture and Design

Application Structure

Organize your application with clear separation of concerns:

# Recommended project structure
my_app/
├── main.py                 # Application entry point
├── config/
   ├── __init__.py
   ├── settings.py         # Application settings
   └── themes.py           # Custom themes
├── ui/
   ├── __init__.py
   ├── main_window.py      # Main window setup
   ├── panels/             # Panel builders
      ├── __init__.py
      ├── file_panel.py
      ├── editor_panel.py
      └── properties_panel.py
   └── dialogs/            # Dialog windows
├── core/
   ├── __init__.py
   ├── models.py           # Data models
   ├── services.py         # Business logic
   └── events.py           # Event handling
├── resources/
   ├── icons/              # Application icons
   ├── themes/             # Theme definitions
   └── config/             # Configuration files
└── tests/                  # Unit tests

Main Application Structure:

# main.py
import tkinter as tk
from ui.main_window import MainWindow
from config.settings import AppSettings

class Application:
    def __init__(self):
        self.root = tk.Tk()
        self.settings = AppSettings()
        self.main_window = None

    def initialize(self):
        """Initialize the application."""
        self.setup_root_window()
        self.create_main_window()
        self.load_user_preferences()

    def setup_root_window(self):
        """Configure the root window."""
        self.root.title(self.settings.APP_NAME)
        self.root.geometry(self.settings.DEFAULT_GEOMETRY)
        self.root.minsize(800, 600)

    def create_main_window(self):
        """Create the main application window."""
        self.main_window = MainWindow(self.root, self.settings)

    def load_user_preferences(self):
        """Load and apply user preferences."""
        prefs = self.settings.load_preferences()
        if prefs:
            self.main_window.apply_preferences(prefs)

    def run(self):
        """Start the application."""
        self.initialize()
        self.root.mainloop()

    def shutdown(self):
        """Clean shutdown of the application."""
        if self.main_window:
            self.main_window.save_preferences()
        self.root.quit()

if __name__ == "__main__":
    app = Application()
    try:
        app.run()
    except KeyboardInterrupt:
        app.shutdown()

Modular Panel Design

Create reusable, self-contained panel modules:

# ui/panels/base_panel.py
import tkinter as tk
from abc import ABC, abstractmethod

class BasePanel(ABC):
    """Base class for all panels."""

    def __init__(self, parent, config=None):
        self.parent = parent
        self.config = config or {}
        self.widgets = {}
        self.is_initialized = False

    @abstractmethod
    def build_ui(self):
        """Build the panel UI. Must be implemented by subclasses."""
        pass

    def initialize(self):
        """Initialize the panel."""
        if not self.is_initialized:
            self.build_ui()
            self.setup_bindings()
            self.load_data()
            self.is_initialized = True

    def setup_bindings(self):
        """Setup event bindings. Override in subclasses."""
        pass

    def load_data(self):
        """Load initial data. Override in subclasses."""
        pass

    def cleanup(self):
        """Cleanup resources. Override in subclasses."""
        pass

# ui/panels/file_panel.py
from .base_panel import BasePanel

class FilePanel(BasePanel):
    """File explorer panel."""

    def build_ui(self):
        """Build the file panel UI."""
        # Header
        header_frame = tk.Frame(self.parent)
        header_frame.pack(fill=tk.X, padx=5, pady=5)

        tk.Label(header_frame, text="📁 Files",
                font=("Arial", 12, "bold")).pack(side=tk.LEFT)

        refresh_btn = tk.Button(header_frame, text="🔄",
                              command=self.refresh_files)
        refresh_btn.pack(side=tk.RIGHT)

        # File list
        list_frame = tk.Frame(self.parent)
        list_frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)

        self.widgets['file_list'] = tk.Listbox(list_frame)
        self.widgets['file_list'].pack(fill=tk.BOTH, expand=True)

        # Scrollbar
        scrollbar = tk.Scrollbar(list_frame,
                               command=self.widgets['file_list'].yview)
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
        self.widgets['file_list'].config(yscrollcommand=scrollbar.set)

    def setup_bindings(self):
        """Setup file panel bindings."""
        self.widgets['file_list'].bind('<Double-Button-1>', self.on_file_double_click)
        self.widgets['file_list'].bind('<Button-3>', self.show_context_menu)

    def load_data(self):
        """Load file list."""
        self.refresh_files()

    def refresh_files(self):
        """Refresh the file list."""
        # Implementation here
        pass

    def on_file_double_click(self, event):
        """Handle file double-click."""
        # Implementation here
        pass

    def show_context_menu(self, event):
        """Show context menu for files."""
        # Implementation here
        pass

Configuration Management

Implement robust configuration management:

# config/settings.py
import json
import os
from pathlib import Path

class AppSettings:
    """Application settings manager."""

    # Default settings
    APP_NAME = "My ThreePaneWindows App"
    DEFAULT_GEOMETRY = "1200x800"
    DEFAULT_THEME = "light"

    def __init__(self):
        self.config_dir = Path.home() / ".my_app"
        self.config_file = self.config_dir / "config.json"
        self.ensure_config_dir()

    def ensure_config_dir(self):
        """Ensure configuration directory exists."""
        self.config_dir.mkdir(exist_ok=True)

    def load_preferences(self):
        """Load user preferences."""
        try:
            if self.config_file.exists():
                with open(self.config_file, 'r') as f:
                    return json.load(f)
        except Exception as e:
            print(f"Error loading preferences: {e}")

        return self.get_default_preferences()

    def save_preferences(self, preferences):
        """Save user preferences."""
        try:
            with open(self.config_file, 'w') as f:
                json.dump(preferences, f, indent=2)
        except Exception as e:
            print(f"Error saving preferences: {e}")

    def get_default_preferences(self):
        """Get default preferences."""
        return {
            "window": {
                "geometry": self.DEFAULT_GEOMETRY,
                "theme": self.DEFAULT_THEME
            },
            "panes": {
                "left_width": 250,
                "right_width": 300,
                "left_detached": False,
                "right_detached": False
            },
            "recent_files": [],
            "ui": {
                "show_status_bar": True,
                "show_toolbar": True
            }
        }

User Interface Design

Consistent Visual Design

Maintain visual consistency throughout your application:

# ui/styles.py
class UIStyles:
    """Centralized UI styling constants."""

    # Fonts
    HEADER_FONT = ("Arial", 12, "bold")
    CONTENT_FONT = ("Arial", 10)
    CODE_FONT = ("Consolas", 10)

    # Colors (will be overridden by themes)
    PRIMARY_COLOR = "#007bff"
    SECONDARY_COLOR = "#6c757d"
    SUCCESS_COLOR = "#28a745"
    WARNING_COLOR = "#ffc107"
    ERROR_COLOR = "#dc3545"

    # Spacing
    PADDING_SMALL = 5
    PADDING_MEDIUM = 10
    PADDING_LARGE = 20

    # Widget sizes
    BUTTON_WIDTH = 100
    ENTRY_WIDTH = 200
    LISTBOX_HEIGHT = 10

    @classmethod
    def apply_button_style(cls, button, style="primary"):
        """Apply consistent button styling."""
        styles = {
            "primary": {"bg": cls.PRIMARY_COLOR, "fg": "white"},
            "secondary": {"bg": cls.SECONDARY_COLOR, "fg": "white"},
            "success": {"bg": cls.SUCCESS_COLOR, "fg": "white"},
            "warning": {"bg": cls.WARNING_COLOR, "fg": "black"},
            "danger": {"bg": cls.ERROR_COLOR, "fg": "white"}
        }

        if style in styles:
            button.configure(**styles[style])
            button.configure(relief=tk.FLAT, padx=cls.PADDING_MEDIUM)

Responsive Layout Design

Design layouts that work well at different sizes:

def create_responsive_panel(parent):
    """Create panel that adapts to different sizes."""

    def build_responsive_content(frame):
        """Build content that adapts to frame size."""

        # Use frames that expand/contract appropriately
        header_frame = tk.Frame(frame, height=40)
        header_frame.pack(fill=tk.X)
        header_frame.pack_propagate(False)

        content_frame = tk.Frame(frame)
        content_frame.pack(fill=tk.BOTH, expand=True)

        footer_frame = tk.Frame(frame, height=30)
        footer_frame.pack(fill=tk.X)
        footer_frame.pack_propagate(False)

        # Responsive content in main area
        def update_layout(event=None):
            """Update layout based on available space."""
            width = content_frame.winfo_width()
            height = content_frame.winfo_height()

            # Adjust layout based on size
            if width < 300:
                # Narrow layout - stack vertically
                configure_narrow_layout(content_frame)
            else:
                # Wide layout - use columns
                configure_wide_layout(content_frame)

        # Bind to size changes
        content_frame.bind('<Configure>', update_layout)

        return content_frame

    return build_responsive_content

Error Handling and Validation

Robust Error Handling

Implement comprehensive error handling:

# core/error_handling.py
import logging
import traceback
from functools import wraps

class ErrorHandler:
    """Centralized error handling."""

    def __init__(self):
        self.setup_logging()

    def setup_logging(self):
        """Setup application logging."""
        logging.basicConfig(
            level=logging.INFO,
            format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
            handlers=[
                logging.FileHandler('app.log'),
                logging.StreamHandler()
            ]
        )
        self.logger = logging.getLogger(__name__)

    def handle_exception(self, exc_type, exc_value, exc_traceback):
        """Handle uncaught exceptions."""
        if issubclass(exc_type, KeyboardInterrupt):
            return  # Allow Ctrl+C to work

        error_msg = ''.join(traceback.format_exception(exc_type, exc_value, exc_traceback))
        self.logger.error(f"Uncaught exception: {error_msg}")

        # Show user-friendly error dialog
        self.show_error_dialog("An unexpected error occurred", str(exc_value))

    def show_error_dialog(self, title, message):
        """Show error dialog to user."""
        import tkinter.messagebox as messagebox
        messagebox.showerror(title, message)

def safe_execute(error_handler=None):
    """Decorator for safe function execution."""
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            try:
                return func(*args, **kwargs)
            except Exception as e:
                if error_handler:
                    error_handler.logger.error(f"Error in {func.__name__}: {e}")
                    error_handler.show_error_dialog("Operation Failed", str(e))
                else:
                    print(f"Error in {func.__name__}: {e}")
                return None
        return wrapper
    return decorator

Input Validation

Validate user input consistently:

# core/validation.py
import re
from typing import Any, Tuple, Optional

class Validator:
    """Input validation utilities."""

    @staticmethod
    def validate_file_path(path: str) -> Tuple[bool, str]:
        """Validate file path."""
        if not path:
            return False, "Path cannot be empty"

        if not os.path.exists(path):
            return False, f"Path does not exist: {path}"

        if not os.path.isfile(path):
            return False, f"Path is not a file: {path}"

        return True, "Valid file path"

    @staticmethod
    def validate_icon_path(path: str) -> Tuple[bool, str]:
        """Validate icon file path."""
        if not path:
            return True, "No icon specified"

        is_valid, message = Validator.validate_file_path(path)
        if not is_valid:
            return False, message

        # Check file extension
        valid_extensions = ['.ico', '.png', '.gif', '.bmp', '.xbm']
        ext = os.path.splitext(path)[1].lower()

        if ext not in valid_extensions:
            return False, f"Unsupported icon format: {ext}"

        return True, "Valid icon file"

    @staticmethod
    def validate_number_range(value: Any, min_val: float, max_val: float) -> Tuple[bool, str]:
        """Validate number is within range."""
        try:
            num_value = float(value)
            if min_val <= num_value <= max_val:
                return True, f"Valid number: {num_value}"
            else:
                return False, f"Number must be between {min_val} and {max_val}"
        except (ValueError, TypeError):
            return False, f"Invalid number: {value}"

Performance Optimization

Efficient Content Loading

Implement lazy loading and caching:

# core/content_manager.py
import threading
import time
from typing import Dict, Any, Callable

class ContentManager:
    """Manage content loading and caching."""

    def __init__(self):
        self.cache: Dict[str, Any] = {}
        self.loading_tasks: Dict[str, threading.Thread] = {}
        self.max_cache_size = 100

    def load_content_async(self, key: str, loader_func: Callable,
                         callback: Callable = None):
        """Load content asynchronously."""

        # Check cache first
        if key in self.cache:
            if callback:
                callback(self.cache[key])
            return self.cache[key]

        # Check if already loading
        if key in self.loading_tasks and self.loading_tasks[key].is_alive():
            return None

        # Start loading task
        def load_task():
            try:
                content = loader_func()
                self.cache[key] = content

                # Manage cache size
                if len(self.cache) > self.max_cache_size:
                    # Remove oldest entry (simple FIFO)
                    oldest_key = next(iter(self.cache))
                    del self.cache[oldest_key]

                if callback:
                    # Schedule callback on main thread
                    root.after(0, lambda: callback(content))

            except Exception as e:
                print(f"Error loading content for {key}: {e}")
                if callback:
                    root.after(0, lambda: callback(None))

            finally:
                # Clean up task reference
                if key in self.loading_tasks:
                    del self.loading_tasks[key]

        task = threading.Thread(target=load_task, daemon=True)
        self.loading_tasks[key] = task
        task.start()

        return None

    def clear_cache(self):
        """Clear the content cache."""
        self.cache.clear()

    def preload_content(self, keys_and_loaders: Dict[str, Callable]):
        """Preload multiple content items."""
        for key, loader in keys_and_loaders.items():
            self.load_content_async(key, loader)

Memory Management

Implement proper memory management:

# core/memory_manager.py
import gc
import psutil
import os

class MemoryManager:
    """Monitor and manage memory usage."""

    def __init__(self, warning_threshold=80, critical_threshold=90):
        self.warning_threshold = warning_threshold
        self.critical_threshold = critical_threshold
        self.process = psutil.Process(os.getpid())

    def get_memory_usage(self):
        """Get current memory usage percentage."""
        memory_info = self.process.memory_info()
        system_memory = psutil.virtual_memory()

        usage_percent = (memory_info.rss / system_memory.total) * 100
        return {
            'usage_percent': usage_percent,
            'rss': memory_info.rss,
            'vms': memory_info.vms,
            'system_total': system_memory.total,
            'system_available': system_memory.available
        }

    def check_memory_status(self):
        """Check memory status and take action if needed."""
        usage = self.get_memory_usage()
        usage_percent = usage['usage_percent']

        if usage_percent > self.critical_threshold:
            self.handle_critical_memory()
            return "critical"
        elif usage_percent > self.warning_threshold:
            self.handle_warning_memory()
            return "warning"

        return "normal"

    def handle_warning_memory(self):
        """Handle warning memory level."""
        print(f"Memory usage warning: {self.get_memory_usage()['usage_percent']:.1f}%")
        # Trigger garbage collection
        gc.collect()

    def handle_critical_memory(self):
        """Handle critical memory level."""
        print(f"Critical memory usage: {self.get_memory_usage()['usage_percent']:.1f}%")
        # Force garbage collection
        gc.collect()
        # Clear caches
        # Notify user

    def start_monitoring(self, interval=30000):  # 30 seconds
        """Start periodic memory monitoring."""
        def monitor():
            self.check_memory_status()
            root.after(interval, monitor)

        monitor()

Cross-Platform Compatibility

Platform-Specific Handling

Handle platform differences gracefully:

# core/platform_utils.py
import platform
import os

class PlatformUtils:
    """Platform-specific utilities."""

    @staticmethod
    def get_platform():
        """Get current platform."""
        return platform.system()

    @staticmethod
    def get_config_dir():
        """Get platform-appropriate config directory."""
        system = platform.system()

        if system == "Windows":
            return os.path.join(os.environ['APPDATA'], 'MyApp')
        elif system == "Darwin":  # macOS
            return os.path.expanduser('~/Library/Application Support/MyApp')
        else:  # Linux and others
            return os.path.expanduser('~/.config/myapp')

    @staticmethod
    def get_default_font():
        """Get platform-appropriate default font."""
        system = platform.system()

        if system == "Windows":
            return ("Segoe UI", 10)
        elif system == "Darwin":  # macOS
            return ("SF Pro Text", 10)
        else:  # Linux
            return ("Ubuntu", 10)

    @staticmethod
    def setup_platform_specific_ui(root):
        """Setup platform-specific UI elements."""
        system = platform.system()

        if system == "Darwin":  # macOS
            # macOS-specific menu setup
            root.createcommand('tk::mac::ShowPreferences', lambda: show_preferences())
            root.createcommand('tk::mac::Quit', lambda: root.quit())

        elif system == "Windows":
            # Windows-specific setup
            try:
                # Set Windows-specific icon
                root.iconbitmap('resources/icons/app.ico')
            except:
                pass

Icon Management Best Practices

Implement robust icon handling:

# ui/icon_manager.py
from threepanewindows import get_recommended_icon_formats, validate_icon_path

class IconManager:
    """Manage application icons across platforms."""

    def __init__(self, icon_dir="resources/icons"):
        self.icon_dir = icon_dir
        self.icon_cache = {}
        self.recommended_formats = get_recommended_icon_formats()

    def get_icon_path(self, icon_name):
        """Get best icon path for current platform."""

        # Try recommended formats first
        for ext in self.recommended_formats:
            icon_path = os.path.join(self.icon_dir, f"{icon_name}{ext}")
            if os.path.exists(icon_path):
                is_valid, _ = validate_icon_path(icon_path)
                if is_valid:
                    return icon_path

        # Try all supported formats
        all_formats = ['.ico', '.png', '.gif', '.bmp', '.xbm']
        for ext in all_formats:
            if ext not in self.recommended_formats:
                icon_path = os.path.join(self.icon_dir, f"{icon_name}{ext}")
                if os.path.exists(icon_path):
                    is_valid, _ = validate_icon_path(icon_path)
                    if is_valid:
                        return icon_path

        return ""  # No suitable icon found

    def create_pane_config_with_icon(self, title, icon_name, **kwargs):
        """Create PaneConfig with appropriate icon."""
        from threepanewindows import PaneConfig

        icon_path = self.get_icon_path(icon_name)

        return PaneConfig(
            title=title,
            window_icon=icon_path,
            **kwargs
        )

Testing and Quality Assurance

Unit Testing

Implement comprehensive unit tests:

# tests/test_panels.py
import unittest
import tkinter as tk
from ui.panels.file_panel import FilePanel

class TestFilePanel(unittest.TestCase):
    """Test cases for FilePanel."""

    def setUp(self):
        """Set up test fixtures."""
        self.root = tk.Tk()
        self.root.withdraw()  # Hide test window
        self.test_frame = tk.Frame(self.root)
        self.panel = FilePanel(self.test_frame)

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

    def test_panel_initialization(self):
        """Test panel initializes correctly."""
        self.assertFalse(self.panel.is_initialized)
        self.panel.initialize()
        self.assertTrue(self.panel.is_initialized)

    def test_ui_creation(self):
        """Test UI elements are created."""
        self.panel.initialize()
        self.assertIn('file_list', self.panel.widgets)
        self.assertIsInstance(self.panel.widgets['file_list'], tk.Listbox)

    def test_error_handling(self):
        """Test error handling in panel operations."""
        # Test with invalid configuration
        with self.assertRaises(ValueError):
            FilePanel(None)  # Invalid parent

Integration Testing

Test component integration:

# tests/test_integration.py
import unittest
import tkinter as tk
from threepanewindows import EnhancedDockableThreePaneWindow, PaneConfig

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

    def setUp(self):
        """Set up integration test environment."""
        self.root = tk.Tk()
        self.root.withdraw()

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

    def test_complete_window_creation(self):
        """Test complete window creation and configuration."""

        def build_test_panel(frame):
            tk.Label(frame, text="Test Panel").pack()

        # Create window with all features
        window = EnhancedDockableThreePaneWindow(
            self.root,
            left_config=PaneConfig(title="Left", detachable=True),
            center_config=PaneConfig(title="Center", detachable=False),
            right_config=PaneConfig(title="Right", detachable=True),
            left_builder=build_test_panel,
            center_builder=build_test_panel,
            right_builder=build_test_panel,
            theme_name="light"
        )

        window.pack(fill=tk.BOTH, expand=True)

        # Test window is created and functional
        self.assertIsNotNone(window)
        self.assertEqual(window.get_current_theme(), "light")

Documentation and Maintenance

Code Documentation

Document your code thoroughly:

def create_professional_application():
    """
    Create a professional three-pane application.

    This function demonstrates best practices for creating a robust,
    maintainable three-pane application with ThreePaneWindows.

    Returns:
        EnhancedDockableThreePaneWindow: Configured main window

    Example:
        >>> app = create_professional_application()
        >>> app.pack(fill=tk.BOTH, expand=True)

    Note:
        This function requires proper icon files in the resources/icons
        directory for optimal cross-platform compatibility.
    """

    # Implementation with detailed comments
    pass

Version Management

Implement proper version management:

# version.py
__version__ = "1.0.0"
__version_info__ = (1, 0, 0)

def check_compatibility():
    """Check ThreePaneWindows version compatibility."""
    import threepanewindows

    required_version = (1, 0, 4)
    current_version = threepanewindows.__version_info__

    if current_version < required_version:
        raise RuntimeError(
            f"ThreePaneWindows {'.'.join(map(str, required_version))} "
            f"or higher required, found {'.'.join(map(str, current_version))}"
        )

Summary of Best Practices

Architecture: 1. Use modular design with clear separation of concerns 2. Implement proper configuration management 3. Use event-driven architecture for loose coupling 4. Design for testability from the start

User Interface: 1. Maintain visual consistency throughout the application 2. Implement responsive design principles 3. Provide comprehensive keyboard navigation 4. Follow platform-specific UI conventions

Error Handling: 1. Implement comprehensive error handling and logging 2. Validate all user input 3. Provide meaningful error messages 4. Handle edge cases gracefully

Performance: 1. Use lazy loading for expensive operations 2. Implement efficient caching strategies 3. Monitor memory usage and clean up resources 4. Optimize for the common use case

Cross-Platform: 1. Test on all target platforms regularly 2. Handle platform differences gracefully 3. Use appropriate file paths and conventions 4. Provide platform-specific optimizations

Quality Assurance: 1. Write comprehensive unit and integration tests 2. Use static analysis tools 3. Implement continuous integration 4. Document code thoroughly

Maintenance: 1. Keep dependencies up to date 2. Monitor performance metrics 3. Collect and analyze user feedback 4. Plan for future extensibility

Following these best practices will help you create robust, maintainable, and user-friendly applications that provide an excellent user experience across all platforms.