# Licensed under a 3-clause BSD style license - see LICENSE.rst
import os
from typing import Any, List, Dict, Tuple, Union
try:
from PyQt6 import QtWidgets, QtGui, QtCore
from sofia_redux.visualization.display.ui import cursor_location as cl
except ImportError:
HAS_PYQT6 = False
QtCore = None
class QtWidgets:
class QDialog:
pass
class QtGui:
class QCloseEvent:
pass
class cl:
class Ui_Dialog:
pass
else:
from PyQt6.QtWidgets import QTableWidgetItem
from PyQt6.QtGui import QColor
HAS_PYQT6 = True
__all__ = ['CursorLocation']
[docs]
class CursorLocation(QtWidgets.QDialog, cl.Ui_Dialog):
"""
Cursor location display widget.
Parameters
----------
parent : View
Parent view widget for the dialog window.
Attributes
----------
points : list
Cursor data points to display in the widget.
"""
def __init__(self, parent: Any) -> None:
if not HAS_PYQT6: # pragma: no cover
raise ImportError('PyQt6 package is required for the Eye.')
super(self.__class__, self).__init__(parent)
self.setupUi(self)
self.setModal(0)
self.points = list()
[docs]
def closeEvent(self, a0: QtGui.QCloseEvent) -> None:
"""
Close the window.
Calls the parent `closed_cursor_popout` method.
Parameters
----------
a0 : QtGui.QCloseEvent
The close event.
"""
self.parentWidget().closed_cursor_popout()
[docs]
def update_points(self, data_coords: Dict,
cursor_coords: Union[List, Tuple]) -> None:
"""
Update displayed data points.
Parameters
----------
data_coords : dict
Keys are filenames; values are lists of dicts
containing 'order', 'bin', 'bin_x', 'bin_y',
'x_field', 'y_field', 'color', and 'visible'
values to display.
cursor_coords : tuple or list
Current cursor (x, y) coordinates.
"""
new_points = self._flatten_combine(data_coords,
cursor_coords)
if len(new_points) == 0:
return
self._define_table(row_count=len(new_points),
col_count=len(new_points[0]) - 1)
for row_index, new_point in enumerate(new_points):
for col_index, value in enumerate(new_point[1:]):
if isinstance(value, float):
item = QTableWidgetItem(f'{value:.3f}')
elif isinstance(value, int):
item = QTableWidgetItem(f'{value:d}')
elif isinstance(value, str):
if value.startswith('#'):
# special handling for color values
item = QTableWidgetItem('')
item.setBackground(QColor(value))
else:
item = QTableWidgetItem(value)
else:
item = QTableWidgetItem('######')
item.setTextAlignment(QtCore.Qt.AlignmentFlag.AlignHCenter)
self.table_widget.setItem(row_index, col_index, item)
# set filename as vertical header
vhead = QTableWidgetItem(os.path.basename(str(new_point[0])))
vhead.setTextAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
vhead.setToolTip(new_point[0])
self.table_widget.setVerticalHeaderItem(row_index, vhead)
self._resize()
@staticmethod
def _flatten_combine(data_coords: Dict,
cursor_coords: Union[List, Tuple]) -> List:
"""
Flatten input coordinates to a single table.
Parameters
----------
data_coords : dict
Keys are filenames; values are lists of dicts
containing 'order', 'bin', 'bin_x', 'bin_y',
'x_field', 'y_field', 'color', and 'visible'
values to display,
cursor_coords : tuple or list
Current cursor (x, y) coordinates.
Returns
-------
points : list
Each element of the list is a list with values
filename, order, color, x_field, y_field,
x_cursor, y_cursor, x_value, y_value, column.
"""
points = list()
for model_id, model_data_coords in data_coords.items():
point = None
for values in model_data_coords:
if not values['visible']:
continue
# Add 1 to order to change from 0-based indexing
# used by models to 1-based indexing used by humans
point = [values['filename'], values['order'] + 1,
values['aperture'] + 1, values['color'],
values['x_field'], values['y_field'],
cursor_coords[0], cursor_coords[1],
values['bin_x'], values['bin_y'],
values['bin']]
points.append(point)
if point is None:
# no points found, append a blank entry for the model
point = [model_data_coords[0]['filename']] + ['-'] * 10
points.append(point)
return points
def _define_table(self, row_count: int, col_count: int) -> None:
"""
Define the table widget.
Parameters
----------
row_count : int
Number of data rows in the table, not including the
horizontal header row.
col_count : int
Number of data columns in the table, not including the
vertical header column.
"""
self.table_widget.setRowCount(row_count)
self.table_widget.setColumnCount(col_count)
def _resize(self) -> None:
"""Resize the table widget to its contents."""
self.table_widget.resizeRowsToContents()