Source code for utils.CustomNavBar
from PyQt5.QtCore import qVersion, QSize
from PyQt5.QtGui import *
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import *
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg, NavigationToolbar2QT as NavigationToolbar
from frame_controls.plot_properties import PPropsControl
[docs]class MyCustomToolbar(NavigationToolbar):
def __init__(self, plotCanvas, parent, coordinates=True):
self.canvas = plotCanvas
self.ax = self.canvas.ax
# self.setContentsMargins(0, 0, 0, 0)
# propertiesl widget
self.pprops = PPropsControl(self.canvas)
# create the default toolbar
NavigationToolbar.__init__(self, plotCanvas, parent, coordinates)
toolitems = [*NavigationToolbar.toolitems]
# print(toolitems)
# check toolitems
toolitems = [('Home', 'Reset original view', 'home', 'home'),
('Back', 'Back to previous view', 'back', 'back'),
('Forward', 'Forward to next view', 'forward', 'forward'),
(None, None, None, None),
('Pan', 'Left button pans, Right button zooms\nx/y fixes axis, CTRL fixes aspect', 'pan', 'pan'),
('Zoom', 'Zoom to rectangle\nx/y fixes axis, CTRL fixes aspect', 'zoom', 'zoom'),
('Subplots', 'Configure subplots', 'subplots', 'configure_subplots'),
('Customize', 'Edit axis, curve and image parameters', 'options', 'edit_parameters'),
(None, None, None, None),
('Plot format', 'Edit rcParams, curve and image parameters', 'options', 'rcParams'),
('Linear', 'Save the figure', 'lin', 'linear_scale'),
('Log', 'Save the figure', 'log', 'log_scale'),
('Decibel', 'Save the figure', 'dB', 'decibel_scale'),
('Grid', 'Toggle Grid', 'dB', 'toggle_grid'),
(None, None, None, None),
("Size", None, None, None),
('Save', 'Save the figure', 'save', 'save_figure'),
]
self.canvas.toolbar.setContentsMargins(0, 0, 0, 0)
self.canvas.toolbar.setIconSize(QSize(20, 20))
# print(type(self.canvas.toolbar))
actions = self.findChildren(QAction)
for a in actions:
self.removeAction(a)
self._actions = {} # mapping of toolitem method names to QActions.
for text, tooltip_text, image_file, callback in toolitems:
if text is None:
self.addSeparator()
elif text == 'Size':
# size definitions
plot_settings_options = ['Presentation', 'Wakefield', 'Square', 'Portrait', 'Landscape', 'Custom']
self.cb_Save_Plot_Settings = QComboBox()
for a in plot_settings_options:
self.cb_Save_Plot_Settings.addItem(a)
self.addWidget(self.cb_Save_Plot_Settings)
elif text == 'Grid':
self.cb_Grid = QCheckBox()
self.cb_Grid.clicked.connect(lambda: self.toggle_grid())
self.addWidget(self.cb_Grid)
else:
a = self.addAction(self._icon(image_file + '.png'),
text, getattr(self, callback))
self._actions[callback] = a
if callback in ['zoom', 'pan']:
a.setCheckable(True)
if tooltip_text is not None:
a.setToolTip(tooltip_text)
# Add the (x, y) location widget at the right side of the toolbar
# The stretch factor is 1 which means any resizing of the toolbar
# will resize this label instead of the buttons.
if coordinates:
self.locLabel = QLabel("", self)
self.locLabel.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
self.locLabel.setSizePolicy(QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Ignored))
labelAction = self.addWidget(self.locLabel)
labelAction.setVisible(True)
[docs] def save_figure(self, *args):
options = QFileDialog.Options()
# options |= QFileDialog.DontUseNativeDialog
fileName, _ = QFileDialog.getSaveFileName(None, "QFileDialog.getSaveFileName()", "", "PNG Files (*.png)", options=options)
if fileName:
if fileName.split('.')[-1] != 'png':
fileName = f'{fileName}.png'
try:
if self.cb_Save_Plot_Settings.currentText() == 'Presentation':
# get size of figure before change
fig_size = self.canvas.fig.get_size_inches()
self.canvas.fig.set_size_inches(8, 6) # actual size = 8*dpi, 6*dpi; dpi = 100
elif self.cb_Save_Plot_Settings.currentText() == 'Wakefield':
# get size of figure before change
fig_size = self.canvas.fig.get_size_inches()
self.canvas.fig.set_size_inches(10, 5) # forward=False
elif self.cb_Save_Plot_Settings.currentText() == 'Square':
# get size of figure before change
fig_size = self.canvas.fig.get_size_inches()
self.canvas.fig.set_size_inches(8, 8) # forward=False
elif self.cb_Save_Plot_Settings.currentText() == 'Landscape':
# get size of figure before change
fig_size = self.canvas.fig.get_size_inches()
self.canvas.fig.set_size_inches(12, 8) # forward=False
else:
# get size of figure before change
fig_size = self.canvas.fig.get_size_inches()
# open popup window to get size input
size_input_dialog = SizeInputDialog(self)
size_input_dialog.show()
if size_input_dialog.exec_():
width, height = size_input_dialog.getInputs()
width, height = float(width)/self.canvas.fig.dpi, float(height)/self.canvas.fig.dpi
self.canvas.fig.set_size_inches(width, height) # , forward=False
self.canvas.fig.savefig(fileName, transparent=True, bbox_inches='tight', pad_inches=0.2)
# restore windows size
self.canvas.fig.set_size_inches(fig_size[0], fig_size[1]) # actual size = 8*dpi, 6*dpi; dpi = 100
self.canvas.draw()
self.canvas.flush_events()
except Exception as e:
print("Cannot save file -> ", e)
else:
print('Enter a valid filename')
def _icon(self, name):
"""
Construct a `.QIcon` from an image file *name*, including the extension
and relative to Matplotlib's "images" data directory.
"""
# if qVersion() >= '5.':
# name = name.replace('.png', '_large.png')
pm = QPixmap(f":/icons/icons/PNG/{name}")
if self.palette().color(self.backgroundRole()).value() < 128:
icon_color = self.palette().color(self.foregroundRole())
mask = pm.createMaskFromColor(QColor('black'),
Qt.MaskOutColor)
pm.fill(icon_color)
pm.setMask(mask)
return QIcon(pm)
[docs] def toggle_grid(self):
if self.cb_Grid.checkState() == 2:
self.canvas.fig.get_axes()[0].grid(True, 'both')
else:
self.canvas.fig.get_axes()[0].grid(False)
self.canvas.draw()
[docs] def rcParams(self):
# pop up plot properties widget
# create pop up widget instance
self.pprops.ppropsUI.cb_Plots.addItems([x.get_label() for x in self.canvas.fig.get_axes()])
# get and set default parameters
# legend
# update legend
handles, labels = self.canvas.ax.get_legend_handles_labels()
legend_labels = '%%'.join(labels)
self.pprops.ppropsUI.le_Legend.setText(legend_labels)
# axis limits
self.pprops.ppropsUI.dsb_X0.setValue(0.45*(self.canvas.ax.get_xlim()[1]+self.canvas.ax.get_xlim()[0]))
self.pprops.ppropsUI.dsb_X1.setValue(0.55*(self.canvas.ax.get_xlim()[1]+self.canvas.ax.get_xlim()[0]))
self.pprops.ppropsUI.dsb_Y0.setValue(0.45*(self.canvas.ax.get_ylim()[1]+self.canvas.ax.get_ylim()[0]))
self.pprops.ppropsUI.dsb_Y1.setValue(0.55*(self.canvas.ax.get_ylim()[1]+self.canvas.ax.get_ylim()[0]))
self.pprops.w_PProps.show()
[docs]class SizeInputDialog(QDialog):
def __init__(self, parent=None):
super().__init__(parent)
self.first = QLineEdit(self)
self.second = QLineEdit(self)
# add validator for only integers
self.first.textChanged.connect(lambda: self.validating(self.first))
self.second.textChanged.connect(lambda: self.validating(self.second))
buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel, self)
layout = QFormLayout(self)
layout.addRow("Width", self.first)
layout.addRow("Height", self.second)
layout.addWidget(buttonBox)
buttonBox.accepted.connect(self.accept)
buttonBox.rejected.connect(self.reject)
[docs] def validating(self, le):
validation_rule = QDoubleValidator(1, 3840, 0)
if validation_rule.validate(le.text(), 20)[0] == QValidator.Acceptable:
le.setFocus()
else:
le.setText('')