Scatter plot in Tkinter using Matplotlib. No plot is showing - python

I have scoured the web for creating a live matplotlib scatter plot within a tkinter window but have found copious amounts of varying information making it confusing to decipher.As an example, I sometimes see some people use matplotlib.pyplot and I sometimes see some people use matplotlib.figure. I have no idea what the real differences between these two modules are.
I have created the example code below which I thought should simply create a matplotlib scatter plot inside the tkinter root window when I click the "Graph It" button. It does nothing when I click it though. The ultimate goal is to have a scatter plot within tkinter that updates whenever new data is read from a sensor but I'm starting simple. It should also be noted this is my first exposure to matplotlib so it may be something trivial I'm overlooking. Any help is appreciated.
#Python 3.7.9#
import tkinter as tk
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import (FigureCanvasTkAgg,NavigationToolbar2Tk)
import numpy as np
figure = Figure(figsize = (5,5), dpi = 100)
ax = figure.add_subplot(111)
def plot():
x = np.random.rand(1,10)
y = np.random.rand(1,10)
ax.scatter(x,y)
root = tk.Tk()
canvas = FigureCanvasTkAgg(figure, root)
canvas.draw()
canvas.get_tk_widget().pack(pady = 10)
toolbar = NavigationToolbar2Tk(canvas,root)
toolbar.update()
canvas.get_tk_widget().pack()
button = tk.Button(root, text = "Graph It", command = plot)
button.pack()
root.mainloop()

The problem lies in that you do not "update" the plot.
With the minimal modification to your code, you just have to arrange to redraw the figure.
For example, you can use your wrapper canvas object (or the figure.canvas) to update the figure's canvas in each data update step as follows:
import tkinter as tk
import numpy as np
from matplotlib.backends.backend_tkagg import (FigureCanvasTkAgg, NavigationToolbar2Tk)
from matplotlib.figure import Figure
figure = Figure(figsize=(5, 5), dpi=100)
ax = figure.add_subplot(111)
def plot():
x = np.random.rand(1, 10)
y = np.random.rand(1, 10)
ax.scatter(x, y)
canvas.draw()
root = tk.Tk()
canvas = FigureCanvasTkAgg(figure, root)
canvas.draw()
canvas.get_tk_widget().pack(pady=10)
toolbar = NavigationToolbar2Tk(canvas, root)
toolbar.update()
canvas.get_tk_widget().pack()
button = tk.Button(root, text="Graph It", command=plot)
button.pack()
root.mainloop()
Output window:
However, in this case you add/create a new scatter plot in each update step. If you wish to extend the original data and update according to the updated data, you can find different methods in this post to update a single scatter plot.

Related

make plt graph fill area without padding

I am trying to integrate matplotlib into tkinter, this is doable with FigureCanvasTkAgg. However, in my case I want to remove the y axis ticks and then make the graph cover the entire available area (i.e. remove the padding). Removing the y axis was not difficult but I cannot find any information on removing the padding.
If I found a solution (for example: Remove padding from matplotlib plotting) it included using plt.savefig, but saving the image wouldn't help me. I guess I could save the image and display it that way although that would feel pretty hacky. Is there a better way?
My code so far:
import customtkinter
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure
import matplotlib
import numpy as np
root = customtkinter.CTk()
root.geometry('600x500')
# figure
figure = plt.Figure()
ax = figure.add_subplot(111)
ax.yaxis.set_visible(False)
t = np.arange(0, 3, .01)
ax.plot(t, 2 * np.sin(2 * np.pi * t))
# add widget
canvas = FigureCanvasTkAgg(figure, master=root)
canvas.draw()
canvas.get_tk_widget().pack(fill='both', expand=True)
root.mainloop()

How to format equation with matplotlib so that all characters (except sub- and superscipts) are the same size?

I'm trying to write equations in Tkinter widgets. I managed to find help on this site, and implemented the solution with matplotlib. However, one problem I'm facing now is that the font size of the equation I write is not consistent throughout the equation. The fraction part is much smaller than the rest of the equation. I will be in need of writing many more complicated equations, and I would like for all the normal (not subscript and superscript) characters to be equal. How can I do this?
Part of the code I'm using for this is:
import matplotlib
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
def popup(msg):
popup = tk.Tk()
popup.geometry("400x200")
popup.wm_title("error")
popup.resizable(False,False)
label = tk.Label(popup,anchor=CENTER)
label.grid(row=0,column=0)
tk.Grid.rowconfigure(popup,0,weight=1)
tk.Grid.columnconfigure(popup,0,weight=1)
ok = tk.Button(popup, text="OK", command=popup.destroy)
ok.grid(row=1,column=0)
fig=matplotlib.figure.Figure(figsize=(4.17, 1), dpi=100)
ax = fig.add_subplot()
ax.get_xaxis().set_visible(False)
ax.get_yaxis().set_visible(False)
fig.patch.set_facecolor('#F0F0F0')
ax.axis("off")
canvas = FigureCanvasTkAgg(fig, master=label)
canvas.get_tk_widget().grid()
ax.text(0.5, 0.5, msg, fontsize=15, horizontalalignment='center', verticalalignment='center', transform=ax.transAxes)
canvas.draw()
popup.mainloop()
The msg is written as r"$\frac{\sigma\ '_{v}}{p_{atm}}$ must be > 0.25".
If you use a "display fraction" with \dfrac in place of \frac the fonts in the fraction will be the same size:
msg = r"$\dfrac{\sigma\ '_{v}}{p_{atm}}$ must be > 0.25"

