I have some data plotted which I force to scientific notation to powers of 10 (instead of exponential). Heres a snippet of the code:
import matplotlib.ticker
You may set the offset to invisible, such that it does not appear in its original position.
ax.yaxis.offsetText.set_visible(False)
You may then get the offset from the formatter an update the label with it
offset = ax.yaxis.get_major_formatter().get_offset()
ax.yaxis.set_label_text("original label" + " " + offset)
such that it appears inside the label.
The following automates this using a class with a callback, such that if the offset changes, it will be updated in the label.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.ticker as mticker
class Labeloffset():
def __init__(self, ax, label="", axis="y"):
self.axis = {"y":ax.yaxis, "x":ax.xaxis}[axis]
self.label=label
ax.callbacks.connect(axis+'lim_changed', self.update)
ax.figure.canvas.draw()
self.update(None)
def update(self, lim):
fmt = self.axis.get_major_formatter()
self.axis.offsetText.set_visible(False)
self.axis.set_label_text(self.label + " "+ fmt.get_offset() )
x = np.arange(5)
y = np.exp(x)*1e-6
fig, ax = plt.subplots()
ax.plot(x,y, marker="d")
formatter = mticker.ScalarFormatter(useMathText=True)
formatter.set_powerlimits((-3,2))
ax.yaxis.set_major_formatter(formatter)
lo = Labeloffset(ax, label="my label", axis="y")
plt.show()
Minimal example for a GUI, where draw()
should only be called once per plot refresh. Keeps the correct scale factor. Can also be used with locked exponent, e.g. like here.
As an example I've just added a simple button to refresh the plot, but it could just as well be any other event.
from PyQt5.Qt import *
from PyQt5 import QtWidgets, QtCore # sorry about the Qt Creator mess
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
from matplotlib.ticker import ScalarFormatter
import numpy as np
class WidgetPlot(QWidget):
def __init__(self, parent=None):
QWidget.__init__(self, parent)
self.setLayout(QVBoxLayout())
self.canvas = PlotCanvas(self)
self.layout().addWidget(self.canvas)
class PlotCanvas(FigureCanvas):
def __init__(self, parent = None, width = 5, height = 5, dpi = 100):
self.fig = Figure(figsize = (width, height), dpi = dpi, tight_layout = True)
self.ax = self.fig.add_subplot(111)
FigureCanvas.__init__(self, self.fig)
def majorFormatterInLabel(self, ax, axis, axis_label, major_formatter):
if axis == "x":
axis = ax.xaxis
if axis == "y":
axis = ax.yaxis
axis.set_major_formatter(major_formatter)
axis.offsetText.set_visible(False)
exponent = axis.get_offset_text().get_text()
axis.set_label_text(axis_label + " (" + exponent + ")")
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(674, 371)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.gridLayoutWidget = QtWidgets.QWidget(self.centralwidget)
self.gridLayoutWidget.setGeometry(QtCore.QRect(50, 10, 601, 281))
self.gridLayoutWidget.setObjectName("gridLayoutWidget")
self.mpl_layoutBox = QtWidgets.QGridLayout(self.gridLayoutWidget)
self.mpl_layoutBox.setContentsMargins(0, 0, 0, 0)
self.mpl_layoutBox.setObjectName("mpl_layoutBox")
self.pushButton = QtWidgets.QPushButton(self.centralwidget)
self.pushButton.setGeometry(QtCore.QRect(280, 300, 113, 32))
self.pushButton.setObjectName("pushButton")
MainWindow.setCentralWidget(self.centralwidget)
self.w = WidgetPlot()
self.canvas = self.w.canvas
self.mpl_layoutBox.addWidget(self.w)
self.pushButton.clicked.connect(self.refresh)
def refresh(self):
self.canvas.ax.clear()
# Could've made it more beautiful. In any case, the cost of doing this is sub-ms.
formatter = ScalarFormatter()
formatter.set_powerlimits((-1, 1))
self.canvas.majorFormatterInLabel(self.canvas.ax, "y", "label", major_formatter = formatter)
r = np.random.choice((1e3, 1e5, 1e7, 1e9, 1e100))
self.canvas.ax.plot(r)
self.canvas.draw()
if __name__ == "__main__":
import sys
app = QApplication(sys.argv)
TraceWindow = QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(TraceWindow)
TraceWindow.show()
sys.exit(app.exec_())