From abb21792d6a2aace8fee7bc90e97a95c8578c2d0 Mon Sep 17 00:00:00 2001 From: Gabriel20xx Date: Thu, 31 Jul 2025 13:45:00 +0200 Subject: [PATCH] Add real-time resource monitoring and UI enhancements - Implemented a new resource usage display in the main UI, showing memory, CPU, and system metrics. - Added toggle functionality to hide/show resource information. - Enhanced performance monitoring with real-time updates every 2 seconds. - Included error handling and fallback modes for when psutil is not installed. - Updated requirements.txt to include psutil for enhanced monitoring capabilities. --- RESOURCE_DISPLAY_SUMMARY.md | 90 +++++++++++++ RESOURCE_MONITORING.md | 143 ++++++++++++++++++++ __pycache__/main.cpython-313.pyc | Bin 127764 -> 138511 bytes main.py | 222 +++++++++++++++++++++++++++++-- requirements.txt | 3 +- 5 files changed, 443 insertions(+), 15 deletions(-) create mode 100644 RESOURCE_DISPLAY_SUMMARY.md create mode 100644 RESOURCE_MONITORING.md diff --git a/RESOURCE_DISPLAY_SUMMARY.md b/RESOURCE_DISPLAY_SUMMARY.md new file mode 100644 index 0000000..451f6fb --- /dev/null +++ b/RESOURCE_DISPLAY_SUMMARY.md @@ -0,0 +1,90 @@ +# Resource Usage Display - Implementation Summary + +## ✅ **Features Successfully Added** + +### 🎯 **Main UI Enhancements** +- **Resource Usage Group Box**: New section displaying real-time resource metrics +- **Toggle Button**: Hide/Show resources button to save screen space +- **Color-Coded Indicators**: Visual feedback for performance status +- **Interactive Help**: Click to get psutil installation instructions + +### 📊 **Monitoring Metrics** +1. **Memory Usage**: Process memory in MB and percentage +2. **CPU Usage**: Process CPU utilization percentage +3. **System Memory**: Overall system memory usage and available space +4. **Cache Information**: Template cache size and cooldown entries +5. **Performance**: Average automation loop execution time + +### 🔄 **Real-Time Updates** +- **2-Second Refresh**: Resource display updates every 2 seconds +- **Non-Blocking**: Updates don't interfere with automation performance +- **Automatic Monitoring**: Starts immediately when application launches + +### 🎨 **Visual Design** +- **Color-Coded Status**: + - 🟢 Green: Normal/Good performance + - 🟡 Orange: Warning levels + - 🔴 Red: High usage/Performance issues + - ⚫ Gray: Unavailable metrics +- **Compact Layout**: Fits seamlessly into existing UI +- **Professional Styling**: Consistent with application theme + +### 💡 **Smart Features** +- **Fallback Mode**: Works without psutil, shows basic cache information +- **Enhanced Mode**: Full system metrics when psutil is installed +- **Error Handling**: Graceful degradation if monitoring fails +- **Installation Guide**: Built-in help for psutil setup + +## 🚀 **Benefits** + +### For Performance Monitoring +- **Real-Time Visibility**: See exactly how much resources the app uses +- **Performance Bottlenecks**: Identify slow automation loops +- **Memory Leak Detection**: Monitor memory usage over time +- **System Impact**: Understand effect on overall system performance + +### For Troubleshooting +- **Quick Diagnosis**: Color-coded warnings for immediate issue identification +- **Cache Monitoring**: Track template cache efficiency +- **Resource Optimization**: Data to optimize scenario performance +- **System Health**: Monitor system memory and CPU usage + +## 📝 **Usage Instructions** + +### Basic Usage +1. **View Resources**: Resource panel is visible by default at the bottom of the main window +2. **Toggle Display**: Click "Hide Resources" button to minimize screen usage +3. **Monitor Status**: Watch color changes for performance alerts + +### Enhanced Monitoring +1. **Install psutil**: Run `pip install psutil` (already added to requirements.txt) +2. **Restart App**: Close and reopen VisionFlow Automator +3. **Full Metrics**: All detailed system information will be displayed + +### Reading the Display +- **Memory**: Shows process memory usage (green < 10%, red > 10%) +- **CPU**: Shows process CPU usage (green < 50%, red > 50%) +- **System**: Shows system memory usage (green < 85%, red > 85%) +- **Cache**: Shows template cache size (blue normal, orange > 20 entries) +- **Performance**: Shows loop execution time (green < 500ms, red > 500ms) + +## 🔧 **Technical Implementation** + +### Code Changes +- Enhanced `get_memory_usage()` method with comprehensive metrics +- Added `_update_resource_display()` for UI updates +- Modified `_monitor_performance()` for real-time monitoring +- Added resource display widgets to main UI layout +- Implemented toggle functionality for space-saving + +### Error Handling +- Graceful fallback when psutil unavailable +- Error display in resource panel for troubleshooting +- Maintains basic functionality even if monitoring fails + +### Performance Impact +- Minimal overhead (2-second update cycle) +- Non-blocking UI updates +- Optimized memory usage for monitoring itself + +The resource usage display provides valuable real-time insights into application performance and helps users optimize their automation scenarios for better efficiency and system resource management. diff --git a/RESOURCE_MONITORING.md b/RESOURCE_MONITORING.md new file mode 100644 index 0000000..4469706 --- /dev/null +++ b/RESOURCE_MONITORING.md @@ -0,0 +1,143 @@ +# Resource Usage Monitoring - VisionFlow Automator + +## Overview +The VisionFlow Automator now includes real-time resource usage monitoring displayed directly in the UI. This feature helps users monitor the application's performance and system impact. + +## Features Added + +### 🔍 **Resource Usage Display** +A new "Resource Usage" group box has been added to the main UI showing: + +#### Memory Usage +- **Process Memory**: Shows the application's memory usage in MB and percentage of system memory +- **Color-coded warnings**: Green for normal usage, red for high usage (>10% of system memory) + +#### CPU Usage +- **Process CPU**: Shows the application's CPU usage percentage +- **Color-coded warnings**: Green for normal usage, red for high usage (>50%) + +#### System Information +- **System Memory**: Shows overall system memory usage percentage and available memory in GB +- **Color-coded warnings**: Green for normal, red when system memory usage exceeds 85% + +#### Cache Information +- **Template Cache**: Number of cached CV2 templates +- **Cooldown Entries**: Number of active step cooldowns +- **Color-coded status**: Blue for normal, orange when template cache exceeds 20 entries + +#### Performance Metrics +- **Loop Time**: Average automation loop execution time in milliseconds +- **Color-coded performance**: Green for fast loops (<500ms), red for slow loops (>500ms) + +### 🎛️ **Controls** + +#### Toggle Button +- **Hide/Show Resources**: Button to toggle the visibility of resource usage information +- **Space-saving**: Users can hide the resource display if they don't need it +- **Tooltip**: Provides information about psutil installation for enhanced monitoring + +#### Interactive Elements +- **Clickable Labels**: When psutil is not installed, clicking on resource labels shows installation instructions +- **Installation Guide**: Built-in dialog explaining how to install psutil for enhanced monitoring + +### 📊 **Monitoring Levels** + +#### Basic Monitoring (Without psutil) +When psutil is not installed, the display shows: +- Template cache size +- Cooldown entries count +- Basic performance metrics +- "N/A" for system metrics with installation instructions + +#### Enhanced Monitoring (With psutil) +When psutil is installed (`pip install psutil`), the display shows: +- Detailed process memory usage +- CPU usage percentage +- System memory statistics +- Available system memory +- Process performance metrics + +### ⚙️ **Technical Details** + +#### Update Frequency +- **Real-time Updates**: Resource display updates every 2 seconds +- **Responsive UI**: Non-blocking updates that don't interfere with automation +- **Performance Optimized**: Minimal overhead from monitoring + +#### Color Coding System +- **🟢 Green**: Normal/Good performance +- **🟡 Orange/Yellow**: Warning levels +- **🔴 Red**: High usage/Performance issues +- **⚫ Gray**: Unavailable/Error states + +#### Error Handling +- **Graceful Degradation**: Shows basic info if detailed monitoring fails +- **Error Display**: Shows error messages for troubleshooting +- **Fallback Information**: Always shows cache and performance data + +### 🎯 **Benefits** + +#### For Users +- **Real-time Monitoring**: See exactly how much resources the app is using +- **Performance Insights**: Identify when the app is running slowly +- **System Impact**: Understand the app's impact on overall system performance +- **Troubleshooting**: Quickly identify memory leaks or performance issues + +#### For Developers +- **Performance Metrics**: Built-in performance profiling +- **Memory Leak Detection**: Monitor memory usage over time +- **Cache Optimization**: Track template cache efficiency +- **System Resources**: Monitor system-wide impact + +### 📋 **Usage Instructions** + +#### Basic Usage +1. **View Resources**: Resource usage is displayed by default in the main window +2. **Toggle Display**: Click "Hide Resources" to save screen space +3. **Monitor Performance**: Watch for color changes indicating performance issues + +#### Enhanced Monitoring Setup +1. **Install psutil**: Run `pip install psutil` in your Python environment +2. **Restart Application**: Close and reopen VisionFlow Automator +3. **Enhanced Display**: All metrics will now show detailed system information + +#### Interpreting Metrics +- **Memory < 100MB**: Normal usage for typical scenarios +- **CPU < 20%**: Normal usage during automation +- **Loop Time < 200ms**: Good performance +- **Cache < 20 entries**: Normal template usage + +### 🚨 **Warning Thresholds** + +#### Memory Warnings +- **Process Memory > 10%**: High memory usage warning +- **System Memory > 85%**: System memory critical + +#### Performance Warnings +- **CPU > 50%**: High CPU usage +- **Loop Time > 500ms**: Performance degradation +- **Cache > 20 entries**: Large template cache + +### 🔧 **Troubleshooting** + +#### High Memory Usage +- Check for memory leaks in long-running sessions +- Clear template cache periodically +- Reduce number of simultaneous image templates + +#### High CPU Usage +- Reduce automation frequency +- Optimize image template sizes +- Check for infinite loops in scenarios + +#### Slow Performance +- Monitor loop times for bottlenecks +- Reduce screenshot frequency +- Optimize template matching regions + +#### Missing psutil +- Install with: `pip install psutil` +- Click on gray resource labels for installation instructions +- Restart application after installation + +The resource monitoring feature provides valuable insights into the application's performance and helps users optimize their automation scenarios for better efficiency. diff --git a/__pycache__/main.cpython-313.pyc b/__pycache__/main.cpython-313.pyc index 96dc202467debf2c27a56fd0c60ad2b799d70e23..d4a507d391f08cd8e479a4834b03f60f3ca7ca6b 100644 GIT binary patch delta 15654 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 diff --git a/main.py b/main.py index 9ace82d..55b3780 100644 --- a/main.py +++ b/main.py @@ -603,16 +603,45 @@ class MainWindow(QtWidgets.QMainWindow): import psutil process = psutil.Process() memory_info = process.memory_info() + cpu_percent = process.cpu_percent() + + # Get system info + system_memory = psutil.virtual_memory() + return { - 'rss': memory_info.rss / 1024 / 1024, # MB - 'vms': memory_info.vms / 1024 / 1024, # MB - 'percent': process.memory_percent() + 'process_memory_mb': memory_info.rss / 1024 / 1024, # MB + 'process_memory_percent': process.memory_percent(), + 'cpu_percent': cpu_percent, + 'system_memory_percent': system_memory.percent, + 'system_memory_available_gb': system_memory.available / 1024 / 1024 / 1024, # GB + 'template_cache_size': len(template_cache._cache) if hasattr(template_cache, '_cache') else 0, + 'cooldown_entries': len(self._step_cooldown) if hasattr(self, '_step_cooldown') else 0, + 'has_psutil': True } except ImportError: # psutil not available, return basic info return { - 'template_cache_size': len(template_cache._cache), - 'cooldown_entries': len(self._step_cooldown) if hasattr(self, '_step_cooldown') else 0 + 'process_memory_mb': 0, + 'process_memory_percent': 0, + 'cpu_percent': 0, + 'system_memory_percent': 0, + 'system_memory_available_gb': 0, + 'template_cache_size': len(template_cache._cache) if hasattr(template_cache, '_cache') else 0, + 'cooldown_entries': len(self._step_cooldown) if hasattr(self, '_step_cooldown') else 0, + 'has_psutil': False + } + except Exception as e: + logger.warning(f"Error getting memory usage: {e}") + return { + 'process_memory_mb': 0, + 'process_memory_percent': 0, + 'cpu_percent': 0, + 'system_memory_percent': 0, + 'system_memory_available_gb': 0, + 'template_cache_size': len(template_cache._cache) if hasattr(template_cache, '_cache') else 0, + 'cooldown_entries': len(self._step_cooldown) if hasattr(self, '_step_cooldown') else 0, + 'has_psutil': False, + 'error': str(e) } def stop_automation(self): @@ -694,20 +723,23 @@ class MainWindow(QtWidgets.QMainWindow): # Setup periodic monitoring timer self.monitor_timer = QtCore.QTimer() self.monitor_timer.timeout.connect(self._monitor_performance) - self.monitor_timer.start(10000) # Monitor every 10 seconds + self.monitor_timer.start(2000) # Monitor every 2 seconds for responsive UI updates + + # Initial resource display update + QtCore.QTimer.singleShot(100, self._monitor_performance) def _monitor_performance(self): """ Monitor application performance and memory usage. """ - if not self.running: - return - try: memory_info = self.get_memory_usage() - # Log performance stats periodically - if hasattr(self, '_loop_times') and self._loop_times: + # Update resource display + self._update_resource_display(memory_info) + + # Log performance stats periodically (only if running) + if self.running and hasattr(self, '_loop_times') and self._loop_times: avg_loop_time = sum(self._loop_times) / len(self._loop_times) # Warning thresholds @@ -715,17 +747,133 @@ class MainWindow(QtWidgets.QMainWindow): logger.warning(f"Performance issue: Average loop time {avg_loop_time:.3f}s") # Memory warning for cache-based monitoring - if 'template_cache_size' in memory_info and memory_info['template_cache_size'] > 20: + if memory_info.get('template_cache_size', 0) > 20: logger.info(f"Template cache has {memory_info['template_cache_size']} entries") except Exception as e: logger.debug(f"Performance monitoring error: {e}") + def _update_resource_display(self, memory_info): + """ + Update the resource usage display in the UI. + """ + try: + # Memory usage + if memory_info.get('has_psutil', False): + memory_text = f"Memory: {memory_info['process_memory_mb']:.1f}MB ({memory_info['process_memory_percent']:.1f}%)" + memory_color = '#d9534f' if memory_info['process_memory_percent'] > 10 else '#5cb85c' + else: + memory_text = "Memory: N/A (psutil needed)" + memory_color = '#f0ad4e' + + self.memory_label.setText(memory_text) + self.memory_label.setStyleSheet(f'font-size: 9pt; color: {memory_color};') + + # Make memory label clickable to show psutil installation info + if not memory_info.get('has_psutil', False): + self.memory_label.mousePressEvent = lambda event: self._show_psutil_info() + self.memory_label.setCursor(QtGui.QCursor(QtCore.Qt.CursorShape.PointingHandCursor)) + + # CPU usage + if memory_info.get('has_psutil', False): + cpu_text = f"CPU: {memory_info['cpu_percent']:.1f}%" + cpu_color = '#d9534f' if memory_info['cpu_percent'] > 50 else '#5cb85c' + else: + cpu_text = "CPU: N/A" + cpu_color = '#777' + + self.cpu_label.setText(cpu_text) + self.cpu_label.setStyleSheet(f'font-size: 9pt; color: {cpu_color};') + + # System memory + if memory_info.get('has_psutil', False): + system_text = f"System: {memory_info['system_memory_percent']:.1f}% ({memory_info['system_memory_available_gb']:.1f}GB free)" + system_color = '#d9534f' if memory_info['system_memory_percent'] > 85 else '#5cb85c' + else: + system_text = "System: N/A" + system_color = '#777' + + self.system_memory_label.setText(system_text) + self.system_memory_label.setStyleSheet(f'font-size: 9pt; color: {system_color};') + + # Cache info + cache_text = f"Cache: {memory_info['template_cache_size']} templates, {memory_info['cooldown_entries']} cooldowns" + cache_color = '#f0ad4e' if memory_info['template_cache_size'] > 20 else '#5bc0de' + + self.cache_label.setText(cache_text) + self.cache_label.setStyleSheet(f'font-size: 9pt; color: {cache_color};') + + # Performance info + if hasattr(self, '_loop_times') and self._loop_times: + avg_time = sum(self._loop_times) / len(self._loop_times) + perf_text = f"Performance: {avg_time*1000:.0f}ms avg loop time" + perf_color = '#d9534f' if avg_time > 0.5 else '#5cb85c' + else: + perf_text = "Performance: Not running" + perf_color = '#777' + + self.performance_label.setText(perf_text) + self.performance_label.setStyleSheet(f'font-size: 9pt; color: {perf_color};') + + # Show error if any + if 'error' in memory_info: + self.performance_label.setText(f"Error: {memory_info['error'][:50]}...") + self.performance_label.setStyleSheet('font-size: 9pt; color: #d9534f;') + + except Exception as e: + logger.debug(f"Error updating resource display: {e}") + # Show basic fallback info + self.memory_label.setText("Memory: Error") + self.cpu_label.setText("CPU: Error") + self.system_memory_label.setText("System: Error") + self.cache_label.setText("Cache: Error") + self.performance_label.setText(f"Display Error: {str(e)[:30]}...") + def stop_monitoring(self): """Stop performance monitoring timer.""" if hasattr(self, 'monitor_timer') and self.monitor_timer: self.monitor_timer.stop() + def _toggle_resource_display(self): + """Toggle the visibility of resource usage widgets.""" + if self.resource_widgets_visible: + # Hide resource widgets + self.memory_label.hide() + self.cpu_label.hide() + self.system_memory_label.hide() + self.cache_label.hide() + self.performance_label.hide() + self.toggle_resources_btn.setText('Show Resources') + self.resource_widgets_visible = False + else: + # Show resource widgets + self.memory_label.show() + self.cpu_label.show() + self.system_memory_label.show() + self.cache_label.show() + self.performance_label.show() + self.toggle_resources_btn.setText('Hide Resources') + self.resource_widgets_visible = True + + def _show_psutil_info(self): + """Show information about installing psutil for enhanced monitoring.""" + msg = QtWidgets.QMessageBox(self) + msg.setWindowTitle("Enhanced Resource Monitoring") + msg.setIcon(QtWidgets.QMessageBox.Icon.Information) + msg.setText("Install psutil for detailed resource monitoring") + msg.setInformativeText( + "For detailed CPU, memory, and system resource monitoring, install the psutil package:\n\n" + "pip install psutil\n\n" + "This will enable:\n" + "• Process memory usage in MB and percentage\n" + "• CPU usage percentage\n" + "• System memory statistics\n" + "• Available system memory\n\n" + "Without psutil, only basic cache information is shown." + ) + msg.setStandardButtons(QtWidgets.QMessageBox.StandardButton.Ok) + msg.exec() + def closeEvent(self, event): """Handle application close event with proper cleanup.""" self.cleanup_resources() @@ -877,6 +1025,48 @@ class MainWindow(QtWidgets.QMainWindow): self.state_label.setStyleSheet('font-weight: bold; font-size: 11pt; color: #0055aa;') self.state_label.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft | QtCore.Qt.AlignmentFlag.AlignVCenter) + # Resource usage display + self.resource_group = QtWidgets.QGroupBox('Resource Usage') + self.resource_group.setSizePolicy(QtWidgets.QSizePolicy.Policy.Preferred, QtWidgets.QSizePolicy.Policy.Fixed) + resource_layout = QtWidgets.QGridLayout() + resource_layout.setSpacing(4) + resource_layout.setContentsMargins(6, 6, 6, 6) + + # Toggle button for resource display + self.toggle_resources_btn = QtWidgets.QPushButton('Hide Resources') + self.toggle_resources_btn.setMaximumWidth(100) + self.toggle_resources_btn.setToolTip('Toggle resource usage display. Install psutil for detailed system metrics.') + self.toggle_resources_btn.clicked.connect(self._toggle_resource_display) + resource_layout.addWidget(self.toggle_resources_btn, 0, 2, 1, 1) + + # Memory usage + self.memory_label = QtWidgets.QLabel('Memory: --') + self.memory_label.setStyleSheet('font-size: 9pt; color: #333;') + resource_layout.addWidget(self.memory_label, 0, 0) + + # CPU usage + self.cpu_label = QtWidgets.QLabel('CPU: --') + self.cpu_label.setStyleSheet('font-size: 9pt; color: #333;') + resource_layout.addWidget(self.cpu_label, 0, 1) + + # System memory + self.system_memory_label = QtWidgets.QLabel('System: --') + self.system_memory_label.setStyleSheet('font-size: 9pt; color: #333;') + resource_layout.addWidget(self.system_memory_label, 1, 0) + + # Cache info + self.cache_label = QtWidgets.QLabel('Cache: --') + self.cache_label.setStyleSheet('font-size: 9pt; color: #333;') + resource_layout.addWidget(self.cache_label, 1, 1) + + # Performance info + self.performance_label = QtWidgets.QLabel('Performance: --') + self.performance_label.setStyleSheet('font-size: 9pt; color: #333;') + resource_layout.addWidget(self.performance_label, 2, 0, 1, 3) + + self.resource_group.setLayout(resource_layout) + self.resource_widgets_visible = True + # Main layout layout = QtWidgets.QGridLayout() layout.setHorizontalSpacing(6) @@ -889,6 +1079,10 @@ class MainWindow(QtWidgets.QMainWindow): layout.addWidget(window_group_box, 1, 0, 1, 5) layout.addWidget(steps_group_box, 2, 0, 3, 5) + # Resource usage group + layout.addWidget(self.resource_group, 5, 0, 1, 5) + layout.setRowStretch(5, 0) # Resource group should not stretch vertically + # Start/State row in a group start_state_group = QtWidgets.QGroupBox() start_state_group.setTitle("") @@ -899,8 +1093,8 @@ class MainWindow(QtWidgets.QMainWindow): start_state_layout.addWidget(self.state_label) start_state_layout.addStretch(1) start_state_group.setLayout(start_state_layout) - layout.addWidget(start_state_group, 5, 0, 1, 5) - layout.setRowStretch(5, 0) # Prevent vertical stretch + layout.addWidget(start_state_group, 6, 0, 1, 5) + layout.setRowStretch(6, 0) # Prevent vertical stretch # Set central widget (must be at the end of init_ui) central = QtWidgets.QWidget() diff --git a/requirements.txt b/requirements.txt index 38259df..ead58e3 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,4 +4,5 @@ pyautogui pynput pygetwindow numpy -Pillow \ No newline at end of file +Pillow +psutil # Optional: For enhanced resource monitoring \ No newline at end of file