CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-uiautomation

Python UIAutomation for Windows - comprehensive library for automating Windows applications using Microsoft's UIAutomation framework

Overview
Eval results
Files

logging-debugging.mddocs/

Logging and Debugging

Comprehensive logging system with color support and control tree enumeration for debugging automation scripts and understanding application structure. These tools help developers troubleshoot automation issues and analyze UI hierarchies.

Capabilities

Logger Class

Main logging class providing various output methods with color support.

class Logger:
    """Comprehensive logging system for automation debugging."""
    
    def Write(self, text: str) -> None:
        """
        Write text to log without newline.
        
        Args:
            text: Text to write
        """
    
    def WriteLine(self, text: str) -> None:
        """
        Write text to log with newline.
        
        Args:
            text: Text to write
        """
    
    def ColorfulWrite(self, text: str, color: int) -> None:
        """
        Write colored text to log without newline.
        
        Args:
            text: Text to write
            color: Console color constant
        """
    
    def ColorfulWriteLine(self, text: str, color: int) -> None:
        """
        Write colored text to log with newline.
        
        Args:
            text: Text to write  
            color: Console color constant
        """
    
    def Log(self, message: str) -> None:
        """
        Log message with timestamp.
        
        Args:
            message: Message to log
        """
    
    def ColorfulLog(self, message: str, color: int) -> None:
        """
        Log colored message with timestamp.
        
        Args:
            message: Message to log
            color: Console color constant
        """
    
    def SetLogFile(self, filename: str) -> None:
        """
        Set output log file.
        
        Args:
            filename: Path to log file
        """
    
    def Close(self) -> None:
        """Close log file."""

Console Colors

class ConsoleColor:
    """Console color constants for colored logging."""
    
    # Standard colors
    Black: int = 0
    DarkBlue: int = 1
    DarkGreen: int = 2
    DarkCyan: int = 3
    DarkRed: int = 4
    DarkMagenta: int = 5
    DarkYellow: int = 6
    Gray: int = 7
    DarkGray: int = 8
    Blue: int = 9
    Green: int = 10
    Cyan: int = 11
    Red: int = 12
    Magenta: int = 13
    Yellow: int = 14
    White: int = 15

class Logger:
    # Color name to value mapping
    ColorName2Value: dict = {
        'Black': 0,
        'DarkBlue': 1,
        'DarkGreen': 2,
        'DarkCyan': 3,
        'DarkRed': 4,
        'DarkMagenta': 5,
        'DarkYellow': 6,
        'Gray': 7,
        'DarkGray': 8,
        'Blue': 9,
        'Green': 10,
        'Cyan': 11,
        'Red': 12,
        'Magenta': 13,
        'Yellow': 14,
        'White': 15
    }

Control Logging Functions

Global functions for logging control information and hierarchies.

def LogControl(control: Control) -> None:
    """
    Log detailed information about a control.
    
    Args:
        control: Control to log information for
    """

def EnumAndLogControl(control: Control, depth: int = 1, showAllControls: bool = True) -> None:
    """
    Enumerate and log control hierarchy.
    
    Args:
        control: Root control to start enumeration from
        depth: Maximum depth to enumerate (-1 for unlimited)
        showAllControls: Whether to show all controls or just visible ones
    """

def EnumAndLogControlAncestors(control: Control) -> None:
    """
    Log all ancestor controls of the given control.
    
    Args:
        control: Control to log ancestors for
    """

System Utilities

def ShowDesktop(waitTime: float = 1) -> None:
    """
    Show the desktop (minimize all windows).
    
    Args:
        waitTime: Time to wait after showing desktop
    """

def RunWithHotKey(func: callable, hotkey: str) -> None:
    """
    Run a function when a hotkey is pressed.
    
    Args:
        func: Function to execute
        hotkey: Hotkey combination (e.g., 'ctrl+f1')
    """

def WaitHotKeyReleased(hotkey: str, timeout: float = 15) -> bool:
    """
    Wait for a hotkey to be released.
    
    Args:
        hotkey: Hotkey combination
        timeout: Maximum wait time in seconds
        
    Returns:
        bool: True if hotkey was released, False if timeout
    """

Debug Configuration

Global debug settings for controlling logging behavior.

# Debug flags
DEBUG_SEARCH_TIME: bool = False    # Log search timing information
DEBUG_EXIST_DISAPPEAR: bool = False # Log control existence checking

# Configuration functions
def SetDebugSearchTime(enabled: bool) -> None:
    """Enable/disable search time debugging."""

