feat: Add real-time inference results display to deployment UI

- Add result callback mechanism to WorkflowOrchestrator
- Implement result_updated signal in DeploymentWorker
- Create detailed inference results display with timestamps and formatted output
- Support both tuple and dict result formats
- Add auto-scrolling results panel with history management
- Connect pipeline results to Live View tab for real-time monitoring

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Masonmason 2025-07-17 10:22:48 +08:00
parent e97fd7a025
commit e6c9817a98
2 changed files with 62 additions and 0 deletions

View File

@ -31,6 +31,7 @@ class WorkflowOrchestrator:
self.running = False self.running = False
self._stop_event = threading.Event() self._stop_event = threading.Event()
self.frame_callback = None self.frame_callback = None
self.result_callback = None
def start(self): def start(self):
""" """
@ -94,6 +95,12 @@ class WorkflowOrchestrator:
Sets the callback function for frame updates. Sets the callback function for frame updates.
""" """
self.frame_callback = callback self.frame_callback = callback
def set_result_callback(self, callback):
"""
Sets the callback function for inference results.
"""
self.result_callback = callback
def _create_data_source(self) -> Optional[Any]: def _create_data_source(self) -> Optional[Any]:
""" """
@ -157,6 +164,10 @@ class WorkflowOrchestrator:
self.pipeline.pipeline_name, self.pipeline.pipeline_name,
format=self.output_config.get('format', 'json').lower() format=self.output_config.get('format', 'json').lower()
) )
# Also call the result callback if set
if self.result_callback:
self.result_callback(result_dict)
except Exception as e: except Exception as e:
print(f"Error handling result: {e}") print(f"Error handling result: {e}")

View File

@ -65,6 +65,7 @@ class DeploymentWorker(QThread):
deployment_completed = pyqtSignal(bool, str) # success, message deployment_completed = pyqtSignal(bool, str) # success, message
error_occurred = pyqtSignal(str) error_occurred = pyqtSignal(str)
frame_updated = pyqtSignal('PyQt_PyObject') # For live view frame_updated = pyqtSignal('PyQt_PyObject') # For live view
result_updated = pyqtSignal(dict) # For inference results
def __init__(self, pipeline_data: Dict[str, Any]): def __init__(self, pipeline_data: Dict[str, Any]):
super().__init__() super().__init__()
@ -131,6 +132,7 @@ class DeploymentWorker(QThread):
# Create and start the orchestrator # Create and start the orchestrator
self.orchestrator = WorkflowOrchestrator(pipeline, config.input_config, config.output_config) self.orchestrator = WorkflowOrchestrator(pipeline, config.input_config, config.output_config)
self.orchestrator.set_frame_callback(self.frame_updated.emit) self.orchestrator.set_frame_callback(self.frame_updated.emit)
self.orchestrator.set_result_callback(self.result_updated.emit)
self.orchestrator.start() self.orchestrator.start()
self.progress_updated.emit(100, "Pipeline deployed successfully!") self.progress_updated.emit(100, "Pipeline deployed successfully!")
@ -525,6 +527,7 @@ Stage Configurations:
self.deployment_worker.deployment_completed.connect(self.on_deployment_completed) self.deployment_worker.deployment_completed.connect(self.on_deployment_completed)
self.deployment_worker.error_occurred.connect(self.on_deployment_error) self.deployment_worker.error_occurred.connect(self.on_deployment_error)
self.deployment_worker.frame_updated.connect(self.update_live_view) self.deployment_worker.frame_updated.connect(self.update_live_view)
self.deployment_worker.result_updated.connect(self.update_inference_results)
self.deployment_worker.start() self.deployment_worker.start()
@ -634,6 +637,54 @@ Stage Configurations:
except Exception as e: except Exception as e:
print(f"Error updating live view: {e}") print(f"Error updating live view: {e}")
def update_inference_results(self, result_dict):
"""Update the inference results display."""
try:
import json
from datetime import datetime
# Format the results for display
timestamp = datetime.fromtimestamp(result_dict.get('timestamp', 0)).strftime("%H:%M:%S.%f")[:-3]
stage_results = result_dict.get('stage_results', {})
result_text = f"[{timestamp}] Pipeline ID: {result_dict.get('pipeline_id', 'Unknown')}\n"
# Display results from each stage
for stage_id, result in stage_results.items():
result_text += f" {stage_id}:\n"
if isinstance(result, tuple) and len(result) == 2:
# Handle tuple results (probability, result_string)
probability, result_string = result
result_text += f" Result: {result_string}\n"
result_text += f" Probability: {probability:.3f}\n"
elif isinstance(result, dict):
# Handle dict results
for key, value in result.items():
if key == 'probability':
result_text += f" Probability: {value:.3f}\n"
else:
result_text += f" {key}: {value}\n"
else:
result_text += f" {result}\n"
result_text += "-" * 50 + "\n"
# Append to results display (keep last 100 lines)
current_text = self.results_text.toPlainText()
lines = current_text.split('\n')
if len(lines) > 100:
lines = lines[-50:] # Keep last 50 lines
current_text = '\n'.join(lines)
self.results_text.setPlainText(current_text + result_text)
# Auto-scroll to bottom
scrollbar = self.results_text.verticalScrollBar()
scrollbar.setValue(scrollbar.maximum())
except Exception as e:
print(f"Error updating inference results: {e}")
def apply_theme(self): def apply_theme(self):
"""Apply consistent theme to the dialog.""" """Apply consistent theme to the dialog."""
self.setStyleSheet(""" self.setStyleSheet("""