# -*- coding: utf-8 -*-
"""A widget for controlling the appearance of markers
This module defines the :class:`MarkerControl` to control the appearance and
behaviour of :class:`~straditize.cross_mark.CrossMarks` instances in the
:attr:`straditize.straditizer.Straditizer.marks` attribute
**Disclaimer**
Copyright (C) 2018-2019 Philipp S. Sommer
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
from __future__ import division
import numpy as np
from itertools import chain
import matplotlib as mpl
import matplotlib.colors as mcol
from straditize.widgets import StraditizerControlBase, get_icon
from straditize.common import docstrings
from psyplot_gui.compat.qtcompat import (
QWidget, QHBoxLayout, QVBoxLayout, QLabel, QCheckBox,
QComboBox, QLineEdit, QDoubleValidator, QtGui, QTableWidget,
QTableWidgetItem, Qt, with_qt5, QtCore, QGridLayout, QToolBar, QIcon)
if with_qt5:
from PyQt5.QtWidgets import QHeaderView, QColorDialog
else:
from PyQt4.QtGui import QHeaderView, QColorDialog
[docs]class ColorLabel(QTableWidget):
"""A QTableWidget with one cell and no headers to just display a color"""
#: a signal that is emitted with an rgba color if the chosen color changes
color_changed = QtCore.pyqtSignal(QtGui.QColor)
#: QtCore.QColor. The current color that is displayed
color = None
def __init__(self, color='w', *args, **kwargs):
"""The color to display
Parameters
----------
color: object
Either a QtGui.QColor object or a color that can be converted
to RGBA using the :func:`matplotlib.colors.to_rgba` function"""
super(ColorLabel, self).__init__(*args, **kwargs)
self.setColumnCount(1)
self.setRowCount(1)
self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.horizontalHeader().setHidden(True)
self.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
self.verticalHeader().setHidden(True)
self.verticalHeader().setSectionResizeMode(QHeaderView.Stretch)
self.setEditTriggers(QTableWidget.NoEditTriggers)
self.setSelectionMode(QTableWidget.NoSelection)
self.itemClicked.connect(self.select_color)
self.color_item = QTableWidgetItem()
self.setItem(0, 0, self.color_item)
self.adjust_height()
self.set_color(color)
[docs] def select_color(self, *args):
"""Select a color using :meth:`PyQt5.QtWidgets.QColorDialog.getColor`
"""
self.set_color(QColorDialog.getColor(
self.color_item.background().color()))
[docs] def set_color(self, color):
"""Set the color of the label
This method sets the given `color` as background color for the cell
and emits the :attr:`color_changed` signal
Parameters
----------
color: object
Either a QtGui.QColor object or a color that can be converted
to RGBA using the :func:`matplotlib.colors.to_rgba` function"""
color = self._set_color(color)
self.color_changed.emit(color)
def _set_color(self, color):
if not isinstance(color, QtGui.QColor):
color = QtGui.QColor(
*map(int, np.round(mcol.to_rgba(color)) * 255))
self.color_item.setBackground(color)
self.color = color
return color
[docs] def adjust_height(self):
"""Adjust the height to match the row height"""
h = self.rowHeight(0) * self.rowCount()
self.setMaximumHeight(h)
self.setMinimumHeight(h)
[docs] def sizeHint(self):
"""Reimplemented to use the rowHeight as height"""
s = super(ColorLabel, self).sizeHint()
return QtCore.QSize(s.width(), self.rowHeight(0) * self.rowCount())
[docs]class MarkerControl(StraditizerControlBase, QWidget):
"""Widget to control the appearance of the marks
This widget controls the appearance of the
:class:`straditize.cross_mark.CrossMarks` instances in the
:attr:`~straditize.straditizer.Straditizer.marks` attribute of the
:attr:`~straditize.widgets.StraditizerControlBase.straditizer`"""
_toolbar = None
@property
def marks(self):
"""The :class:`~straditize.cross_mark.CrossMarks` of the straditizer
"""
return chain(self.straditizer.marks, self.straditizer.magni_marks)
@docstrings.dedent
def __init__(self, straditizer_widgets, item, *args, **kwargs):
"""
Parameters
----------
%(StraditizerControlBase.init_straditizercontrol.parameters)s
"""
super(MarkerControl, self).__init__(*args, **kwargs)
self.init_straditizercontrol(straditizer_widgets, item)
vbox = QVBoxLayout()
# auto hide button
box_hide = QGridLayout()
self.cb_auto_hide = QCheckBox('Auto-hide')
self.cb_show_connected = QCheckBox('Show additionals')
self.cb_drag_hline = QCheckBox('Drag in y-direction')
self.cb_drag_vline = QCheckBox('Drag in x-direction')
self.cb_selectable_hline = QCheckBox('Horizontal lines selectable')
self.cb_selectable_vline = QCheckBox('Vertical lines selectable')
self.cb_show_hlines = QCheckBox('Show horizontal lines')
self.cb_show_vlines = QCheckBox('Show vertical lines')
box_hide.addWidget(self.cb_auto_hide, 0, 0)
box_hide.addWidget(self.cb_show_connected, 0, 1)
box_hide.addWidget(self.cb_show_vlines, 1, 0)
box_hide.addWidget(self.cb_show_hlines, 1, 1)
box_hide.addWidget(self.cb_drag_hline, 2, 0)
box_hide.addWidget(self.cb_drag_vline, 2, 1)
box_hide.addWidget(self.cb_selectable_hline, 3, 0)
box_hide.addWidget(self.cb_selectable_vline, 3, 1)
vbox.addLayout(box_hide)
style_box = QGridLayout()
style_box.addWidget(QLabel('Unselected:'), 0, 1)
selected_label = QLabel('Selected:')
style_box.addWidget(selected_label, 0, 2)
max_width = selected_label.sizeHint().width()
# line color
self.lbl_color_select = ColorLabel()
self.lbl_color_unselect = ColorLabel()
self.lbl_color_select.setMaximumWidth(max_width)
self.lbl_color_unselect.setMaximumWidth(max_width)
style_box.addWidget(QLabel('Line color:'), 1, 0)
style_box.addWidget(self.lbl_color_unselect, 1, 1)
style_box.addWidget(self.lbl_color_select, 1, 2)
# line width
self.txt_line_width = QLineEdit()
self.txt_line_width_select = QLineEdit()
validator = QDoubleValidator()
validator.setBottom(0)
self.txt_line_width.setValidator(validator)
self.txt_line_width_select.setValidator(validator)
style_box.addWidget(QLabel('Line width:'), 2, 0)
style_box.addWidget(self.txt_line_width, 2, 1)
style_box.addWidget(self.txt_line_width_select, 2, 2)
vbox.addLayout(style_box)
# line style
hbox_line_style = QHBoxLayout()
hbox_line_style.addWidget(QLabel('Line style'))
self.combo_line_style = QComboBox()
hbox_line_style.addWidget(self.combo_line_style)
vbox.addLayout(hbox_line_style)
self.fill_linestyles()
self.combo_line_style.setSizeAdjustPolicy(QComboBox.AdjustToContents)
# marker style
hbox_marker_style = QHBoxLayout()
hbox_marker_style.addWidget(QLabel('Marker size'))
self.txt_marker_size = QLineEdit()
self.txt_marker_size.setMinimumWidth(40)
self.txt_marker_size.setText(str(mpl.rcParams['lines.markersize']))
validator = QDoubleValidator()
validator.setBottom(0)
self.txt_marker_size.setValidator(validator)
hbox_marker_style.addWidget(self.txt_marker_size)
hbox_marker_style.addWidget(QLabel('Marker style'))
self.combo_marker_style = QComboBox()
hbox_marker_style.addWidget(self.combo_marker_style)
vbox.addLayout(hbox_marker_style)
self.setLayout(vbox)
self.widgets2disable = [
self.lbl_color_select, self.lbl_color_unselect,
self.cb_auto_hide, self.txt_line_width, self.txt_line_width_select,
self.combo_line_style, self.cb_show_connected,
self.txt_marker_size, self.combo_marker_style,
self.cb_drag_vline, self.cb_drag_hline,
self.cb_selectable_vline, self.cb_selectable_hline,
self.cb_show_hlines, self.cb_show_vlines]
self.fill_markerstyles()
self.tb_actions = []
# ---------------------------------------------------------------------
# ---------------------------- connections ----------------------------
# ---------------------------------------------------------------------
self.lbl_color_select.color_changed.connect(self.change_select_colors)
self.lbl_color_unselect.color_changed.connect(
self.change_unselect_colors)
self.txt_line_width.textChanged.connect(self.change_line_widths)
self.txt_line_width_select.textChanged.connect(
self.change_selection_line_widths)
self.cb_auto_hide.stateChanged.connect(self.change_auto_hide)
self.combo_marker_style.currentIndexChanged.connect(
self.change_marker_style)
self.combo_line_style.currentIndexChanged.connect(
self.change_line_style)
self.txt_marker_size.textChanged.connect(self.change_marker_size)
self.cb_show_connected.stateChanged.connect(
self.change_show_connected_artists)
self.cb_drag_hline.stateChanged.connect(self.change_hline_draggable)
self.cb_drag_vline.stateChanged.connect(self.change_vline_draggable)
self.cb_selectable_hline.stateChanged.connect(
self.change_hline_selectable)
self.cb_selectable_vline.stateChanged.connect(
self.change_vline_selectable)
self.cb_show_vlines.stateChanged.connect(self.change_show_vlines)
self.cb_show_hlines.stateChanged.connect(self.change_show_hlines)
[docs] def draw_figs(self):
"""Draw the figures of the :attr:`marks`"""
for canvas in {m.fig.canvas for m in self.marks}:
canvas.draw_idle()
[docs] def fill_linestyles(self):
"""Fill the :attr:`combo_line_style` combobox"""
self.line_styles = [
('-', 'solid'),
('--', 'dashed'),
('-.', 'dashdot'),
(':', 'dotted'),
('None', None, '', ' ')
]
self.combo_line_style.addItems([t[0] for t in self.line_styles])
[docs] def fill_markerstyles(self):
"""Fill the :attr:`combo_marker_style` combobox"""
self.marker_styles = [
('point', (".", )),
('pixel', (",", )),
('circle', ("o", )),
('triangle down', ("v", "1")),
('triangle up', ("^", "2")),
('triangle left', ("<", "3")),
('triangle right', (">", "4")),
('octagon', ("8", )),
('square', ("s", )),
('pentagon', ("p", )),
('plus (filled)', ("P", )),
('star', ("*", )),
('hexagon1', ("h", )),
('hexagon2', ("H", )),
('plus', ("+", )),
('x', ("x", )),
('x (filled)', ("X", )),
('diamond', ("D", )),
('thin diamond', ("d", )),
('vline', ("|", )),
('hline', ("_", )),
('no marker', ('None', None, '', ' '))
]
self.combo_marker_style.addItems([
'%s (%s)' % (key.capitalize(), ','.join(map(str, filter(None, t))))
for key, t in self.marker_styles])
[docs] def set_marker_item(self, marker):
"""Switch the :attr:`combo_marker_style` to the given `marker`
Parameters
----------
marker: str
A matplotlib marker string"""
for i, (key, t) in enumerate(self.marker_styles):
if marker in t:
block = self.combo_marker_style.blockSignals(True)
self.combo_marker_style.setCurrentIndex(i)
self.combo_marker_style.blockSignals(block)
break
[docs] def set_line_style_item(self, ls):
"""Switch the :attr:`combo_line_style` to the given linestyle
Parameters
----------
ls: str
The matplotlib linestyle string
"""
for i, t in enumerate(self.line_styles):
if ls in t:
block = self.combo_line_style.blockSignals(True)
self.combo_line_style.setCurrentIndex(i)
self.combo_line_style.blockSignals(block)
break
[docs] def should_be_enabled(self, w):
"""Check if a widget `w` should be enabled or disabled
Parameters
----------
w: PyQt5.QtWidgets.QWidget
The widget to (potentially) enable
Returns
-------
bool
True if the :attr:`straditizer` of this instance has marks.
Otherwise False"""
return self.straditizer is not None and bool(self.straditizer.marks)
[docs] def change_select_colors(self, color):
"""Change the selection color of the marks
Change the selection color of the marks to the given color.
Parameters
----------
color: PyQt5.QtGui.QColor or a matplotlib color
The color to use
"""
if isinstance(color, QtGui.QColor):
color = np.array(color.getRgb()) / 255.
for mark in self.marks:
key = 'color' if 'color' in mark._select_props else 'c'
mark._select_props[key] = color
mark._unselect_props[key] = mark.hline.get_c()
self.draw_figs()
[docs] def change_unselect_colors(self, color):
"""Change the :attr:`straditize.cross_mark.CrossMark.cunselect` color
Change the unselection color of the marks to the given color.
Parameters
----------
color: PyQt5.QtGui.QColor or a matplotlib color
The color to use
"""
if isinstance(color, QtGui.QColor):
color = np.array(color.getRgb()) / 255.
for mark in self.marks:
key = 'color' if 'color' in mark._unselect_props else 'c'
mark._unselect_props[key] = color
for l in chain(mark.hlines, mark.vlines, mark.line_connections):
l.set_color(color)
self.draw_figs()
[docs] def change_line_widths(self, lw):
"""Change the linewidth of the marks
Parameters
----------
lw: float
The line width to use
"""
lw = float(lw or 0)
for mark in self.marks:
key = 'lw' if 'lw' in mark._unselect_props else 'linewidth'
mark._unselect_props[key] = lw
if not mark.auto_hide:
for l in chain(mark.hlines, mark.vlines,
mark.line_connections):
l.set_lw(lw)
self.draw_figs()
[docs] def change_selection_line_widths(self, lw):
"""Change the linewidth for selected marks
Parameters
----------
lw: float
The linewidth for selected marks"""
lw = float(lw or 0)
for mark in self.marks:
key = 'lw' if 'lw' in mark._select_props else 'linewidth'
mark._select_props[key] = lw
[docs] def change_auto_hide(self, auto_hide):
"""Toggle the :attr:`~straditize.cross_mark.CrossMark.auto_hide`
This method disables or enables the
:attr:`~straditize.cross_mark.CrossMark.auto_hide` of the marks
Parameters
----------
auto_hide: bool or PyQt5.QtGui.Qt.Checked or PyQt5.QtGui.Qt.Unchecked
The value to use for the auto_hide. :data:`PyQt5.QtGui.Qt.Checked`
is equivalent to ``True``
"""
if auto_hide is Qt.Checked:
auto_hide = True
elif auto_hide is Qt.Unchecked:
auto_hide = False
for mark in self.marks:
mark.auto_hide = auto_hide
if auto_hide:
for l in chain(mark.hlines, mark.vlines,
mark.line_connections):
l.set_lw(0)
else:
lw = mark._unselect_props.get(
'lw', mark._unselect_props.get('linewidth'))
for l in chain(mark.hlines, mark.vlines,
mark.line_connections):
l.set_lw(lw)
self.draw_figs()
[docs] def change_show_connected_artists(self, show):
"""Change the visibility of connected artists
Parameters
----------
show: bool
The visibility for the
:meth:`straditize.cross_mark.CrossMarks.set_connected_artists_visible`
method"""
if show is Qt.Checked:
show = True
elif show is Qt.Unchecked:
show = False
for mark in self.marks:
mark.set_connected_artists_visible(show)
self.draw_figs()
[docs] def change_line_style(self, i):
"""Change the line style of the marks
Parameters
----------
i: int
The index of the line style in the :attr:`line_styles` attribute
to use
"""
ls = self.line_styles[i][0]
for mark in self.marks:
for l in chain(mark.hlines, mark.vlines, mark.line_connections):
l.set_ls(ls)
self.draw_figs()
[docs] def change_marker_style(self, i):
"""Change the marker style of the marks
Parameters
----------
i: int
The index of the marker style in the :attr:`marker_styles`
attribute to use
"""
marker = self.marker_styles[i][1][0]
for mark in self.marks:
for l in chain(mark.hlines, mark.vlines, mark.line_connections):
l.set_marker(marker)
self.draw_figs()
[docs] def change_marker_size(self, markersize):
"""Change the size of the markers
Parameters
----------
markersize: float
The size of the marker to use"""
markersize = float(markersize or 0)
for mark in self.marks:
for l in chain(mark.hlines, mark.vlines, mark.line_connections):
l.set_markersize(markersize)
self.draw_figs()
[docs] def fill_from_mark(self, mark):
"""Set the widgets of this :class:`MarkerControl` from a mark
This method sets the color labels, combo boxes, check boxes and
text edits to match the properties of the given `mark`
Parameters
----------
mark: straditize.cross_mark.CrossMark
The mark to use the attributes from
"""
line = mark.hline
try:
cselect = mark._select_props['c']
except KeyError:
try:
cselect = mark._select_props['color']
except KeyError:
cselect = mark.hline.get_c()
lw_key = 'lw' if 'lw' in mark._unselect_props else 'linewidth'
self.set_line_style_item(line.get_linestyle())
self.set_marker_item(line.get_marker())
self.txt_line_width.setText(str(mark._unselect_props.get(lw_key, 0)))
self.txt_line_width_select.setText(
str(mark._select_props.get(lw_key, 0)))
self.txt_marker_size.setText(str(line.get_markersize()))
self.lbl_color_select._set_color(cselect)
self.lbl_color_unselect._set_color(mark.hline.get_c())
self.cb_auto_hide.setChecked(mark.auto_hide)
self.cb_show_connected.setChecked(mark.show_connected_artists)
try:
mark.x
except NotImplementedError:
self.cb_drag_vline.setEnabled(False)
self.cb_show_vlines.setEnabled(False)
self.cb_selectable_vline.setEnabled(False)
else:
self.cb_drag_vline.setEnabled(True)
self.cb_show_vlines.setEnabled(True)
self.cb_selectable_vline.setEnabled(True)
try:
mark.y
except NotImplementedError:
self.cb_drag_hline.setEnabled(False)
self.cb_show_hlines.setEnabled(False)
self.cb_selectable_hline.setEnabled(False)
else:
self.cb_drag_hline.setEnabled(True)
self.cb_show_hlines.setEnabled(True)
self.cb_selectable_hline.setEnabled(True)
self.cb_drag_hline.setChecked('h' in mark.draggable)
self.cb_drag_vline.setChecked('v' in mark.draggable)
self.cb_show_hlines.setChecked(not mark.hide_horizontal)
self.cb_show_vlines.setChecked(not mark.hide_vertical)
self.cb_selectable_hline.setChecked('h' in mark.selectable)
self.cb_selectable_vline.setChecked('v' in mark.selectable)
@property
def line_props(self):
"""The properties of the lines as a :class:`dict`"""
return {
'ls': self.combo_line_style.currentText(),
'marker': self.marker_styles[
self.combo_marker_style.currentIndex()][1][0],
'lw': float(self.txt_line_width.text().strip() or 0),
'markersize': float(self.txt_marker_size.text().strip() or 0),
'c': self.lbl_color_unselect.color.getRgbF(),
}
@property
def select_props(self):
"""The properties of selected marks as a :class:`dict`"""
return {
'c': self.lbl_color_select.color.getRgbF(),
'lw': float(self.txt_line_width_select.text().strip() or 0)}
[docs] def update_mark(self, mark):
"""Update the properties of a mark to match the settings
Parameters
----------
mark: straditize.cross_mark.CrossMarks
The mark to update
"""
if len(self.straditizer.marks) < 2:
return
# line properties
props = self.line_props
mark._unselect_props.update(props)
mark._select_props.update(self.select_props)
# auto_hide
auto_hide = self.cb_auto_hide.isChecked()
mark.auto_hide = auto_hide
if auto_hide:
props['lw'] = 0
# show_connected
show_connected = self.cb_show_connected.isChecked()
mark.set_connected_artists_visible(show_connected)
# drag hline
drag_hline = self.cb_drag_hline.isChecked()
if drag_hline and 'h' not in mark.draggable:
mark.draggable.append('h')
elif not drag_hline and 'h' in mark.draggable:
mark.draggable.remove('h')
# drag vline
drag_vline = self.cb_drag_vline.isChecked()
if drag_vline and 'v' not in mark.draggable:
mark.draggable.append('v')
elif not drag_vline and 'v' in mark.draggable:
mark.draggable.remove('v')
# select hline
select_hline = self.cb_selectable_hline.isChecked()
if select_hline and 'h' not in mark.selectable:
mark.selectable.append('h')
elif not select_hline and 'h' in mark.selectable:
mark.selectable.remove('h')
# select hline
select_vline = self.cb_selectable_vline.isChecked()
if select_vline and 'v' not in mark.selectable:
mark.selectable.append('v')
elif not select_vline and 'v' in mark.selectable:
mark.selectable.remove('v')
show_horizontal = self.cb_show_hlines.isChecked()
mark.hide_horizontal = ~show_horizontal
show_vertical = self.cb_show_vlines.isChecked()
mark.hide_vertical = ~show_vertical
for l in chain(mark.hlines, mark.vlines):
l.update(props)
for l in mark.hlines:
l.set_visible(show_horizontal)
for l in mark.vlines:
l.set_visible(show_vertical)
[docs] def change_hline_draggable(self, state):
"""Enable or disable the dragging of horizontal lines
Parameters
----------
state: Qt.Checked or Qt.Unchecked
If Qt.Checked, the horizontal lines can be dragged and dropped"""
if state == Qt.Checked:
for mark in self.marks:
mark.draggable = np.unique(np.r_[['h'], mark.draggable])
else:
for mark in self.marks:
mark.draggable = np.array(list(set(mark.draggable) - {'h'}))
[docs] def change_hline_selectable(self, state):
"""Enable or disable the selection of horizontal lines
Parameters
----------
state: Qt.Checked or Qt.Unchecked
If Qt.Checked, the horizontal lines can be selected"""
if state == Qt.Checked:
for mark in self.marks:
mark.selectable = np.unique(np.r_[['h'], mark.selectable])
else:
for mark in self.marks:
mark.selectable = np.array(list(set(mark.selectable) - {'h'}))
[docs] def change_vline_draggable(self, state):
"""Enable or disable the dragging of vertical lines
Parameters
----------
state: Qt.Checked or Qt.Unchecked
If Qt.Checked, the vertical lines can be dragged and dropped"""
if state == Qt.Checked:
for mark in self.marks:
mark.draggable = np.unique(np.r_[['v'], mark.draggable])
else:
for mark in self.marks:
mark.draggable = np.array(list(set(mark.draggable) - {'v'}))
[docs] def change_vline_selectable(self, state):
"""Enable or disable the selection of vertical lines
Parameters
----------
state: Qt.Checked or Qt.Unchecked
If Qt.Checked, the vertical lines can be selected"""
if state == Qt.Checked:
for mark in self.marks:
mark.selectable = np.unique(np.r_[['v'], mark.selectable])
else:
for mark in self.marks:
mark.selectable = np.array(list(set(mark.selectable) - {'v'}))
[docs] def change_show_hlines(self, state):
"""Enable of disable the visibility of horizontal lines
Parameters
----------
state: Qt.Checked or Qt.Unchecked
If Qt.Checked, all horizontal lines are hidden"""
if state == Qt.Checked:
for mark in self.marks:
mark.hide_horizontal = False
mark.set_visible(True)
else:
for mark in self.marks:
mark.hide_horizontal = True
mark.set_visible(True)
self.draw_figs()
[docs] def change_show_vlines(self, state):
"""Enable of disable the visibility of vertical lines
Parameters
----------
state: Qt.Checked or Qt.Unchecked
If Qt.Checked, all vertical lines are hidden"""
if state == Qt.Checked:
for mark in self.marks:
mark.hide_vertical = False
mark.set_visible(True)
else:
for mark in self.marks:
mark.hide_vertical = True
mark.set_visible(True)
self.draw_figs()
[docs] def fill_after_adding(self, mark):
if not self._filled:
self.refresh()
[docs] def go_to_right_mark(self):
"""Move the plot to the next right cross mark"""
ax = self.straditizer.marks[0].ax
if ax.xaxis_inverted():
return self.go_to_smaller_x_mark(min(ax.get_xlim()))
else:
return self.go_to_greater_x_mark(max(ax.get_xlim()))
[docs] def go_to_left_mark(self):
"""Move the plot to the previous left cross mark"""
ax = self.straditizer.marks[0].ax
if ax.xaxis_inverted():
return self.go_to_greater_x_mark(max(ax.get_xlim()))
else:
return self.go_to_smaller_x_mark(min(ax.get_xlim()))
[docs] def go_to_greater_x_mark(self, x):
"""Move the plot to the next mark with a x-position greater than `x`
Parameters
----------
x: float
The reference x-position that shall be smaller than the new
centered mark"""
def is_visible(mark):
return (np.searchsorted(np.sort(xlim), mark.xa) == 1).any() and (
np.searchsorted(np.sort(ylim), mark.ya) == 1).any()
ax = next(self.marks).ax
xlim = np.asarray(ax.get_xlim())
ylim = np.asarray(ax.get_ylim())
marks = self.straditizer.marks
if len(marks[0].xa) > 1:
# get the mark in the center
yc = ylim.mean()
try:
mark = min(
filter(is_visible, marks),
key=lambda m: np.abs(m.ya - yc).min())
except ValueError: # empty sequence
mark = min(
marks, key=lambda m: np.abs(m.ya - yc).min())
# if all edges are visible already, we return
if (np.searchsorted(xlim, mark.xa) == 1).all():
return
mask = mark.xa > x
if not mask.any(): # already on the right side
return
i = mark.xa[mask].argmin()
x = mark.xa[mask][i]
dx = np.diff(xlim) / 2.
ax.set_xlim(x - dx, x + dx)
ax.set_ylim(*ax.get_ylim())
else:
distances = (
(mark.xa > x).any() and (mark.xa[mark.xa > x] - x).min()
for mark in marks)
try:
dist, mark = min((t for t in zip(distances, marks) if t[0]),
key=lambda t: t[0])
except ValueError: # empty sequence
return
mask = mark.xa > x
i = mark.xa[mask].argmin()
x = mark.xa[mask][i]
j = np.abs(mark.ya - ylim.mean()).argmin()
y = mark.ya[j]
dx = np.diff(xlim) / 2.
ax.set_xlim(x - dx, x + dx)
dy = np.diff(ylim) / 2.
ax.set_ylim(y - dy, y + dy)
self.straditizer.draw_figure()
[docs] def go_to_smaller_x_mark(self, x):
"""Move the plot to the next mark with a x-position smaller than `x`
Parameters
----------
x: float
The reference x-position that shall be greater than the new
centered mark"""
def is_visible(mark):
return (np.searchsorted(np.sort(xlim), mark.xa) == 1).any() and (
np.searchsorted(np.sort(ylim), mark.ya) == 1).any()
ax = next(self.marks).ax
xlim = np.asarray(ax.get_xlim())
ylim = np.asarray(ax.get_ylim())
marks = self.straditizer.marks
if len(marks[0].xa) > 1:
# get the mark in the center
yc = ylim.mean()
try:
mark = min(
filter(is_visible, marks),
key=lambda m: np.abs(m.ya - yc).min())
except ValueError: # empty sequence
mark = min(
marks, key=lambda m: np.abs(m.ya - yc).min())
# if all edges are visible already, we return
if (np.searchsorted(xlim, mark.xa) == 1).all():
return
mask = mark.xa < x
if not mask.any(): # already on the right side
return
i = mark.xa[mask].argmin()
x = mark.xa[mask][i]
dx = np.diff(xlim) / 2.
ax.set_xlim(x - dx, x + dx)
ax.set_ylim(*ax.get_ylim())
else:
distances = (
(mark.xa < x).any() and (mark.xa[mark.xa < x] - x).min()
for mark in marks)
try:
dist, mark = min((t for t in zip(distances, marks) if t[0]),
key=lambda t: t[0])
except ValueError: # empty sequence
return
mask = mark.xa < x
i = mark.xa[mask].argmin()
x = mark.xa[mask][i]
j = np.abs(mark.ya - ylim.mean()).argmin()
y = mark.ya[j]
dx = np.diff(xlim) / 2.
ax.set_xlim(x - dx, x + dx)
dy = np.diff(ylim) / 2.
ax.set_ylim(y - dy, y + dy)
self.straditizer.draw_figure()
[docs] def go_to_upper_mark(self):
"""Go to the next mark above the current y-limits"""
ax = self.straditizer.marks[0].ax
if ax.xaxis_inverted():
return self.go_to_greater_y_mark(max(ax.get_ylim()))
else:
return self.go_to_smaller_y_mark(min(ax.get_ylim()))
[docs] def go_to_lower_mark(self):
"""Go to the next mark below the current y-limits"""
ax = self.straditizer.marks[0].ax
if ax.xaxis_inverted():
return self.go_to_smaller_y_mark(min(ax.get_ylim()))
else:
return self.go_to_greater_y_mark(max(ax.get_ylim()))
[docs] def go_to_greater_y_mark(self, y):
"""Move the plot to the next mark with a y-position greater than `y`
Parameters
----------
y: float
The reference y-position that shall be smaller than the new
centered mark"""
def is_visible(mark):
return (np.searchsorted(np.sort(xlim), mark.xa) == 1).any() and (
np.searchsorted(np.sort(ylim), mark.ya) == 1).any()
ax = next(self.marks).ax
xlim = np.asarray(ax.get_xlim())
ylim = np.asarray(ax.get_ylim())
marks = self.straditizer.marks
if len(marks[0].ya) > 1:
# get the mark in the center
xc = xlim.mean()
try:
mark = min(
filter(is_visible, marks),
key=lambda m: np.abs(m.xa - xc).min())
except ValueError: # empty sequence
mark = min(
marks, key=lambda m: np.abs(m.xa - xc).min())
# if all edges are visible already, we return
if (np.searchsorted(ylim, mark.ya) == 1).all():
return
mask = mark.ya > y
if not mask.any(): # already on the right side
return
i = mark.ya[mask].argmin()
y = mark.ya[mask][i]
dy = np.diff(ylim) / 2.
ax.set_ylim(y - dy, y + dy)
else:
distances = (
(mark.ya > y).any() and (mark.xa[mark.ya > y] - y).min()
for mark in marks)
try:
dist, mark = min((t for t in zip(distances, marks) if t[0]),
key=lambda t: t[0])
except ValueError: # empty sequence
return
mask = mark.ya > y
i = mark.ya[mask].argmin()
y = mark.ya[mask][i]
j = np.abs(mark.xa - xlim.mean()).argmin()
x = mark.xa[j]
dx = np.diff(xlim) / 2.
ax.set_xlim(x - dx, x + dx)
dy = np.diff(ylim) / 2.
ax.set_ylim(y - dy, y + dy)
self.straditizer.draw_figure()
[docs] def go_to_smaller_y_mark(self, y):
"""Move the plot to the next mark with a y-position smaller than `x`
Parameters
----------
y: float
The reference y-position that shall be smaller than the new
centered mark"""
def is_visible(mark):
return (np.searchsorted(np.sort(xlim), mark.xa) == 1).any() and (
np.searchsorted(np.sort(ylim), mark.ya) == 1).any()
ax = next(self.marks).ax
xlim = np.asarray(ax.get_xlim())
ylim = np.asarray(ax.get_ylim())
marks = self.straditizer.marks
if len(marks[0].ya) > 1:
# get the mark in the center
xc = xlim.mean()
try:
mark = min(
filter(is_visible, marks),
key=lambda m: np.abs(m.xa - xc).min())
except ValueError: # empty sequence
mark = min(
marks, key=lambda m: np.abs(m.xa - xc).min())
# if all edges are visible already, we return
if (np.searchsorted(ylim, mark.ya) == 1).all():
return
mask = mark.ya < y
if not mask.any(): # already on the right side
return
i = mark.ya[mask].argmin()
y = mark.ya[mask][i]
dy = np.diff(ylim) / 2.
ax.set_ylim(y - dy, y + dy)
else:
distances = (
(mark.ya < y).any() and (mark.ya[mark.ya < y] - y).min()
for mark in marks)
try:
dist, mark = min((t for t in zip(distances, marks) if t[0]),
key=lambda t: t[0])
except ValueError: # empty sequence
return
mask = mark.ya < y
i = mark.ya[mask].argmin()
y = mark.ya[mask][i]
j = np.abs(mark.xa - xlim.mean()).argmin()
x = mark.xa[j]
dx = np.diff(xlim) / 2.
ax.set_xlim(x - dx, x + dx)
dy = np.diff(ylim) / 2.
ax.set_ylim(y - dy, y + dy)
self.straditizer.draw_figure()
[docs] def remove_actions(self):
"""Remove the navigation actions from the toolbar"""
if self._toolbar is None:
return
tb = self._toolbar
for a in self.tb_actions:
tb.removeAction(a)
self.tb_actions.clear()
[docs] def refresh(self):
"""Reimplemented to also set the properties of this widget
"""
super(MarkerControl, self).refresh()
if self.straditizer is not None and self.straditizer.marks:
self._filled = True
mark = self.straditizer.marks[0]
self.fill_from_mark(mark)
self.add_toolbar_widgets(mark)
else:
self.remove_actions()
self._filled = False
if self.straditizer is not None:
self.straditizer.mark_added.connect(self.fill_after_adding)
self.straditizer.mark_added.connect(self.update_mark)