def SetDebugExistDisappear(enabled: bool) -> None:
    """Enable/disable existence checking debugging."""

Usage Examples

Basic Logging

import uiautomation

# Create logger instance
logger = uiautomation.Logger()

# Basic text logging
logger.WriteLine("Starting automation script")
logger.Write("Processing... ")
logger.WriteLine("Done")

# Colored logging
logger.ColorfulWriteLine("Success!", uiautomation.ConsoleColor.Green)
logger.ColorfulWriteLine("Warning: Check configuration", uiautomation.ConsoleColor.Yellow)
logger.ColorfulWriteLine("Error: Failed to connect", uiautomation.ConsoleColor.Red)

# Timestamped logging
logger.Log("Script execution started")
logger.ColorfulLog("Critical error occurred", uiautomation.ConsoleColor.Red)

File Logging

# Set up file logging
logger = uiautomation.Logger()
logger.SetLogFile("automation_log.txt")

# Log to file
logger.WriteLine("This will be written to the file")
logger.ColorfulWriteLine("Colored text in file", uiautomation.ConsoleColor.Blue)

# Close file when done
logger.Close()

Control Information Logging

# Log detailed information about a specific control
button = uiautomation.ButtonControl(Name='Submit')
if button.Exists():
    uiautomation.LogControl(button)

# Log information about the focused control
focused = uiautomation.GetFocusedControl()
if focused:
    uiautomation.LogControl(focused)

Control Hierarchy Enumeration

# Enumerate and log entire application window
app_window = uiautomation.WindowControl(Name='Calculator')
if app_window.Exists():
    # Log all controls in the window (unlimited depth)
    uiautomation.EnumAndLogControl(app_window, depth=-1)

# Enumerate with limited depth
desktop = uiautomation.GetRootControl()
uiautomation.EnumAndLogControl(desktop, depth=2)  # Only 2 levels deep

# Show only visible controls
visible_only = False  # Set to True to show only visible controls
uiautomation.EnumAndLogControl(app_window, depth=3, showAllControls=visible_only)

Ancestor Logging

# Find a specific control and log its ancestry
text_field = uiautomation.EditControl(Name='Username')
if text_field.Exists():
    uiautomation.EnumAndLogControlAncestors(text_field)
    # This will show: Desktop -> Window -> Pane -> Group -> EditControl

Debug Mode Configuration

# Enable debug logging for performance analysis
uiautomation.SetDebugSearchTime(True)
uiautomation.SetDebugExistDisappear(True)

# Now control searches will log timing information
button = uiautomation.ButtonControl(Name='Click Me')
button.Click()  # This will log search time and existence checks

# Disable debug logging
uiautomation.SetDebugSearchTime(False)
uiautomation.SetDebugExistDisappear(False)

Automation Script Debugging

def debug_automation_step(step_name, func, *args, **kwargs):
    """Wrapper function to debug automation steps."""
    logger = uiautomation.Logger()
    
    logger.ColorfulWriteLine(f"Starting step: {step_name}", uiautomation.ConsoleColor.Cyan)
    
    try:
        start_time = time.time()
        result = func(*args, **kwargs)
        end_time = time.time()
        
        logger.ColorfulWriteLine(
            f"Step completed in {end_time - start_time:.2f}s: {step_name}", 
            uiautomation.ConsoleColor.Green
        )
        return result
        
    except Exception as e:
        logger.ColorfulWriteLine(
            f"Step failed: {step_name} - {str(e)}", 
            uiautomation.ConsoleColor.Red
        )
        raise

# Use the debug wrapper
def click_submit_button():
    button = uiautomation.ButtonControl(Name='Submit')
    button.Click()

debug_automation_step("Click Submit Button", click_submit_button)

Control Search Debugging

def debug_control_search(control_type, **search_criteria):
    """Debug control search operations."""
    logger = uiautomation.Logger()
    
    # Log search criteria
    criteria_str = ", ".join([f"{k}='{v}'" for k, v in search_criteria.items()])
    logger.ColorfulWriteLine(f"Searching for {control_type.__name__} with {criteria_str}", 
                           uiautomation.ConsoleColor.Yellow)
    
    # Perform search
    control = control_type(**search_criteria)
    
    if control.Exists():
        logger.ColorfulWriteLine("Control found!", uiautomation.ConsoleColor.Green)
        # Log control details
        uiautomation.LogControl(control)
        return control
    else:
        logger.ColorfulWriteLine("Control not found!", uiautomation.ConsoleColor.Red)
        
        # Try to find similar controls for debugging
        logger.WriteLine("Looking for similar controls...")
        root = uiautomation.GetRootControl()
        uiautomation.EnumAndLogControl(root, depth=5, showAllControls=True)
        return None

