/
devsec
/
anothertap
Обзор
Документация
Войти
/
devsec
/
anothertap
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
src/gui/main_window.py
443 строки
16 KB
devsec
init project
23 авг 2025, 21:01
23 авг 2025, 21:01
5b0e5af
Код
Авторство
О чём код?
""" Main application window for the Test Automation Platform """ import sys import logging from pathlib import Path from PySide6.QtWidgets import ( QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, QTabWidget, QMenuBar, QStatusBar, QToolBar, QMessageBox, QFileDialog, QSplitter, QDockWidget, QTextEdit, QLabel ) from PySide6.QtCore import Qt, QTimer, Signal, QThread from PySide6.QtGui import QIcon, QFont, QAction from services.device_manager import DeviceManager from services.test_plan_manager import TestPlanManager from gui.device_manager_widget import DeviceManagerWidget from gui.test_plan_editor_widget import TestPlanEditorWidget from gui.execution_widget import ExecutionWidget class MainWindow(QMainWindow): """ Main application window """ def __init__(self): super().__init__() # Set up logging self.setup_logging() self.logger = logging.getLogger(__name__) # Initialize services self.device_manager = DeviceManager() self.test_plan_manager = TestPlanManager() # Set up UI self.setup_ui() self.setup_menus() self.setup_toolbars() self.setup_status_bar() self.setup_connections() # Set up periodic updates self.update_timer = QTimer() self.update_timer.timeout.connect(self.update_status) self.update_timer.start(1000) # Update every second self.logger.info("Application started") def setup_ui(self): """Set up the main user interface""" self.setWindowTitle("Test Automation Platform") self.setMinimumSize(1200, 800) self.resize(1400, 900) # Create central widget with tab widget central_widget = QWidget() self.setCentralWidget(central_widget) # Main layout main_layout = QVBoxLayout(central_widget) # Create tab widget self.tab_widget = QTabWidget() main_layout.addWidget(self.tab_widget) # Create main tabs self.setup_tabs() # Create dockable panels self.setup_dock_widgets() def setup_tabs(self): """Set up the main tabs""" # Device Manager Tab self.device_manager_widget = DeviceManagerWidget(self.device_manager) self.tab_widget.addTab(self.device_manager_widget, "Devices") # Test Plan Editor Tab self.test_plan_editor_widget = TestPlanEditorWidget( self.test_plan_manager, self.device_manager ) self.tab_widget.addTab(self.test_plan_editor_widget, "Test Plans") # Execution Tab self.execution_widget = ExecutionWidget( self.test_plan_manager, self.device_manager ) self.tab_widget.addTab(self.execution_widget, "Execution") def setup_dock_widgets(self): """Set up dockable widgets""" # Log viewer dock self.log_dock = QDockWidget("Log", self) self.log_text = QTextEdit() self.log_text.setReadOnly(True) # Note: setMaximumBlockCount is not available in PySide6 font = QFont("Consolas", 9) font.setStyleHint(QFont.Monospace) self.log_text.setFont(font) self.log_dock.setWidget(self.log_text) self.addDockWidget(Qt.BottomDockWidgetArea, self.log_dock) # Device status dock self.device_status_dock = QDockWidget("Device Status", self) self.device_status_widget = QWidget() self.device_status_layout = QVBoxLayout(self.device_status_widget) self.device_status_label = QLabel("No devices connected") self.device_status_layout.addWidget(self.device_status_label) self.device_status_dock.setWidget(self.device_status_widget) self.addDockWidget(Qt.RightDockWidgetArea, self.device_status_dock) def setup_menus(self): """Set up the menu bar""" menubar = self.menuBar() # File menu file_menu = menubar.addMenu("File") # New test plan new_plan_action = QAction("New Test Plan", self) new_plan_action.setShortcut("Ctrl+N") new_plan_action.triggered.connect(self.new_test_plan) file_menu.addAction(new_plan_action) # Open test plan open_plan_action = QAction("Open Test Plan...", self) open_plan_action.setShortcut("Ctrl+O") open_plan_action.triggered.connect(self.open_test_plan) file_menu.addAction(open_plan_action) # Save test plan save_plan_action = QAction("Save Test Plan", self) save_plan_action.setShortcut("Ctrl+S") save_plan_action.triggered.connect(self.save_test_plan) file_menu.addAction(save_plan_action) file_menu.addSeparator() # Import/Export import_devices_action = QAction("Import Devices...", self) import_devices_action.triggered.connect(self.import_devices) file_menu.addAction(import_devices_action) export_devices_action = QAction("Export Devices...", self) export_devices_action.triggered.connect(self.export_devices) file_menu.addAction(export_devices_action) file_menu.addSeparator() # Exit exit_action = QAction("Exit", self) exit_action.setShortcut("Ctrl+Q") exit_action.triggered.connect(self.close) file_menu.addAction(exit_action) # Tools menu tools_menu = menubar.addMenu("Tools") # Discover devices discover_action = QAction("Discover Devices", self) discover_action.triggered.connect(self.discover_devices) tools_menu.addAction(discover_action) # Disconnect all devices disconnect_all_action = QAction("Disconnect All Devices", self) disconnect_all_action.triggered.connect(self.disconnect_all_devices) tools_menu.addAction(disconnect_all_action) # View menu view_menu = menubar.addMenu("View") # Toggle docks view_menu.addAction(self.log_dock.toggleViewAction()) view_menu.addAction(self.device_status_dock.toggleViewAction()) # Help menu help_menu = menubar.addMenu("Help") about_action = QAction("About", self) about_action.triggered.connect(self.show_about) help_menu.addAction(about_action) def setup_toolbars(self): """Set up toolbars""" # Main toolbar main_toolbar = self.addToolBar("Main") # New test plan new_plan_action = QAction("New Plan", self) new_plan_action.triggered.connect(self.new_test_plan) main_toolbar.addAction(new_plan_action) # Save save_action = QAction("Save", self) save_action.triggered.connect(self.save_test_plan) main_toolbar.addAction(save_action) main_toolbar.addSeparator() # Run test plan run_action = QAction("Run", self) run_action.triggered.connect(self.run_current_test_plan) main_toolbar.addAction(run_action) # Stop execution stop_action = QAction("Stop", self) stop_action.triggered.connect(self.stop_execution) main_toolbar.addAction(stop_action) def setup_status_bar(self): """Set up the status bar""" self.status_bar = self.statusBar() self.status_bar.showMessage("Ready") # Add permanent widgets self.connection_status_label = QLabel("Devices: 0 connected") self.status_bar.addPermanentWidget(self.connection_status_label) def setup_connections(self): """Set up signal connections between widgets""" # Connect device manager signals self.device_manager_widget.device_connected.connect(self.on_device_connected) self.device_manager_widget.device_disconnected.connect(self.on_device_disconnected) # Connect test plan editor signals self.test_plan_editor_widget.test_plan_selected.connect( self.execution_widget.load_test_plan ) # Connect execution widget signals self.execution_widget.execution_started.connect(self.on_execution_started) self.execution_widget.execution_finished.connect(self.on_execution_finished) def setup_logging(self): """Set up logging configuration""" # Create logs directory logs_dir = Path("logs") logs_dir.mkdir(exist_ok=True) # Configure logging logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', handlers=[ logging.FileHandler(logs_dir / "application.log"), logging.StreamHandler(sys.stdout) ] ) # Add custom handler for GUI log display self.log_handler = GuiLogHandler(self) logging.getLogger().addHandler(self.log_handler) def new_test_plan(self): """Create a new test plan""" self.tab_widget.setCurrentWidget(self.test_plan_editor_widget) self.test_plan_editor_widget.new_test_plan() def open_test_plan(self): """Open a test plan from file""" file_path, _ = QFileDialog.getOpenFileName( self, "Open Test Plan", "", "JSON Files (*.json);;All Files (*)" ) if file_path: plan = self.test_plan_manager.import_test_plan(file_path) if plan: self.tab_widget.setCurrentWidget(self.test_plan_editor_widget) # Load the plan in editor (would need method implementation) self.status_bar.showMessage(f"Opened test plan: {plan.name}") else: QMessageBox.warning(self, "Error", "Failed to open test plan") def save_test_plan(self): """Save current test plan""" current_plan = self.test_plan_editor_widget.get_current_test_plan() if current_plan: file_path, _ = QFileDialog.getSaveFileName( self, "Save Test Plan", f"{current_plan.name}.json", "JSON Files (*.json);;All Files (*)" ) if file_path: if self.test_plan_manager.export_test_plan(current_plan.id, file_path): self.status_bar.showMessage(f"Saved test plan: {current_plan.name}") else: QMessageBox.warning(self, "Error", "Failed to save test plan") else: QMessageBox.information(self, "Info", "No test plan selected") def import_devices(self): """Import devices from file""" file_path, _ = QFileDialog.getOpenFileName( self, "Import Devices", "", "JSON Files (*.json);;All Files (*)" ) if file_path: if self.device_manager.import_devices(file_path): self.device_manager_widget.refresh_devices() self.status_bar.showMessage("Devices imported successfully") else: QMessageBox.warning(self, "Error", "Failed to import devices") def export_devices(self): """Export devices to file""" file_path, _ = QFileDialog.getSaveFileName( self, "Export Devices", "devices.json", "JSON Files (*.json);;All Files (*)" ) if file_path: if self.device_manager.export_devices(file_path): self.status_bar.showMessage("Devices exported successfully") else: QMessageBox.warning(self, "Error", "Failed to export devices") def discover_devices(self): """Discover available devices""" self.tab_widget.setCurrentWidget(self.device_manager_widget) self.device_manager_widget.discover_devices() def disconnect_all_devices(self): """Disconnect from all devices""" self.device_manager.disconnect_all() self.status_bar.showMessage("All devices disconnected") def run_current_test_plan(self): """Run the currently selected test plan""" current_plan = self.test_plan_editor_widget.get_current_test_plan() if current_plan: self.tab_widget.setCurrentWidget(self.execution_widget) self.execution_widget.load_test_plan(current_plan.id) self.execution_widget.start_execution() else: QMessageBox.information(self, "Info", "No test plan selected") def stop_execution(self): """Stop test plan execution""" self.execution_widget.stop_execution() def show_about(self): """Show about dialog""" QMessageBox.about( self, "About Test Automation Platform", "Test Automation Platform v1.0.0\n\n" "A Python-based test automation platform similar to OpenTAP\n" "for controlling measurement devices via SCPI commands.\n\n" "Built with PySide6 and PyVISA" ) def on_device_connected(self, device_id): """Handle device connection""" device = self.device_manager.get_device(device_id) if device: self.status_bar.showMessage(f"Connected to {device.display_name}") def on_device_disconnected(self, device_id): """Handle device disconnection""" device = self.device_manager.get_device(device_id) if device: self.status_bar.showMessage(f"Disconnected from {device.display_name}") def on_execution_started(self, plan_id): """Handle execution start""" plan = self.test_plan_manager.get_test_plan(plan_id) if plan: self.status_bar.showMessage(f"Executing: {plan.display_name}") def on_execution_finished(self, plan_id, success): """Handle execution completion""" plan = self.test_plan_manager.get_test_plan(plan_id) if plan: status = "completed successfully" if success else "failed" self.status_bar.showMessage(f"Execution {status}: {plan.display_name}") def update_status(self): """Update status information""" # Update device connection status connected_devices = self.device_manager.get_connected_devices() total_devices = len(self.device_manager.get_all_devices()) self.connection_status_label.setText( f"Devices: {len(connected_devices)}/{total_devices} connected" ) # Update device status in dock if connected_devices: status_text = "Connected devices:\n" for device in connected_devices: status_text += f"• {device.display_name}\n" else: status_text = "No devices connected" self.device_status_label.setText(status_text) def append_log(self, message): """Append message to log display""" self.log_text.append(message) def closeEvent(self, event): """Handle application close""" # Disconnect all devices self.device_manager.disconnect_all() # Stop any running executions self.execution_widget.stop_execution() self.logger.info("Application closing") event.accept() class GuiLogHandler(logging.Handler): """Custom log handler for displaying logs in GUI""" def __init__(self, main_window): super().__init__() self.main_window = main_window def emit(self, record): """Emit log record to GUI""" try: msg = self.format(record) # Use QTimer to ensure GUI update happens in main thread QTimer.singleShot(0, lambda: self.main_window.append_log(msg)) except Exception: pass