magscope.ui.controls

Contents

magscope.ui.controls#

Classes#

ControlPanelBase

MatplotlibCleanupMixin

ResponsivePlotCanvas

Figure canvas that grows taller when constrained to a narrow panel.

HelpPanel

Clickable panel that links to the MagScope documentation.

MotorsPlaceholderPanel

Panel shown when no user hardware managers are configured.

MagScopeSettingsPanel

Allow importing, exporting, and editing MagScope configuration values.

AcquisitionPanel

BeadSelectionPanel

CameraPanel

HistogramPanel

PlotSettingsPanel

AllanDeviationPanel

ProfilePanel

TrackingOptionsPanel

PreferencesDialog

Modal dialog for global MagScope preferences.

ScriptPanel

StatusPanel

_LockInfoButton

Compact info icon that shows a tooltip on hover.

_LockStatusBadge

Compact color-coded status label with rounded background.

_LockActivityIndicator

Small circular ring indicator that fills over the lock interval and resets on correction.

_LockNumberInput

Compact row: label | spinbox | unit suffix.

XYLockPanel

ZLockPanel

ZLUTGenerationPanel

ZLUTGenerationSetupDialog

ZLUTSweepPreviewWidget

ZLUTGenerationDialog

CurrentZLUTDialog

ZLUTPanel

Functions#

_panel_control_target(, description, keywords, ...)

_preference_widget_targets(...)

has_tweezepy_support(→ bool)

load_tweezepy_avar(→ tuple[callable | None, str | None])

Module Contents#

magscope.ui.controls._panel_control_target(label: str, panel_id: str, widget_attr: str, *, context: str, aliases: tuple[str, Ellipsis] = (), description: str = '', keywords: tuple[str, Ellipsis] = ()) magscope.ui.search.PanelControlTarget[source]#
magscope.ui.controls._preference_widget_targets(definitions: tuple[tuple[str, str, tuple[str, Ellipsis]], Ellipsis], *, tab_name: str, context: str) list[magscope.ui.search.SearchTarget][source]#
class magscope.ui.controls.ControlPanelBase(manager: magscope.ui.ui.UIManager, title: str, collapsed_by_default: bool = False, collapsible: bool = False)[source]#

Bases: PyQt6.QtWidgets.QWidget

manager: magscope.ui.ui.UIManager[source]#
groupbox: magscope.ui.widgets.CollapsibleGroupBox | None = None[source]#
_content_layout: PyQt6.QtWidgets.QBoxLayout | None = None[source]#
set_title(text: str) None[source]#
setLayout(layout: PyQt6.QtWidgets.QBoxLayout) None[source]#
layout() PyQt6.QtWidgets.QBoxLayout[source]#
set_highlighted(enabled: bool) None[source]#
class magscope.ui.controls.MatplotlibCleanupMixin[source]#
_init_matplotlib_cleanup() None[source]#
_dispose_matplotlib(*_args: object) None[source]#
closeEvent(event) None[source]#
class magscope.ui.controls.ResponsivePlotCanvas(figure: matplotlib.figure.Figure, *, minimum_height: int = 210, maximum_height: int | None = 235, height_for_width: float = 0.72)[source]#

Bases: matplotlib.backends.backend_qtagg.FigureCanvasQTAgg

Figure canvas that grows taller when constrained to a narrow panel.

_minimum_height = 210[source]#
_maximum_height = 235[source]#
_height_for_width = 0.72[source]#
_preferred_height = 210[source]#
_apply_preferred_height(height: int) None[source]#
_update_preferred_height(width: int) None[source]#
resizeEvent(event)[source]#
sizeHint() PyQt6.QtCore.QSize[source]#
class magscope.ui.controls.HelpPanel(manager: magscope.ui.ui.UIManager)[source]#

Bases: PyQt6.QtWidgets.QFrame

Clickable panel that links to the MagScope documentation.

HELP_URL[source]#
manager[source]#
title_label[source]#
description_label[source]#
_is_hovered = False[source]#
mouseReleaseEvent(event)[source]#
enterEvent(event)[source]#
leaveEvent(event)[source]#
_apply_styles()[source]#
class magscope.ui.controls.MotorsPlaceholderPanel(manager: magscope.ui.ui.UIManager)[source]#

Bases: ControlPanelBase

