from PyQt5.QtCore import pyqtSlot, Qt
from PyQt5.QtGui import QFontMetrics
from PyQt5.QtWidgets import QWidget
import numpy as np
from mdt.gui.maps_visualizer.actions import SetMapTitle, SetMapColormap, SetMapScale, SetMapClipping, \
SetMapColorbarLabel
from mdt.gui.maps_visualizer.base import DataConfigModel
from mdt.gui.maps_visualizer.design.ui_MapSpecificOptions import Ui_MapSpecificOptions
from mdt.gui.maps_visualizer.design.ui_TabMapSpecific import Ui_TabMapSpecific
from mdt.gui.utils import blocked_signals, TimedUpdate, split_long_path_elements
from mdt.visualization.maps.base import DataInfo, SingleMapConfig, MapPlotConfig
__author__ = 'Robbert Harms'
__date__ = "2016-09-03"
__maintainer__ = "Robbert Harms"
__email__ = "robbert@xkls.nl"
[docs]class TabMapSpecific(QWidget, Ui_TabMapSpecific):
def __init__(self, controller, parent=None):
super().__init__(parent)
self.setupUi(self)
self.map_specific_tab = MapSpecificOptions(controller, self)
self.mapSpecificOptionsPosition.addWidget(self.map_specific_tab)
self._controller = controller
self._controller.model_updated.connect(self.model_updated)
self.selectedMap.currentIndexChanged.connect(
lambda ind: self._update_map_specifics(self.selectedMap.itemData(ind, Qt.UserRole)))
[docs] @pyqtSlot(DataConfigModel)
def model_updated(self, model):
config = model.get_config()
map_names = config.maps_to_show
with blocked_signals(self.selectedMap):
current_selected = self.selectedMap.currentData(Qt.UserRole)
self.selectedMap.clear()
self.selectedMap.addItems(map_names)
for index, map_name in enumerate(map_names):
self.selectedMap.setItemData(index, map_name, Qt.UserRole)
if map_name in config.map_plot_options and config.map_plot_options[map_name].title:
title = config.map_plot_options[map_name].title
self.selectedMap.setItemData(index, map_name + ' (' + title + ')', Qt.DisplayRole)
for ind in range(self.selectedMap.count()):
if self.selectedMap.itemData(ind, Qt.UserRole) == current_selected:
self.selectedMap.setCurrentIndex(ind)
break
if self.selectedMap.count():
self._update_map_specifics(self.selectedMap.currentData(Qt.UserRole))
else:
self._update_map_specifics(None)
def _update_map_specifics(self, map_name):
"""Set the map specific options to reflect the settings of the given map"""
if map_name is None:
self.map_specific_tab.reset()
else:
self.map_specific_tab.use(map_name)
[docs]class MapSpecificOptions(QWidget, Ui_MapSpecificOptions):
def __init__(self, controller, parent=None):
super().__init__(parent)
self.setupUi(self)
self._controller = controller
current_model = self._controller.get_model()
self._current_map = None
self.colormap.addItems(['-- Use global --'] + current_model.get_config().get_available_colormaps())
self.colormap.currentIndexChanged.connect(self._update_colormap)
self.data_clipping_min.valueChanged.connect(self._update_clipping_min)
self.data_clipping_max.valueChanged.connect(self._update_clipping_max)
self.data_scale_min.valueChanged.connect(self._update_scale_min)
self.data_scale_max.valueChanged.connect(self._update_scale_max)
self.data_set_use_scale.stateChanged.connect(self._set_use_scale)
self.use_data_scale_min.stateChanged.connect(self._set_use_data_scale_min)
self.use_data_scale_max.stateChanged.connect(self._set_use_data_scale_max)
self.data_set_use_clipping.stateChanged.connect(self._set_use_clipping)
self.use_data_clipping_min.stateChanged.connect(self._set_use_data_clipping_min)
self.use_data_clipping_max.stateChanged.connect(self._set_use_data_clipping_max)
self._title_timer = TimedUpdate(self._update_map_title)
self.map_title.textChanged.connect(lambda: self._title_timer.add_delayed_callback(500))
self.map_title.setFixedHeight(QFontMetrics(self.map_title.font()).lineSpacing() * 3)
self._colorbar_label_timer = TimedUpdate(self._update_colorbar_label)
self.data_colorbar_label.textChanged.connect(lambda : self._colorbar_label_timer.add_delayed_callback(500))
self.data_colorbar_label.setFixedHeight(QFontMetrics(self.data_colorbar_label.font()).lineSpacing() * 3)
self.info_Clipping.set_collapse(True)
self._auto_enable_scale_min = False
self._auto_enable_scale_max = False
self._auto_enable_clipping_min = False
self._auto_enable_clipping_max = False
self.reset()
self._update_scaling_delays()
def _update_scaling_delays(self):
if self.use_data_scale_max.isChecked():
self.data_scale_max.set_update_delay(500)
else:
self.data_scale_max.set_update_delay(0)
if self.use_data_scale_min.isChecked():
self.data_scale_min.set_update_delay(500)
else:
self.data_scale_min.set_update_delay(0)
if self.use_data_clipping_max.isChecked():
self.data_clipping_max.set_update_delay(500)
else:
self.data_clipping_max.set_update_delay(0)
if self.use_data_clipping_min.isChecked():
self.data_clipping_min.set_update_delay(500)
else:
self.data_clipping_min.set_update_delay(0)
[docs] def reset(self):
"""Set all the values to their defaults"""
self._current_map = None
self.colormap.setCurrentText('hot')
with blocked_signals(self.map_title):
self.map_title.document().setPlainText('')
with blocked_signals(self.data_colorbar_label):
self.data_colorbar_label.document().setPlainText('')
with blocked_signals(self.data_clipping_min):
self.data_clipping_min.setValue(0)
with blocked_signals(self.data_clipping_max):
self.data_clipping_max.setValue(0)
with blocked_signals(self.data_scale_min):
self.data_scale_min.setValue(0)
with blocked_signals(self.data_scale_max):
self.data_scale_max.setValue(0)
with blocked_signals(self.use_data_clipping_min):
self.use_data_clipping_min.setChecked(False)
with blocked_signals(self.use_data_clipping_max):
self.use_data_clipping_max.setChecked(False)
with blocked_signals(self.use_data_scale_min):
self.use_data_scale_min.setChecked(False)
with blocked_signals(self.use_data_scale_max):
self.use_data_scale_max.setChecked(False)
self.info_file_location.setText('-')
self.info_maximum.setText('-')
self.info_minimum.setText('-')
self.info_shape.setText('-')
def _get_mask(self, data_info, map_name):
current_model = self._controller.get_model()
plot_config = current_model.get_config()
def get_map_attr():
if map_name in plot_config.map_plot_options:
value = getattr(plot_config.map_plot_options[map_name], 'mask_name')
if value is not None:
return value
return plot_config.mask_name
mask_name = get_map_attr()
if mask_name is not None:
return data_info.get_map_data(mask_name)
return None
[docs] def use(self, map_name):
"""Load the settings of the given map"""
self._current_map = map_name
current_model = self._controller.get_model()
try:
map_info = current_model.get_config().map_plot_options[map_name]
except KeyError:
map_info = SingleMapConfig()
data_info = current_model.get_data()
vmin, vmax = data_info.get_single_map_info(map_name).min_max(mask=self._get_mask(data_info, map_name))
with blocked_signals(self.map_title):
self.map_title.document().setPlainText(map_info.title if map_info.title else '')
with blocked_signals(self.data_colorbar_label):
self.data_colorbar_label.document().setPlainText(map_info.colorbar_label if map_info.colorbar_label else '')
with blocked_signals(self.colormap):
if map_info.colormap is None:
self.colormap.setCurrentIndex(0)
else:
self.colormap.setCurrentText(map_info.colormap)
with blocked_signals(self.data_clipping_min):
self.data_clipping_min.setValue(map_info.clipping.vmin)
with blocked_signals(self.data_clipping_max):
self.data_clipping_max.setValue(map_info.clipping.vmax)
with blocked_signals(self.data_scale_min):
self.data_scale_min.setValue(map_info.scale.vmin)
with blocked_signals(self.data_scale_max):
self.data_scale_max.setValue(map_info.scale.vmax)
with blocked_signals(self.data_set_use_scale):
self.data_set_use_scale.setChecked(map_info.scale.use_min or map_info.scale.use_max)
with blocked_signals(self.data_set_use_clipping):
self.data_set_use_clipping.setChecked(map_info.clipping.use_min or map_info.clipping.use_max)
with blocked_signals(self.use_data_clipping_min):
self.use_data_clipping_min.setChecked(map_info.clipping.use_min)
with blocked_signals(self.use_data_clipping_max):
self.use_data_clipping_max.setChecked(map_info.clipping.use_max)
with blocked_signals(self.use_data_scale_min):
self.use_data_scale_min.setChecked(map_info.scale.use_min)
with blocked_signals(self.use_data_scale_max):
self.use_data_scale_max.setChecked(map_info.scale.use_max)
map_filename = data_info.get_file_path(map_name)
if map_filename:
self.info_file_location.setText(split_long_path_elements(map_filename, 25))
self.info_minimum.setText(str(vmin))
self.info_maximum.setText(str(vmax))
self.info_has_nan.setText('Yes' if data_info.get_single_map_info(map_name).has_nan() else 'No')
self.info_shape.setText(str(data_info.get_single_map_info(map_name).shape))
def _get_current_map_config(self):
current_model = self._controller.get_model()
current_config = current_model.get_config()
current_map_config = current_config.map_plot_options.get(self._current_map, SingleMapConfig())
return current_map_config
@pyqtSlot()
def _update_map_title(self):
string = self.map_title.toPlainText()
if self._current_map:
if string == '':
string = None
self._controller.apply_action(SetMapTitle(self._current_map, string))
@pyqtSlot()
def _update_colorbar_label(self):
string = self.data_colorbar_label.toPlainText()
if self._current_map:
if string == '':
string = None
self._controller.apply_action(SetMapColorbarLabel(self._current_map, string))
@pyqtSlot(int)
def _update_colormap(self, index):
if self._current_map:
if index == 0:
self._controller.apply_action(SetMapColormap(self._current_map, None))
else:
self._controller.apply_action(SetMapColormap(self._current_map, self.colormap.itemText(index)))
@pyqtSlot(float)
def _update_scale_min(self, value):
if self._current_map:
current_scale = self._get_current_map_config().scale
kwargs = dict(vmin=value)
if current_scale.use_max and current_scale.use_min and value > current_scale.vmax:
kwargs.update(dict(use_min=False))
self._auto_enable_scale_min = True
elif self._auto_enable_scale_min and value < current_scale.vmax:
kwargs.update(dict(use_min=True))
self._auto_enable_scale_min = False
new_scale = current_scale.get_updated(**kwargs)
self._controller.apply_action(SetMapScale(self._current_map, new_scale))
@pyqtSlot(float)
def _update_scale_max(self, value):
if self._current_map:
current_scale = self._get_current_map_config().scale
kwargs = dict(vmax=value)
if current_scale.use_max and current_scale.use_min and value < current_scale.vmin:
kwargs.update(dict(use_max=False))
self._auto_enable_scale_max = True
elif self._auto_enable_scale_max and value > current_scale.vmin:
kwargs.update(dict(use_max=True))
self._auto_enable_scale_max = False
new_scale = current_scale.get_updated(**kwargs)
self._controller.apply_action(SetMapScale(self._current_map, new_scale))
@pyqtSlot(int)
def _set_use_scale(self, check_state):
use_scale = check_state == 2
if self._current_map:
current_scale = self._get_current_map_config().scale
if use_scale and current_scale.vmax < current_scale.vmin:
new_scale = current_scale.get_updated(use_min=use_scale, use_max=use_scale, vmax=current_scale.vmin)
else:
new_scale = current_scale.get_updated(use_min=use_scale, use_max=use_scale)
self._controller.apply_action(SetMapScale(self._current_map, new_scale))
self._update_scaling_delays()
@pyqtSlot(int)
def _set_use_data_scale_min(self, check_state):
use_scale = check_state == 2
if self._current_map:
if use_scale and self._get_current_map_config().scale.use_max:
self._set_use_scale(2)
else:
new_scale = self._get_current_map_config().scale.get_updated(use_min=use_scale)
self._controller.apply_action(SetMapScale(self._current_map, new_scale))
@pyqtSlot(int)
def _set_use_data_scale_max(self, check_state):
use_scale = check_state == 2
if self._current_map:
if use_scale and self._get_current_map_config().scale.use_min:
self._set_use_scale(2)
else:
new_scale = self._get_current_map_config().scale.get_updated(use_max=use_scale)
self._controller.apply_action(SetMapScale(self._current_map, new_scale))
@pyqtSlot(float)
def _update_clipping_min(self, value):
if self._current_map:
current_clipping = self._get_current_map_config().clipping
kwargs = dict(vmin=value)
if current_clipping.use_max and current_clipping.use_min and value > current_clipping.vmax:
kwargs.update(dict(use_min=False))
self._auto_enable_scale_min = True
elif self._auto_enable_scale_min and value < current_clipping.vmax:
kwargs.update(dict(use_min=True))
self._auto_enable_scale_min = False
new_clipping = current_clipping.get_updated(**kwargs)
self._controller.apply_action(SetMapClipping(self._current_map, new_clipping))
@pyqtSlot(float)
def _update_clipping_max(self, value):
if self._current_map:
current_clipping = self._get_current_map_config().clipping
kwargs = dict(vmax=value)
if current_clipping.use_max and current_clipping.use_min and value < current_clipping.vmin:
kwargs.update(dict(use_max=False))
self._auto_enable_scale_max = True
elif self._auto_enable_scale_max and value > current_clipping.vmin:
kwargs.update(dict(use_max=True))
self._auto_enable_scale_max = False
new_clipping = current_clipping.get_updated(**kwargs)
self._controller.apply_action(SetMapClipping(self._current_map, new_clipping))
@pyqtSlot(int)
def _set_use_clipping(self, check_state):
use_clipping = check_state == 2
if self._current_map:
current_clipping = self._get_current_map_config().clipping
if use_clipping and current_clipping.vmax < current_clipping.vmin:
new_clipping = current_clipping.get_updated(use_min=use_clipping, use_max=use_clipping,
vmax=current_clipping.vmin)
else:
new_clipping = current_clipping.get_updated(use_min=use_clipping, use_max=use_clipping)
self._controller.apply_action(SetMapClipping(self._current_map, new_clipping))
self._update_scaling_delays()
@pyqtSlot(int)
def _set_use_data_clipping_min(self, check_state):
use_clipping = check_state == 2
if self._current_map:
if use_clipping and self._get_current_map_config().clipping.use_max:
self._set_use_clipping(2)
else:
new_clipping = self._get_current_map_config().clipping.get_updated(use_min=use_clipping)
self._controller.apply_action(SetMapClipping(self._current_map, new_clipping))
@pyqtSlot(int)
def _set_use_data_clipping_max(self, check_state):
use_clipping = check_state == 2
if self._current_map:
if use_clipping and self._get_current_map_config().clipping.use_min:
self._set_use_clipping(2)
else:
new_clipping = self._get_current_map_config().clipping.get_updated(use_max=use_clipping)
self._controller.apply_action(SetMapClipping(self._current_map, new_clipping))