Source code for mpl_qt_viz.visualizers._dockPlot

# Copyright 2018-2021 Nick Anthony, Backman Biophotonics Lab, Northwestern University
#
# This file is part of mpl_qt_viz.
#
# mpl_qt_viz 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.
#
# mpl_qt_viz 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 mpl_qt_viz.  If not, see <https://www.gnu.org/licenses/>.

import typing

from PyQt5 import QtCore
from PyQt5.QtWidgets import QMainWindow, QApplication, QDockWidget, QWidget, QGridLayout
import matplotlib.pyplot as plt
from matplotlib.backends.backend_qt5 import NavigationToolbar2QT
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg


[docs]class DockablePlotWindow(QMainWindow): def __init__(self, title: str = "Dockable Plots"): super().__init__(parent=None) self.setWindowTitle(title) self._plots: typing.List[DockablePlot] = [] self.setDockOptions(QMainWindow.AnimatedDocks | QMainWindow.AllowNestedDocks | QMainWindow.AllowTabbedDocks | QMainWindow.GroupedDragging) self.resize(1024, 768) self.show() def subplots(self, title: str, dockArea: str = 'top', **kwargs): dockMap = {'top': QtCore.Qt.TopDockWidgetArea, 'bottom': QtCore.Qt.BottomDockWidgetArea, 'left': QtCore.Qt.LeftDockWidgetArea, 'right': QtCore.Qt.RightDockWidgetArea} try: dockArea = dockMap[dockArea] except KeyError: raise ValueError(f"Dock are `{dockArea}` is not supported. must be: {list(dockMap.keys())}") fig, ax = plt.subplots(**kwargs) fig.suptitle(title) suffix = 0 finalTitle = title titles = [i.title for i in self._plots] while finalTitle in titles: suffix += 1 finalTitle = f"{title}_{suffix}" plot = DockablePlot(fig, finalTitle, self) dockAreas = [self.dockWidgetArea(i) for i in self._plots] if dockArea not in dockAreas: self.addDockWidget(dockArea, plot) else: existing = self._plots[dockAreas.index(dockArea)] self.tabifyDockWidget(existing, plot) self._plots.append(plot) return fig, ax
class DockablePlot(QDockWidget): def __init__(self, figure: plt.Figure, title: str, parent: QWidget = None): super().__init__(title, parent=parent) self._canv = FigureCanvasQTAgg(figure=figure) self._canv.setFocusPolicy(QtCore.Qt.ClickFocus) self._canv.setFocus() self._bar = NavigationToolbar2QT(self._canv, self) l = QGridLayout() l.addWidget(self._canv, 0, 0) l.addWidget(self._bar, 1, 0) self._contentWidget = QWidget(self) self._contentWidget.setLayout(l) self.setWidget(self._contentWidget) @property def title(self) -> str: return self.windowTitle() if __name__ == '__main__': import numpy as np import random names = ['plot', 'data', 'bs'] areas = ['left', 'right', 'bottom', 'top'] def plot(ax): x = np.linspace(0, 1) y = np.random.random(x.size) ax.plot(x, y, ls='--') def im(ax): d = np.random.random((50, 50)) ax.imshow(d) funcs = [plot, im] app = QApplication([]) w = DockablePlotWindow() for i in range(10): fig, ax = w.subplots(random.choice(names), dockArea=random.choice(areas)) random.choice(funcs)(ax) w2 = DockablePlotWindow(title="2nd Plot Window") for i in range(10): x = np.linspace(0, 1) y = np.random.random(x.size) fig, ax = w2.subplots(random.choice(names), dockArea=random.choice(areas)) ax.plot(x, y, ls='--') app.exec() a = 1