I write a small program using tkinter and matplotlib which plots a normal distribution curve and a confidence interval. I want the plot to have a small text box in the upper left corner using plt.text()
which holds some information about the user's input and some other relevant information calculated by the backend model (e.g. reliability, normvalue, etc.).
I explicitly want to avoid using fig, ax = plt.subplots()
since this would open two windows (where one is empty and the other one contains the plot).
However i realized that I have to create a subplot in order to use transform=ax.transAxes
and to make sure that the textbox appears in my plot window (see text caption not appearing matplotlib). Without this keyword argument I am forced to use the explicit data coordinates which I cannot do since the axis coordinates can change depending on the user input.
How can I place the textbox in the upper left corner without opening two windows?
Here is my code:
import tkinter as tk
import matplotlib.pyplot as plt
from matplotlib.figure import Figure
import numpy as np
import math
from scipy.stats import norm
from scipy.stats import zscore
class Application:
def __init__(self, master):
self.master = master
self.callPlotWindow()
def callPlotWindow(self):
plotdata = dict()
plotdata['reliability'] = 0.94
plotdata['sd'] = 10
plotdata['mean'] = 50
plotdata['normvalue'] = 66
plotdata['confidence_level'] = '80%'
plotdata['question'] = 'einseitig'
plotdata['hypothesis'] = 'Äquivalenzhypothese'
plotdata['plot_ci'] = 4.12298113505264
plotdata['plot_ci_lower'] = 63.93850943247368
plotdata['plot_ci_upper'] = 68.06149056752632
# turn on interactive mode
plt.ion()
# if there is already a plot window clear old plot
if plt:
plt.clf()
# create x values for normal distribution
x_normdist = np.concatenate((
np.linspace(plotdata["mean"] - 3 * plotdata["sd"], plotdata["mean"] - 2 * plotdata["sd"],endpoint=False),
np.linspace(plotdata["mean"] - 2 * plotdata["sd"], plotdata["mean"] - 1 * plotdata["sd"],endpoint=False),
np.linspace(plotdata["mean"] - 1 * plotdata["sd"], plotdata["mean"] + 1 * plotdata["sd"],endpoint=False),
np.linspace(plotdata["mean"] + 1 * plotdata["sd"], plotdata["mean"] + 2 * plotdata["sd"],endpoint=False),
np.linspace(plotdata["mean"] + 2 * plotdata["sd"], plotdata["mean"] + 3 * plotdata["sd"])
))
# plot normal distribution curve
y_normdist = norm.pdf(x_normdist,plotdata["mean"],plotdata["sd"])
plt.plot(x_normdist,y_normdist)
# create logical lists which are used for 'where'-argument in fill-between method
average = (x_normdist >= (plotdata["mean"] - 1 * plotdata["sd"])) & (x_normdist <= (plotdata["mean"] + 1 * plotdata["sd"]))
above_and_below_average = (x_normdist >= (plotdata["mean"] - 2 * plotdata["sd"])) & (x_normdist <= (plotdata["mean"] - 1 * plotdata["sd"])) | (x_normdist >= (plotdata["mean"] + 1 * plotdata["sd"])) & (x_normdist <= (plotdata["mean"] + 2 * plotdata["sd"]))
far_above_and_below_average = (x_normdist >= (plotdata["mean"] - 3 * plotdata["sd"])) & (x_normdist <= (plotdata["mean"] - 2 * plotdata["sd"])) | (x_normdist >= (plotdata["mean"] + 2 * plotdata["sd"])) & (x_normdist <= (plotdata["mean"] + 3 * plotdata["sd"]))
regions = [average,
above_and_below_average,
far_above_and_below_average
]
alpha_values = [0.75,0.5,0.25]
region_labels = [
"durchschnittlich",
"unter-/überdurchschnittlich",
"weit unter-/überdurchschnittlich"
]
# shade regions under curve, use different alpha channel values and labels
for idx,region in enumerate(regions):
plt.fill_between(x_normdist, y_normdist,color="C0",alpha=alpha_values[idx],label=region_labels[idx],where=regions[idx])
# plot confidence interval
plt.errorbar(x=plotdata["normvalue"],y=0,xerr=plotdata["plot_ci"],fmt=".k",capsize=10)
# set x and y axis title
plt.xlabel(xlabel="Normwert")
plt.ylabel(ylabel=r'$\phi_{\mu\sigma}(\mathcal{X})$')
textstr = '\n'.join((
r'$Normwert: %.2f$' % (plotdata["normvalue"], ),
r'$Reliabilität: %.2f$' % (plotdata["reliability"], ),
r'$Mittelwert des Normwertes: %.2f$' % (plotdata["mean"], ),
r'$Standardabweichung des Normwertes: %.2f$' % (plotdata["sd"],),
r'$Untere KI-Grenze: %.2f$' % (round(plotdata["plot_ci_lower"],2),),
r'$Obere KI-Grenze: %.2f$' % (round(plotdata["plot_ci_upper"],2),),
))
# these are matplotlib.patch.Patch properties
props = dict(boxstyle='round', facecolor='wheat', alpha=0.5)
# create legend
plt.legend(loc='upper right', prop={'size': 8})
# place a text box in upper left in axes coords
plt.text(0.05, 0.95,textstr,fontsize=14,verticalalignment='top',bbox=props)
if __name__ == "__main__":
root = tk.Tk()
my_gui = Application(root)
root.mainloop()