Source code for mytk.figures
import importlib
from .base import Base
from .modulesmanager import ModulesManager
[docs]
class Figure(Base):
"""Wrapper around matplotlib Figure for embedding plots in tkinter."""
def __init__(self, figure=None, figsize=None):
Base.__init__(self)
self._figure = figure
self.figsize = figsize
if self.figsize is None:
self.figsize = (6, 4)
self.canvas = None
self.toolbar = None
[docs]
def is_environment_valid(self):
"""Check that matplotlib is installed and importable."""
ModulesManager.install_and_import_modules_if_absent(
{"matplotlib": "matplotlib"}
)
self.matplotlib = ModulesManager.imported.get("matplotlib", None)
if self.matplotlib is not None:
self.plt = importlib.import_module("matplotlib.pyplot")
self.MPLFigure = importlib.import_module("matplotlib.figure").Figure
self.FigureCanvasTkAgg = importlib.import_module(
"matplotlib.backends.backend_tkagg"
).FigureCanvasTkAgg
self.NavigationToolbar2Tk = importlib.import_module(
"matplotlib.backends.backend_tkagg"
).NavigationToolbar2Tk
return all(
v is not None
for v in [
self.matplotlib,
self.plt,
self.MPLFigure,
self.FigureCanvasTkAgg,
self.NavigationToolbar2Tk,
]
)
[docs]
def create_widget(self, master):
"""Create the matplotlib canvas and navigation toolbar."""
self.parent = master
if self.figure is None:
self.figure = self.MPLFigure(figsize=self.figsize, dpi=100)
self.canvas = self.FigureCanvasTkAgg(self.figure, master=master)
self.widget = self.canvas.get_tk_widget()
self.toolbar = self.NavigationToolbar2Tk(
self.canvas, master, pack_toolbar=False
)
self.toolbar.update()
@property
def figure(self):
"""The underlying matplotlib Figure."""
return self._figure
@figure.setter
def figure(self, figure):
if self._figure is not None:
self._figure.close()
self._figure = figure
# HACK : For now, we need to destroy the old widget and canvas
self.create_widget(self.parent)
@property
def first_axis(self):
"""The first axes of the figure, or None if unavailable."""
if self.figure is not None:
axes = self.figure.axes
if len(axes) > 0:
return axes[0]
return None
@property
def axes(self):
"""All axes of the figure, or None if the figure is not set."""
if self.figure is not None:
return self.figure.axes
return None
[docs]
def styles_pointmarker(self, linestyle=""):
"""Return a list of point-marker style dictionaries for plotting."""
default_size = 8
plain_black = dict(
fillstyle="full",
marker="o",
linestyle=linestyle,
markersize=default_size,
color="black",
markerfacecolor="black",
markerfacecoloralt="black",
markeredgecolor="black",
)
circle_black = dict(
fillstyle="full",
marker="o",
linestyle=linestyle,
markersize=default_size,
color="black",
markerfacecolor="white",
markerfacecoloralt="white",
markeredgecolor="black",
)
plain_s_black = dict(
fillstyle="full",
marker="s",
linestyle=linestyle,
markersize=default_size,
color="black",
markerfacecolor="black",
markerfacecoloralt="black",
markeredgecolor="black",
)
square_black = dict(
fillstyle="full",
marker="s",
linestyle=linestyle,
markersize=default_size,
color="black",
markerfacecolor="white",
markerfacecoloralt="white",
markeredgecolor="black",
)
plain_red = dict(
fillstyle="full",
marker="o",
linestyle=linestyle,
markersize=default_size,
color="red",
markerfacecolor="red",
markerfacecoloralt="red",
markeredgecolor="red",
)
circle_red = dict(
fillstyle="none",
marker="o",
linestyle=linestyle,
markersize=default_size,
color="red",
markerfacecolor="white",
markerfacecoloralt="white",
markeredgecolor="red",
)
styles = [
plain_black,
circle_black,
plain_s_black,
square_black,
plain_red,
circle_red,
]
return styles
[docs]
def styles_points_linemarkers(self, linestyle="-"):
"""Return a list of point-and-line marker style dictionaries for plotting."""
default_size = 8
plain_black = dict(
fillstyle="full",
marker="o",
linestyle=linestyle,
markersize=default_size,
color="black",
markerfacecolor="black",
markerfacecoloralt="black",
markeredgecolor="black",
)
circle_black = dict(
fillstyle="none",
marker="o",
linestyle=linestyle,
markersize=default_size,
color="black",
markerfacecolor=None,
markerfacecoloralt=None,
markeredgecolor="black",
)
plain_s_black = dict(
fillstyle="full",
marker="s",
linestyle=linestyle,
markersize=default_size,
color="black",
markerfacecolor="black",
markerfacecoloralt="black",
markeredgecolor="black",
)
square_black = dict(
fillstyle="none",
marker="s",
linestyle=linestyle,
markersize=default_size,
color="black",
markerfacecolor=None,
markerfacecoloralt=None,
markeredgecolor="black",
)
plain_red = dict(
fillstyle="full",
marker="o",
linestyle=linestyle,
markersize=default_size,
color="red",
markerfacecolor="red",
markerfacecoloralt="red",
markeredgecolor="red",
)
circle_red = dict(
fillstyle="none",
marker="o",
linestyle=linestyle,
markersize=default_size,
color="red",
markerfacecolor=None,
markerfacecoloralt=None,
markeredgecolor="red",
)
styles = [
plain_black,
circle_black,
plain_s_black,
square_black,
plain_red,
circle_red,
]
return styles
[docs]
class XYPlot(Figure):
"""Simple XY line plot backed by matplotlib."""
def __init__(self, figsize):
super().__init__(figsize=figsize)
self.x = []
self.y = []
self.x_range = 10
# self.style = "https://raw.githubusercontent.com/dccote/Enseignement/master/SRC/dccote-basic.mplstyle"
[docs]
def create_widget(self, master, **kwargs):
"""Create the plot widget and draw the initial plot."""
super().create_widget(master, *kwargs)
if self.first_axis is None:
self.figure.add_subplot()
self.update_plot()
[docs]
def clear_plot(self):
"""Clear all data and redraw the plot."""
self.x = []
self.y = []
self.first_axis.clear()
self.update_plot()
[docs]
def update_plot(self):
"""Redraw the plot with the current data."""
# with plt.style.context(self.style):
self.first_axis.clear()
self.first_axis.plot(self.x, self.y, "k-")
self.figure.canvas.draw()
self.figure.canvas.flush_events()
[docs]
def append(self, x, y):
"""Append a data point to the plot."""
self.x.append(x)
self.y.append(y)
# self.x = self.x[-self.x_range : -1]
# self.y = self.y[-self.x_range : -1]
[docs]
class Histogram(Figure):
"""Histogram plot backed by matplotlib and numpy."""
def __init__(self, figsize):
super().__init__(figsize=figsize)
self.x = []
self.y = []
[docs]
def is_environment_valid(self):
"""Check that matplotlib and numpy are installed and importable."""
if super().is_environment_valid():
ModulesManager.install_and_import_modules_if_absent({"numpy": "numpy"})
return ModulesManager.imported["numpy"]
else:
return False
[docs]
def create_widget(self, master, **kwargs):
"""Create the histogram widget and draw the initial plot."""
super().create_widget(master, *kwargs)
if self.first_axis is None:
self.figure.add_subplot()
self.update_plot()
[docs]
def clear_plot(self):
"""Clear all histogram data and the axes."""
self.x = []
self.y = []
self.first_axis.clear()
[docs]
def update_plot(self):
"""Redraw the histogram with the current data."""
if len(self.x) > 1:
colors = ["red", "green", "blue"]
for i, y in enumerate(self.y):
self.first_axis.stairs(y[:-1], self.x, color=colors[i])
numpy = ModulesManager.imported["numpy"]
self.first_axis.set_ylim((0, numpy.mean(self.y) + numpy.std(self.y) * 2))
self.first_axis.set_yticklabels([])
self.first_axis.set_xticklabels([])
self.first_axis.set_xticks([])
self.first_axis.set_yticks([])
self.figure.canvas.draw()
self.figure.canvas.flush_events()