# Debug a control search
found_button = debug_control_search(
    uiautomation.ButtonControl,
    Name='Submit',
    ClassName='Button'
)

Application Structure Analysis

def analyze_application_structure(app_name):
    """Analyze and log the structure of an application."""
    logger = uiautomation.Logger()
    logger.SetLogFile(f"{app_name}_structure.log")
    
    # Find application window
    app_window = uiautomation.WindowControl(Name=app_name)
    if not app_window.Exists():
        logger.ColorfulWriteLine(f"Application '{app_name}' not found", uiautomation.ConsoleColor.Red)
        return
    
    logger.ColorfulWriteLine(f"Analyzing structure of '{app_name}'", uiautomation.ConsoleColor.Cyan)
    
    # Log basic window information
    logger.WriteLine(f"Window Handle: {app_window.Handle}")
    logger.WriteLine(f"Process ID: {app_window.ProcessId}")
    logger.WriteLine(f"Class Name: {app_window.ClassName}")
    logger.WriteLine(f"Bounds: {app_window.BoundingRectangle}")
    
    # Enumerate all controls
    logger.WriteLine("\n=== COMPLETE CONTROL HIERARCHY ===")
    uiautomation.EnumAndLogControl(app_window, depth=-1)
    
    logger.Close()
    logger.WriteLine(f"Structure analysis saved to {app_name}_structure.log")

# Analyze Calculator application
analyze_application_structure("Calculator")

Hotkey Debugging Support

def setup_debug_hotkey():
    """Set up hotkey for interactive debugging."""
    def debug_current_control():
        logger = uiautomation.Logger()
        logger.ColorfulWriteLine("=== DEBUG HOTKEY PRESSED ===", uiautomation.ConsoleColor.Magenta)
        
        # Get control under cursor
        cursor_control = uiautomation.ControlFromCursor()
        if cursor_control:
            logger.WriteLine("Control under cursor:")
            uiautomation.LogControl(cursor_control)
            
            logger.WriteLine("\nControl ancestors:")
            uiautomation.EnumAndLogControlAncestors(cursor_control)
        else:
            logger.ColorfulWriteLine("No control found under cursor", uiautomation.ConsoleColor.Red)
    
    # Set up Ctrl+F12 as debug hotkey
    uiautomation.RunWithHotKey(debug_current_control, 'ctrl+f12')
    print("Debug hotkey (Ctrl+F12) is active. Press it while hovering over controls to debug.")

# Activate debug hotkey
setup_debug_hotkey()

Log Analysis Utilities

def parse_automation_log(log_file):
    """Parse and analyze automation log files."""
    logger = uiautomation.Logger()
    
    try:
        with open(log_file, 'r') as f:
            lines = f.readlines()
        
        logger.ColorfulWriteLine(f"Analyzing log file: {log_file}", uiautomation.ConsoleColor.Cyan)
        logger.WriteLine(f"Total lines: {len(lines)}")
        
        # Count different types of entries
        error_count = sum(1 for line in lines if 'error' in line.lower())
        warning_count = sum(1 for line in lines if 'warning' in line.lower())
        
        logger.WriteLine(f"Errors found: {error_count}")
        logger.WriteLine(f"Warnings found: {warning_count}")
        
        # Show errors and warnings
        if error_count > 0:
            logger.ColorfulWriteLine("\nErrors:", uiautomation.ConsoleColor.Red)
            for line in lines:
                if 'error' in line.lower():
                    logger.ColorfulWriteLine(f"  {line.strip()}", uiautomation.ConsoleColor.Red)
        
        if warning_count > 0:
            logger.ColorfulWriteLine("\nWarnings:", uiautomation.ConsoleColor.Yellow)
            for line in lines:
                if 'warning' in line.lower():
                    logger.ColorfulWriteLine(f"  {line.strip()}", uiautomation.ConsoleColor.Yellow)
                    
    except FileNotFoundError:
        logger.ColorfulWriteLine(f"Log file not found: {log_file}", uiautomation.ConsoleColor.Red)

# Analyze a log file
parse_automation_log("automation_log.txt")

Install with Tessl CLI

npx tessl i tessl/pypi-uiautomation

docs

automation-patterns.md

control-management.md

control-types.md

index.md

input-simulation.md

logging-debugging.md

screen-capture.md

windows-api.md

tile.json