19. Optiland GUI
This document provides an overview of the Optiland Graphical User Interface (GUI), its components, architecture, and guidelines for developers looking to contribute to its development.
19.1. Overview
The Optiland GUI, built with PySide6, provides an interactive way to design, analyze, and manage optical systems using the Optiland backend. It aims to offer a user-friendly experience while exposing the powerful features of the core library. The GUI is designed to be modular, allowing for the addition of new panels and functionalities.
19.2. Key Technologies
PySide6: The official Python bindings for Qt, used for creating the user interface.
Matplotlib: Used for 2D plotting in various panels (e.g., 2D system view, analysis plots).
VTK (Visualization Toolkit): Used for 3D rendering of optical systems in the ViewerPanel. (Optional, GUI can run without it).
19.3. Core Architecture
The GUI’s architecture revolves around a main window that hosts several dockable panels, each dedicated to a specific aspect of optical design or analysis. The OptilandConnector class serves as a crucial intermediary, linking these GUI components to the underlying Optic object from the Optiland backend.
19.4. Key Components
19.4.1. Main Window (main_window.py)
The MainWindow class is the entry point and central hub of the GUI. It uses an ActionManager to create and manage QAction instances, and a PanelManager to handle the creation and layout of all dockable panels. Its responsibilities include:
Menu Bar and Toolbars: Provides standard application menus (File, Edit, View, Help) and quick-action toolbars.
Theme Handling: Supports light and dark themes, loading stylesheets (.qss files) and propagating theme changes to child widgets and plots.
Window Control: Implements a custom title bar for a modern look and feel, handling minimize, maximize, and close operations.
Settings Persistence: Uses QSettings to save and load window geometry and layout configurations.
Dialogs: Manages application-level dialogs like “About” and file open/save.
19.4.2. Optiland Connector (optiland_connector.py)
The OptilandConnector is a vital QObject that acts as a bridge between the GUI and the Optiland backend.
`Optic` Object Management: It holds and manages the instance of the Optic class, which represents the current optical system.
Signal Emission: Emits Qt signals when the optical system is loaded (opticLoaded), modified (opticChanged), or when specific parts like surface data or count change. GUI panels connect to these signals to refresh their views.
Data Access and Modification: Provides methods for GUI components to safely access data from the Optic object (e.g., get_surface_data, get_column_headers) and to modify it (e.g., set_surface_data, add_surface).
Undo/Redo: Delegates undo and redo operations to an UndoRedoManager instance.
File Operations: Handles loading and saving of optical systems in Optiland’s JSON format.
19.4.3. Core Panels
These are the primary QDockWidget or QWidget instances that provide specific functionalities:
Lens Editor (`lens_editor.py`):
Displays optical system data in a table format (similar to a lens data editor in commercial software).
Allows users to view and edit surface parameters like radius, thickness, material, conic constant, and semi-diameter.
Interacts with OptilandConnector to fetch data and commit changes, triggering updates in other relevant panels.
Supports adding and removing surfaces.
Viewer Panel (`viewer_panel.py`):
Provides visual representations of the optical system.
2D View: Uses Matplotlib to render a 2D cross-section of the system, including lens elements and ray paths. Includes basic navigation (pan/zoom) and a settings area to control ray display.
3D View: Uses VTK (if available) to render a 3D model of the system, allowing for interactive rotation, panning, and zooming.
Both views update in response to changes in the Optic object via signals from OptilandConnector.
Analysis Panel (`analysis_panel.py`):
A comprehensive panel for running various optical analyses.
Users can select an analysis type (e.g., Spot Diagram, Ray Fan, MTF).
Dynamically generates a settings UI based on the selected analysis’s parameters.
Executes the analysis using the OptilandConnector to get the current Optic object.
Displays results, typically as Matplotlib plots, in an embedded canvas.
Supports multiple analysis “pages” (tabs or similar) and cloning of analyses.
Includes features like saving/loading analysis settings.
Optimization Panel (`optimization_panel.py`):
Currently a placeholder for future optimization functionalities.
Intended to allow users to define optimization variables, objectives (merit functions), and run optimization routines from the Optiland backend.
System Properties Panel (`system_properties_panel.py`):
Manages system-wide settings that are not tied to individual surfaces.
Uses a navigation tree to switch between different property editors (Aperture, Fields, Wavelengths, etc.).
Aperture Editor: Configures the system aperture (e.g., Entrance Pupil Diameter, F-number).
Fields Editor: Defines field points (e.g., angle or object height) and their vignetting factors.
Wavelengths Editor: Manages the wavelengths used for analysis and their weights, including setting the primary wavelength.
Changes made here are applied to the Optic object through the OptilandConnector.
19.4.4. Key Widgets
Sidebar Widget (`widgets/sidebar.py`):
Provides main navigation between different functional areas of the GUI (e.g., Design, Analysis, Scripts).
Can be collapsed to save space, showing only icons.
Emits a menuSelected signal when a button is clicked, which the MainWindow uses to raise or show relevant panels.
Python Terminal (`widgets/python_terminal.py`):
An embedded IPython terminal.
Provides direct access to the OptilandConnector instance (via the connector variable), allowing advanced users to interact with the optical system programmatically.
Commands executed in the terminal can trigger GUI updates if they modify the Optic object.
Command Palette (`widgets/command_palette.py`):
A floating panel that allows users to quickly search for and execute GUI commands.
Similar to the command palette found in VS Code, it provides a centralized interface for discovering and running various tools, analyses, and layout operations.
Triggered from the MainWindow via the Ctrl+K shortcut.
19.4.5. Styling and Resources
Qt StyleSheets (`.qss`): Themes (e.g., dark_theme.qss, light_theme.qss) are defined using QSS, Qt’s CSS-like styling language. These are located in optiland_gui/resources/styles/.
Resource Files (`resources.qrc`, `resources_rc.py`): Icons and other assets are managed using Qt’s resource system. resources.qrc is an XML file defining resources, which is compiled into resources_rc.py using pyside6-rcc. This allows resources to be bundled with the application.
Plot Styling (`gui_plot_utils.py`): Contains utility functions to apply consistent Matplotlib styles that match the selected GUI theme.
19.5. Running the GUI
Once Optiland is installed, you can launch the GUI by simply typing the following command in your terminal or console:
optiland
This command is a convenient shortcut to the main GUI script. Alternatively, you can run the GUI module directly using Python’s -m flag, which can be useful for development:
python -m optiland_gui.run_gui
19.6. Contributing to the GUI
Developing for the Optiland GUI generally involves the following:
Understanding PySide6: Familiarity with Qt concepts like signals and slots, layouts, widgets, and the event loop is essential.
Interacting with `OptilandConnector`:
When creating a new panel that needs to display or modify optical data, it should take an OptilandConnector instance in its constructor.
Connect to relevant signals from the OptilandConnector (e.g., opticChanged, surfaceDataChanged) to update the panel’s display when the underlying data changes.
Use the connector’s methods to fetch data (e.g., get_optic(), get_surface_data()) and to apply changes (e.g., set_surface_data(), or by directly modifying the Optic object obtained from get_optic() and then calling connector.opticChanged.emit() if the connector doesn’t automatically detect the change for undo/redo purposes or specific signal emission).
Designing UI:
Use Qt Designer (optional) or create UI elements programmatically.
Employ layouts (QVBoxLayout, QHBoxLayout, QGridLayout, QFormLayout) for responsive and well-organized UIs.
Follow existing patterns for styling and theming. New widgets should respect the application’s theme.
Undo/Redo: For actions that modify the optical system, ensure they are compatible with the UndoRedoManager. This usually involves capturing the state of the Optic object before a change and adding it to the undo stack via OptilandConnector._undo_redo_manager.add_state(old_optic_state_dict).
Modularity: Aim to keep panels self-contained and focused on specific functionalities.
Example Workflow for Adding a New Panel:
Create a new Python file for your panel (e.g., my_new_panel.py).
Define a QWidget or QDockWidget subclass.
In its __init__, accept an OptilandConnector instance.
Build the UI for your panel.
Connect to signals from OptilandConnector to populate/update your panel.
Implement logic to handle user interactions and, if necessary, modify the Optic object via the connector.
In main_window.py:
Instantiate your new panel.
Add it as a QDockWidget or integrate it into the UI as appropriate.
Optionally, add menu actions or sidebar buttons to control its visibility or interaction.
By following these guidelines and referring to existing panels as examples, developers can effectively contribute to and extend the Optiland GUI.