Panel shown when no user hardware managers are configured.

HELP_URL[source]#
class magscope.ui.controls.MagScopeSettingsPanel(manager: magscope.ui.ui.UIManager, *, collapsible: bool = True, file_status_label: PyQt6.QtWidgets.QLabel | None = None)[source]#

Bases: PyQt6.QtWidgets.QWidget

Allow importing, exporting, and editing MagScope configuration values.

_SETTING_GROUPS: tuple[tuple[str, tuple[str, Ellipsis]], Ellipsis] = (('Imaging', ('ROI', 'magnification')), ('Data Buffers', ('tracks max datapoints', 'video buffer...[source]#
manager[source]#
_current_settings[source]#
_setting_inputs: dict[str, PyQt6.QtWidgets.QLineEdit][source]#
_setting_value_labels: dict[str, PyQt6.QtWidgets.QLabel][source]#
static search_targets() list[magscope.ui.search.SearchTarget][source]#
_show_error(message: str) None[source]#
_push_settings(settings: magscope.settings.MagScopeSettings) None[source]#
_refresh_fields() None[source]#
_apply_setting_from_input(key: str) None[source]#
reset_defaults() None[source]#
_build_setting_group(title: str, keys: tuple[str, Ellipsis]) PyQt6.QtWidgets.QWidget[source]#
_update_saved_label_for_input(key: str) None[source]#
class magscope.ui.controls.AcquisitionPanel(manager: magscope.ui.ui.UIManager)[source]#

Bases: ControlPanelBase

NO_DIRECTORY_SELECTED_TEXT = 'No save folder selected'[source]#
acquisition_on_checkbox[source]#
acquisition_mode_combobox[source]#
acquisition_dir_on_checkbox[source]#
acquisition_dir_textedit[source]#
acquisition_dir_button[source]#
set_acquisition_dir_text(path: str | None) None[source]#
callback_acquisition_on()[source]#
callback_acquisition_dir_on()[source]#
callback_acquisition_mode()[source]#
callback_acquisition_dir()[source]#
update_save_highlight(should_save: bool) None[source]#
search_targets() list[magscope.ui.search.SearchTarget][source]#
class magscope.ui.controls.BeadSelectionPanel(manager: magscope.ui.ui.UIManager)[source]#

Bases: ControlPanelBase

next_bead_id_label[source]#
reset_id_button[source]#
roi_size_label[source]#
clear_button[source]#
search_targets() list[magscope.ui.search.SearchTarget][source]#
update_next_bead_id_label(next_bead_id: int) None[source]#
class magscope.ui.controls.CameraPanel(manager: magscope.ui.ui.UIManager)[source]#

Bases: ControlPanelBase

_last_settings_update: datetime.datetime | None = None[source]#
settings[source]#
refresh_button[source]#
last_update_label[source]#
callback_refresh()[source]#
callback_set_camera_setting(name)[source]#
update_camera_setting(name: str, value: str)[source]#
_format_last_update_text() str[source]#
class magscope.ui.controls.HistogramPanel(manager: magscope.ui.ui.UIManager)[source]#

Bases: MatplotlibCleanupMixin, ControlPanelBase

update_interval: float = 1[source]#
_update_last_time: float = 0[source]#
enable_checkbox[source]#
only_beads_checkbox[source]#
n_bins = 256[source]#
figure[source]#
canvas[source]#
axes[source]#
enabled_callback(enabled: bool) None[source]#
_groupbox_toggled(expanded: bool) None[source]#
_apply_enabled_state(enabled: bool) None[source]#
update_plot(data)[source]#
clear()[source]#
class magscope.ui.controls.PlotSettingsPanel(manager: magscope.ui.ui.UIManager)[source]#

Bases: ControlPanelBase

selected_bead[source]#
reference_bead[source]#
limits: dict[str, tuple[PyQt6.QtWidgets.QLineEdit, PyQt6.QtWidgets.QLineEdit]][source]#
grid_layout[source]#
time_mode[source]#
time_label[source]#
time_limits_absolute[source]#
time_relative_window[source]#
time_inputs_stack[source]#
beads_in_view_on[source]#
beads_in_view_count[source]#
beads_in_view_marker_size[source]#
search_targets() list[magscope.ui.search.SearchTarget][source]#
selected_bead_callback(value)[source]#
reference_bead_callback(value)[source]#
time_mode_callback(value: str)[source]#
relative_time_window_callback(_value)[source]#
limits_callback(_)[source]#
beads_in_view_on_callback()[source]#
beads_in_view_count_callback()[source]#
beads_in_view_marker_size_callback()[source]#
magscope.ui.controls.has_tweezepy_support() bool[source]#
magscope.ui.controls.load_tweezepy_avar() tuple[callable | None, str | None][source]#
class magscope.ui.controls.AllanDeviationPanel(manager: magscope.ui.ui.UIManager)[source]#

Bases: MatplotlibCleanupMixin, ControlPanelBase

_SETTINGS_GROUP = 'AllanDeviationPanel'[source]#
refresh_button[source]#
history_window[source]#
history_window_hint[source]#
taus_mode[source]#
figure[source]#
canvas[source]#
axes[source]#
status_label[source]#
_settings() PyQt6.QtCore.QSettings[source]#
_setting_key(name: str) str[source]#
_load_setting(name: str, default: str) str[source]#
_persist_controls() None[source]#
_configure_axes() None[source]#
refresh_plot() None[source]#
clear(message: str = 'Click Refresh to compute Allan deviation') None[source]#
static _parse_window_seconds(value: str) float | None[source]#
static _estimate_sampling_rate(timestamps: numpy.ndarray) float | None[source]#
static _apply_history_window(timestamps: numpy.ndarray, values: numpy.ndarray, window_seconds: float) tuple[numpy.ndarray, numpy.ndarray][source]#
static _extract_axis_series(tracks: numpy.ndarray, *, axis_name: str, selected_bead: int | None, reference_bead: int | None) tuple[numpy.ndarray, numpy.ndarray][source]#
class magscope.ui.controls.ProfilePanel(manager: magscope.ui.ui.UIManager)[source]#

Bases: MatplotlibCleanupMixin, ControlPanelBase

enable[source]#
selected_bead_label[source]#
profile_length_label[source]#
figure[source]#
canvas[source]#
axes[source]#
enabled_callback(enabled: bool) None[source]#
_groupbox_toggled(expanded: bool) None[source]#
_apply_enabled_state(enabled: bool) None[source]#
update_plot()[source]#
clear()[source]#
class magscope.ui.controls.TrackingOptionsPanel(manager: magscope.ui.ui.UIManager, *, collapsible: bool = True)[source]#

Bases: ControlPanelBase

_current_options: dict[str, Any][source]#
_updating_fields = False[source]#
background_combo[source]#
iterations[source]#
line_ratio[source]#
n_local[source]#
use_fft[source]#
fft_oversample[source]#
fft_rmin[source]#
fft_rmax[source]#
fft_gaus_factor[source]#
radial_oversample[source]#
lookup_n_local[source]#
static _build_preferences_group(title: str, parent: PyQt6.QtWidgets.QWidget) tuple[PyQt6.QtWidgets.QWidget, PyQt6.QtWidgets.QVBoxLayout][source]#
static _configure_lineedit_row(widget: magscope.ui.widgets.LabeledLineEditWithValue) None[source]#
static _configure_checkbox_row(widget: magscope.ui.widgets.LabeledCheckbox) None[source]#
static search_targets() list[magscope.ui.search.SearchTarget][source]#
_option_line_edits() tuple[magscope.ui.widgets.LabeledLineEditWithValue, Ellipsis][source]#
_update_value_labels() None[source]#
_sync_fft_enabled_state() None[source]#
_use_fft_changed(value: bool) None[source]#
_set_options(options: dict[str, Any], message: str | None = None, *, populate_inputs: bool = False) None[source]#
_populate_inputs_from_options() None[source]#
_load_options_from_mapping(loaded: Any) dict[str, Any][source]#
_options_from_inputs() dict[str, Any][source]#
_apply_options_from_inputs() None[source]#
reset_defaults() None[source]#
class magscope.ui.controls.PreferencesDialog(manager: magscope.ui.ui.UIManager)[source]#

Bases: PyQt6.QtWidgets.QDialog

Modal dialog for global MagScope preferences.

_SIDEBAR_SECTIONS: tuple[tuple[str, str], Ellipsis] = (('tune', 'MagScope'), ('ads_click', 'Tracking'), ('palette', 'Appearance/Layout'))[source]#
manager[source]#
load_preferences_button[source]#
save_preferences_button[source]#
reset_all_preferences_button[source]#
preferences_file_status[source]#
settings_panel[source]#
settings_scroll[source]#
tracking_options_panel[source]#
tracking_scroll[source]#
appearance_layout_tab[source]#
appearance_scroll[source]#
stack[source]#
reset_section_button[source]#
sidebar[source]#
static _sidebar_selection_background(accent: str) str[source]#
_apply_preferences_style(accent: str) None[source]#
_refresh_sidebar_icons(accent: str | None = None) None[source]#
_on_load_preferences_clicked() None[source]#
_on_save_preferences_clicked() None[source]#
_on_reset_all_preferences_clicked() None[source]#
_set_preferences_file_status(message: str) None[source]#
_create_appearance_layout_tab() PyQt6.QtWidgets.QWidget[source]#
_choose_accent_color() None[source]#
_update_accent_color_swatch(color: str) None[source]#
_apply_accent_color_setting() None[source]#
_apply_live_plot_progress_indicator_setting(checked: bool) None[source]#
_scrollable_tab(widget: PyQt6.QtWidgets.QWidget) PyQt6.QtWidgets.QScrollArea[source]#
static _make_material_symbol_icon(font: PyQt6.QtGui.QFont, text: str, color: str = '#888888', size: int = 16) PyQt6.QtGui.QIcon[source]#
_on_sidebar_selection(index: int) None[source]#
_on_reset_current_section() None[source]#
_stack_index_for_scroll(scroll: PyQt6.QtWidgets.QScrollArea) int[source]#
reveal_setting(setting_key: str) None[source]#
reveal_magscope_setting(setting_key: str) None[source]#
reveal_tracking_option(widget_attr: str) None[source]#
reveal_widget(tab_name: str, widget_attr: str) None[source]#
_reveal_widget(scroll: PyQt6.QtWidgets.QScrollArea, widget: PyQt6.QtWidgets.QWidget) None[source]#
_on_reset_appearance_tab_clicked() None[source]#
_reset_appearance_layout(*, reset_accent: bool) None[source]#
class magscope.ui.controls.ScriptPanel(manager: magscope.ui.ui.UIManager)[source]#

Bases: ControlPanelBase

NO_SCRIPT_SELECTED_TEXT = 'No script loaded'[source]#
status_prefix = 'Status'[source]#
status_label[source]#
step_position_label[source]#
step_description_label[source]#
button_layout[source]#
load_button[source]#
start_button[source]#
pause_button[source]#
filepath_textedit[source]#
update_status(status: magscope.scripting.ScriptStatus)[source]#
update_step(current_step: int | None, total_steps: int, description: str | None)[source]#
callback_load()[source]#
callback_start()[source]#
callback_pause()[source]#
class magscope.ui.controls.StatusPanel(manager: magscope.ui.ui.UIManager)[source]#

Bases: ControlPanelBase

dot_count = 0[source]#
display_rate_status[source]#
video_processors_status[source]#
video_buffer_size_status[source]#
video_buffer_status[source]#
video_buffer_status_bar[source]#
video_buffer_purge_label[source]#
update_display_rate(text)[source]#
update_video_processors_status(status_text: str)[source]#
update_video_buffer_status(status_text: str)[source]#
_update_video_buffer_size_label() None[source]#
update_video_buffer_purge(timestamp: float)[source]#
class magscope.ui.controls._LockInfoButton(tooltip_text: str, parent: PyQt6.QtWidgets.QWidget | None = None)[source]#

Bases: PyQt6.QtWidgets.QToolButton

Compact info icon that shows a tooltip on hover.

class magscope.ui.controls._LockStatusBadge(text: str = '', parent: PyQt6.QtWidgets.QWidget | None = None)[source]#

Bases: PyQt6.QtWidgets.QLabel

Compact color-coded status label with rounded background.

_state = ''[source]#
set_state(state: str) None[source]#
_apply_style() None[source]#
class magscope.ui.controls._LockActivityIndicator(parent: PyQt6.QtWidgets.QWidget | None = None)[source]#

Bases: PyQt6.QtWidgets.QWidget

Small circular ring indicator that fills over the lock interval and resets on correction.

_SIZE = 14[source]#
_RING_WIDTH = 2[source]#
_BACKGROUND_COLOR = '#666666'[source]#
_MAXIMUM = 100[source]#
_active = False[source]#
_progress = 0[source]#
_cycle_seconds = 10.0[source]#
_flash_mode = False[source]#
_timer[source]#
set_cycle_duration(seconds: float) None[source]#
reset() None[source]#
set_active(active: bool) None[source]#
trigger_once_flash() None[source]#
_recalc_interval() None[source]#
_tick() None[source]#
paintEvent(_event) None[source]#
class magscope.ui.controls._LockNumberInput(label_text: str, default: float | int, unit: str, *, is_int: bool = False, minimum: float = 0.0, maximum: float = 999999.0, decimals: int = 1, callback: callable | None = None, show_unit_label: bool = True, parent: PyQt6.QtWidgets.QWidget | None = None)[source]#

Bases: PyQt6.QtWidgets.QWidget

Compact row: label | spinbox | unit suffix.

label[source]#
unit_label[source]#
value_label[source]#
property lineedit[source]#
class magscope.ui.controls.XYLockPanel(manager: magscope.ui.ui.UIManager)[source]#

Bases: ControlPanelBase

_activity_indicator[source]#
once_button[source]#
enabled[source]#
interval[source]#
max[source]#
window[source]#
notify_correction() None[source]#
_update_badge() None[source]#
search_targets() list[magscope.ui.search.SearchTarget][source]#
enabled_callback()[source]#
once_callback()[source]#
interval_callback()[source]#
max_callback()[source]#
window_callback()[source]#
update_enabled(value: bool)[source]#
update_interval(value: float)[source]#
update_max(value: float)[source]#
update_window(value: int)[source]#
class magscope.ui.controls.ZLockPanel(manager: magscope.ui.ui.UIManager)[source]#

Bases: ControlPanelBase

_activity_indicator[source]#
_warning_label[source]#
_once_button[source]#
enabled[source]#
_bead_combo[source]#
bead[source]#
_target_spinbox[source]#
_target_unit_label[source]#
target[source]#
interval[source]#
max[source]#
window[source]#
_target_is_set = False[source]#
_refresh_bead_combo() None[source]#
_bead_combo_callback(text: str) None[source]#
_target_spinbox_callback() None[source]#
_once_callback() None[source]#
_update_badge() None[source]#
search_targets() list[magscope.ui.search.SearchTarget][source]#
enabled_callback()[source]#
interval_callback()[source]#
max_callback()[source]#
window_callback()[source]#
update_enabled(value: bool)[source]#
update_bead(value: int)[source]#
_has_focus_motor() bool[source]#
_set_controls_enabled(enabled: bool) None[source]#
update_target(value: float)[source]#
update_interval(value: float)[source]#
update_max(value: float)[source]#
update_window(value: int)[source]#
class magscope.ui.controls.ZLUTGenerationPanel(manager: magscope.ui.ui.UIManager)[source]#

Bases: ControlPanelBase

roi_size_label[source]#
start_input[source]#
step_input[source]#
stop_input[source]#
measurements_input[source]#
generate_button[source]#
search_targets() list[magscope.ui.search.SearchTarget][source]#
generate_callback()[source]#
update_state(status: str, detail: str | None = None, *, running: bool = False, can_cancel: bool = False, phase: str = 'idle') None[source]#
update_progress(current_step: int, total_steps: int, capture_count: int, capture_capacity: int, motor_z_value: float | None = None) None[source]#
class magscope.ui.controls.ZLUTGenerationSetupDialog(parent: PyQt6.QtWidgets.QWidget | None = None, *, roi_size: int, default_measurements: int)[source]#

Bases: PyQt6.QtWidgets.QDialog

start_input[source]#
step_input[source]#
stop_input[source]#
measurements_input[source]#
cancel_button[source]#
generate_button[source]#
_values: tuple[float, float, float, int] | None = None[source]#
property values: tuple[float, float, float, int] | None[source]#
_accept_if_valid() None[source]#
class magscope.ui.controls.ZLUTSweepPreviewWidget(parent: PyQt6.QtWidgets.QWidget | None = None)[source]#

Bases: MatplotlibCleanupMixin, PyQt6.QtWidgets.QWidget

_STATE_LABELS[source]#
summary_label[source]#
_preview_cmap[source]#
figure[source]#
canvas[source]#
axes[source]#
_image[source]#
clear(message: str = 'Waiting for Z-LUT sweep data...') None[source]#
update_preview(*, state: int, count: int, capacity: int, n_steps: int, n_beads: int, profiles_per_bead: int, profile_length: int, preview_image: numpy.ndarray | None, selected_bead_id: int | None, mode: str, motor_z_min: float | None, motor_z_max: float | None, expected_capture_count: int | None = None, x_axis_label: str = 'Z Position (nm)', x_axis_min: float | None = None, x_axis_max: float | None = None, image_x_min: float | None = None, image_x_max: float | None = None) None[source]#
class magscope.ui.controls.ZLUTGenerationDialog(parent: PyQt6.QtWidgets.QWidget | None = None)[source]#

Bases: PyQt6.QtWidgets.QDialog

_running = False[source]#
_evaluation_active = False[source]#
_startup_pending = False[source]#
_close_when_canceled = False[source]#
_selected_bead_id: int | None = None[source]#
status_label[source]#
detail_label[source]#
progress_label[source]#
progress_bar[source]#
preview_widget[source]#
bead_selector[source]#
cancel_button[source]#
save_button[source]#
save_and_load_button[source]#
close_button[source]#
_cancel_callback = None[source]#
_close_callback = None[source]#
_save_callback = None[source]#
_save_and_load_callback = None[source]#
_select_bead_callback = None[source]#
set_cancel_callback(callback) None[source]#
set_save_callback(callback) None[source]#
set_save_and_load_callback(callback) None[source]#
set_close_callback(callback) None[source]#
set_select_bead_callback(callback) None[source]#
_handle_cancel_clicked() None[source]#
_handle_save_clicked() None[source]#
_handle_save_and_load_clicked() None[source]#
_handle_close_clicked() None[source]#
mark_starting() None[source]#
_handle_bead_selection_changed(index: int) None[source]#
update_state(status: str, detail: str | None = None, *, running: bool = False, can_cancel: bool = False, phase: str = 'idle') None[source]#
update_progress(current_step: int, total_steps: int, capture_count: int, capture_capacity: int, motor_z_value: float | None = None) None[source]#
update_evaluation(*, active: bool, bead_ids: list[int], selected_bead_id: int | None) None[source]#
force_close() None[source]#
closeEvent(event) None[source]#
class magscope.ui.controls.CurrentZLUTDialog(parent: PyQt6.QtWidgets.QWidget | None = None)[source]#

Bases: MatplotlibCleanupMixin, PyQt6.QtWidgets.QDialog

preview_status_label[source]#
figure[source]#
canvas[source]#
axes[source]#
_image[source]#
min_value[source]#
max_value[source]#
step_value[source]#
profile_length_value[source]#
filepath_label[source]#
_add_metadata_row(layout: PyQt6.QtWidgets.QVBoxLayout, label_text: str) PyQt6.QtWidgets.QLabel[source]#
update_zlut(filepath: str | None, *, z_min: float | None = None, z_max: float | None = None, step_size: float | None = None, profile_length: int | None = None) None[source]#
_clear_preview(message: str) None[source]#
_update_preview(filepath: str | None) None[source]#
static _format_number(value: float | int | None, suffix: str = '') str[source]#
class magscope.ui.controls.ZLUTPanel(manager: magscope.ui.ui.UIManager)[source]#

Bases: ControlPanelBase

zlut_file_selected[source]#
zlut_clear_requested[source]#
NO_ZLUT_SELECTED_TEXT = 'No Z-LUT file selected'[source]#
select_button[source]#
clear_button[source]#
filepath_textedit[source]#
_metadata_layout[source]#
min_value[source]#
max_value[source]#
step_value[source]#
profile_length_value[source]#
search_targets() list[magscope.ui.search.SearchTarget][source]#
_add_metadata_row(label_text: str) PyQt6.QtWidgets.QLabel[source]#
_select_zlut_file()[source]#
_clear_zlut()[source]#
set_filepath(path: str | None)[source]#
update_metadata(z_min: float | None = None, z_max: float | None = None, step_size: float | None = None, profile_length: int | None = None)[source]#
clear_metadata()[source]#
static _format_number(value: float | int | None, suffix: str = '') str[source]#