matplotlib and tkinter: fixed figure size / scrolling or zooming on window resize

i want to show a figure using matplotlib in a tkinter window. This works fine using FigureCanvasTkAgg, but it ignores my proposed figsize
Is it omehow possible to show this plot in such a way that it preserves its figsize, no matter the window size? So when it is smaller than the window size (or its allocated space in a grid) than it should be centered in the middle, when its larger scroll bars should appear, but it should never be resized.
Additionally I would love to be able to "zoom in" to that Canvas, so again without changing the size of the plot itself, just zooming in and out, as if it would be a picture (thus resizing all text etc accordingly).
For both of these questions I have no idea where to start, since I have little experience with tkinter and would love some input or help.
Here i have a minimal working example which shows how it ignores the figsize:
from tkinter import *
from matplotlib.backends.backend_tkagg import (
FigureCanvasTkAgg, NavigationToolbar2Tk)
import numpy as np
import matplotlib.pyplot as plt
root = Tk()
root.title('Plotviewer 0.2a')
root.geometry('800x500')
def graph():
# Data for plotting
t = np.arange(0.0, 2.0, 0.01)
s = 1 + np.sin(2 * np.pi * t)
fig, ax = plt.subplots(figsize=(20,10), dpi=80)
ax.plot(t, s)
ax.set(xlabel='time (s)', ylabel='voltage (mV)',
title='About as simple as it gets, folks')
ax.grid()
canvas = FigureCanvasTkAgg(fig, master=root, resize_callback=None) # A tk.DrawingArea.
canvas.draw()
canvas.get_tk_widget().grid(row="0", column="1", sticky="NEWS")
button = Button(root, text="Graph", command=graph)
button.grid(row=0, column=0)
root.columnconfigure(0, weight="0")
root.columnconfigure(1, weight="1")
root.rowconfigure(0, weight="1")
graph()
root.mainloop()

There is a ready to use matplotlib toolbar button to measure axis delta in selected range?

I have simple app of matplotlib figure with toolbar, code:
import tkinter as tk
import numpy as np
from matplotlib import pyplot
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk
root = tk.Tk()
fig, ax = pyplot.subplots()
arr = np.arange(0, 3, .01)
ax.plot(arr, 2 * np.sin(2 * np.pi * arr))
canvas = FigureCanvasTkAgg(fig, master=root)
canvas.draw()
NavigationToolbar2Tk(canvas, root)
canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=1)
tk.mainloop()
Result:
I want to add an interactive "Range Measure" tool button to show X values delta in the selected range.
After choosing the tool from the toolbar, the user clicks on the figure, and dragging the mouse left or right, and the closing range line moving respectively until release the mouse button.
Desired example (created with paint):
I know I can implement it using canvas events binds, but before doing it, I want to know if there is such a thing ready to use?
Thanks!

tkinter NavigationToolbar2TkAgg change [z] format for imshow

I'm displaying an image using imshow inside a tkinter GUI and I have added a NavigationToolbar2TkAgg (python3.5). Very similarly to this question (check it out for visuals), I want to change the formatting of the coordinates and z values (in particular, all my z values are between 0-10000, so I want to write them out instead of using scientific notation).
For x and y this was pretty easy to do by changing the format_coord handle, but I can't seem to find anything to change the final bit [xxx]. I have tried format_cursor_data, but doesn't seem to be it either.
Anyone know a solution?
It seems that the [z] value is only shown for imshow, not for regular plots.
Here goes a minimal code sample to reproduce the problem
import sys
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.figure import Figure
import tkinter as tk
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg,NavigationToolbar2TkAgg
top = tk.Tk()
fig = plt.figure()
plt.imshow(np.array([[0,1],[1,2]]))
ax = fig.gca()
ax.format_coord = lambda x,y: "x:%4u, y:%4u" % (x,y)
ax.format_cursor_data = lambda z: "Hello" % (z) # does nothing
canvas = FigureCanvasTkAgg(fig,master=top)
canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=1)
canvas.show()
toolbar = NavigationToolbar2TkAgg(canvas, top)
toolbar.update()
top.mainloop()
In the mouse_move() method of Matplotlib's NavigationToolbar2 class, the cursor data and its formatting are obtained from the topmost Artist in the Axes instance, rather than from the Axes instance itself, like for the coordinates. So what you should do is:
fig = Figure()
ax = fig.add_subplot(111)
img = np.array([[0,10000],[10000,20000]])
imgplot = ax.imshow(img, interpolation='none')
# Modify the coordinates format of the Axes instance
ax.format_coord = lambda x,y: "x:{0:>4}, y:{0:>4}".format(int(x), int(y))
# Modify the cursor data format of the Artist created by imshow()
imgplot.format_cursor_data = lambda z: "Hello: {}".format(z)

Categories

Resources