Tkinter keeps creating embedded plots instead of updating the current plot - python

I am aware that there are couple of questions on this around the web but unfortunately none of those helped me with this. My relevant matplotlib imports are:
import matplotlib
matplotlib.use("TkAgg")
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure
Below is how I call for new plots:
self.myFig1 = Figure(figsize = (10, 4), tight_layout = True)
self.myPlot1 = self.myFig1.add_subplot(111)
self.myPlot1.plot([blah],[blah])
canvas1 = FigureCanvasTkAgg(self.myFig1, self.myFrame)
canvas1.show()
canvas1.get_tk_widget().grid()
I have a refresh button that sort of keeps calling the function that ends up creating this plot, but I need one embedded plot which is updated after every button click, not recreated. I have tried things like Figure.close(), Figure.clf() but none worked. I'd appreciate your help with this.
Additional info: The reason it keeps creating a new plot is because I keep .grid-ing it over and over again. So I deleted the .grid() part of the code and I tried something like this below which did not work:
self.myFig1.clf()
self.myFig1 = Figure(figsize = (10, 4), tight_layout = True)
self.myPlot1 = self.myFig1.add_subplot(111)
self.myPlot1.plot([blah],[blah])
canvas1 = FigureCanvasTkAgg(self.myFig1, self.myFrame)
canvas1.draw()
This just destroys the figure, canvas, everything and doesn't plot anything.

self.myFig.clear()
self.myPlot = self.myFig.add_subplot(111, **self.nadalkwArgs)
self.myPlot.plot([series1], [series2])
self.myFig.canvas.draw()
Is what solved my problem. Your "refresh plot" button or method should have this in order to keep the canvas, clear the old plot, make the new plot and keep plot style elements such as xlabel, ylabel etc. So basically, first you clear the figure(not the subplot, self.myPlot.clear() would clear the plot but you can't have your kwargs that way), and then you recreate the subplot with kwargs and then you plot, and finally you .canvas.draw()

There is no need to recreate a new canvas or fig.
After grid your FigureCanvasTkAgg, you can reset line in button callback only by:
self.myPlot1.set_data(xdata,ydata)
self.myFig.canvas.draw()

Related

Graph papers black in python GUI

I try to make a line graph in tkinter using the data in the database but then the graph appears as black only in the GUI.
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
###------Show Information Using Graph-------###
graquery = '''SELECT Date, Amount FROM balance'''
CURSOR.execute(graquery)
graresults = CURSOR.fetchall()
Date = [result[0] for result in graresults]
Amount = [result[1] for result in graresults]
figure = plt.figure()
plt.plot(Date, Amount)
plt.xlabel('Date')
plt.ylabel('Amount')
plt.title('Balance graph Graph')
gracanvas = Canvas(main_WINDOW, width=1070, height=452)
gracanvas.pack()
gracanvas.place(x=356, y=270)
figure_canvas = FigureCanvasTkAgg(figure, canvas)
figure_canvas.draw()
change:
figure_canvas = FigureCanvasTkAgg(figure, canvas)
to the following:
figure_canvas = FigureCanvasTkAgg(figure, gracanvas)
Also change this:
figure_canvas.draw()
to this:
gracanvas.create_window(0, 0, window=figure_canvas.get_tk_widget())
It appears that you are attempting to show a graph created using matplotlib within a tkinter interface. The cause of the black graph could be due to a few factors.
It is necessary to confirm that the appropriate backend is being utilized to display the graph in tkinter. In the code provided, you have imported the correct backend, FigureCanvasTkAgg, which is used for displaying matplotlib graphs in tkinter.
It is important to ensure that the figure_canvas object is placed in the correct tkinter widget. In the code provided, you have created a Canvas widget and are attempting to display the figure_canvas object within it, but it seems that you are using the variable canvas instead of gracanvas. It is suggested to use gracanvas instead of canvas.
Lastly, in order to show the graph in your GUI, you should call the get_tk_widget() method on the figure_canvas object to obtain the tkinter widget that can be displayed.

How can I display and update two matplotlib plots in the same window at the same time?

I've implemented a GUI that displays two dropdown menus in which you can choose two different set of graphs to be displayed. However when I create the graphs with the following code:
import matplotlib.pyplot as plt
from matplotlib.backends.backedn_qt4agg import FigureCanvasQTAgg as FigureCanvas
self.comparison_figure1 = plt.figure(figsize=(15,5))
self.comparison_canvas1 = FigureCanvas(self.comparison_figure1)
self.comparison_figure2 = plt.figure(figsize=15,5))
self.comparison_canvas2 = FigureCanvas(self.comparison_figure2)
And then I try to update the plots (plt.tight_layout() for example)
def on_resize(event):
plt.tight_layout()
self.comparison_canvas2.draw()
self.comparison_canvas1.draw() #this would do nothing
cid = self.comparison_canvas2.mpl_connect('resize_event', on_resize)
only the last plot called with "plt." is updated. How do I write my code so that I can reference both plots.
I've also tried to create one plot where I have both graphs being displayed side by side but because of the need to update the graphs independently I encountered more problems. If you are able to make it work that way instead, great! I'm just thinking that fixing the previous problem may be simpler.
If you need more code I can post it!
#
#
Solution (Thanks to ImportanceOfBeingErnest and Ash Sharma):
replace any "plt." with the specific figure
for example:
plt.tight_layout() #replace with self.comparison_figure1.tight_layout()
So this is some of the fixed code:
def on_resize(event):
self.comparison_figure1.tight_layout()
self.comparison_figure2.tight_layout()
self.comparison_canvas1.draw()
self.comparison_canvas2.draw()
cid = self.comparison_canvas2.mpl_connect('resize_event', on_resize)
#
#
Problem:
Previous solution hasn't fixed all plot updating issues. Though "plt" can be replaced with self.comparison_figure1 when using tight_layout(), the same cannot be done when using cla() to clear the plot.
Code where I'm using cla():
sns.set(style="whitegrid")
plt.cla()
ax = self.comparison_figure2.add_subplot(111)
.....
.....
I can post more code if you need it!
Solution (Thanks to ImportanceOfBeingErnest and Ash Sharma):
replace any plt with the specific figure
for example:
plt.tight_layout() #replace with self.comparison_figure1.tight_layout()
So this is some of the fixed code:
def on_resize(event):
self.comparison_figure1.tight_layout()
self.comparison_figure2.tight_layout()
self.comparison_canvas1.draw()
self.comparison_canvas2.draw()
cid = self.comparison_canvas2.mpl_connect('resize_event', on_resize)

Matplotlib figure not updating on data change

I'm implementing an image viewer using matplotlib. The idea is that changes being made to the image (such as filter application) will update automatically.
I create a Figure to show the inital image and have added a button using pyQt to update the data. The data does change, I have checked, but the Figure does not. However, if after I've pressed the filter application button, I move the image using matplotlib's standard tool bar, the image is then updated.
I assume I'm doing something wrong when updating the image, but since the fact of moving it actually forces the update, it then shows the data change. I would like for this to happen when I press the button, though.
Below is some of the code. This is the initial figure initialization, which shows the original image:
self.observableFig = Figure((4.0, 4.0), dpi=100)
self.canvas = FigureCanvas(self.observableFig)
self.canvas.setParent(self.observableWindow)
self.canvas.setFocusPolicy(Qt.StrongFocus)
self.canvas.setFocus()
self.canvas.mpl_connect('button_press_event', self.on_click)
# Showing initial data on Window
self.observableFig.clear()
self.observableAxes = self.observableFig.add_subplot(1, 1, 1)
min, max = self.min, self.max
self.observableAxes.imshow(
self.data,
vmin=min,
vmax=max,
origin='lower'
)
And this is the event for when the button that changes the data is pressed:
self.observableAxes.imshow(self.data/2, origin='lower')
# plt.clf()
# plt.draw()
# plt.show()
I have tried draw(), show(), basically anything I've found on pyplot about this. I have also tried both with and without plt.ion() at the beginning, but it hasn't made a difference in this.
Thanks in advance.
The reason that nothing is updating is that you're trying to use pyplot methods for a figure that's not a part of the pyplot state machine. plt.draw() won't draw this figure, as plt doesn't know the figure exists.
Use fig.canvas.draw() instead.
Regardless, it's better to use fig.canvas.draw() that plt.draw(), as it's clear which figure you're drawing (the former draws one, the latter draws all, but only if they're tracked by pyplot).
Try something along these lines:
import numpy as np
import matplotlib.pyplot as plt
data = np.random.random((10,10))
# To make a standalone example, I'm skipping initializing the
# `Figure` and `FigureCanvas` and using `plt.figure()` instead...
# `plt.draw()` would work for this figure, but the rest is identical.
fig, ax = plt.subplots()
ax.set(title='Click to update the data')
im = ax.imshow(data)
def update(event):
im.set_data(np.random.random((10,10)))
fig.canvas.draw()
fig.canvas.mpl_connect('button_press_event', update)
plt.show()

Print variable in large font in python figure

I have borrowed some code from another source and I want to edit the figure produced. Here is the relevant (i think) code from the script.
import gtk #the gui toolkit we'll use:
from matplotlib.figure import Figure
from Tkinter import *
from matplotlib.backends.backend_gtkagg import FigureCanvasGTKAgg as FigureCanvas
#create a window to put the plot in
win = gtk.Window()
#connect the destroy signal (clicking the x in the corner)
win.connect("destroy", quit_app)
win.set_default_size(600,500)
#create a plot:
fig = Figure()
ax = fig.add_subplot(111,xlabel='Time Step', ylabel='Temp (deg C)', axisbg='black')
ax.set_ylim(0,100) # set limits of y axis.
canvas = FigureCanvas(fig) #put the plot onto a canvas
win.add(canvas) #put the canvas in the window
#show the window
win.show_all()
win.set_title("ready to receive data");
line, = ax.plot(times,yvals, color='red')
while(1):
line.set_ydata(yvals) # draw the line
fig.canvas.draw() # update the Canvas
win.set_title("Temp: "+str(yvals[49])+" deg C")
I don't know whether or not all the code above is necessary - but that is all the 'plot' related code I could find.
So anyway the code works in my program perfectly.
There are TWO tasks I would like to create:
(1) What I want is to include that 'str(yvals[49])' variable, which is currently being displayed in the title of the window, to be displayed in large font underneath the plot. So I think I need to make the window size a little bigger to accompany the text but not sure how to print it.
(2) I manged to change the background of the plot itself to black that plots a red line. But how can I change the background of the window itself to all black and the x/y axis to red as well.
Thanks!!
(1) What you are most probably looking for it the matplotlib text command. Have a look at http://matplotlib.org/users/pyplot_tutorial.html section working with text. It might be convenient to create two separate axes, so you can truly put the text below the whole figure. Maybe it is enough to place the text at xlabel position?
import matplotlib.pyplot as plt
plt.xlabel('yourtext')
(2) There are already good answers out there that might help you: e.g. How to change the pylab window background color?
As for the color of the axis Changing the color of the axis, ticks and labels for a plot in matplotlib
Have also a look at Matplotlib figure facecolor (background color) in case you want to save the figure.

Creating a matplotlib interactive plotting window for an existing figure

I am writing a program that fits curves to large sets of xy coordinate data. It is often helpful to watch the progress of the algorithm by plotting and displaying each iteration as the fitting progresses. I'm using matplotlib for plotting.
What I'd like to do is create the figure in the main thread, then pass it into a child thread that displays it. That way I have access to all the figure's methods and attributes in the main thread. I can plot by calling fig.gca().plot() and draw by calling fig.canvas.draw().
I can't figure out how to create an interactive plotting window that shows only the figure I pass to it. Right now I'm using matplotlib.pyplot.show(), which does display my figure, but it also displays any other figures that may have been defined in the program. Is there an object oriented way to create an interactive window for a specific figure? I am looking for a solution that does not rely on unsupported interfaces in matplotlib.
Here is a post that's similar, but it still doesn't answer my question: Interactive figure with OO Matplotlib
I've never understood why matplotlib always seems to use current objects (current figure, current axes, etc.) rather than specific objects (for example, why not have matplotlib.pyplot.show(fig) rather than just show()?) I think I'm missing something. If anyone could shed some light on why matplotlib is designed this way, or how I'm misunderstanding and/or misusing it, that would also be appreciated.
Here's my code:
import matplotlib.pyplot
import threading
import time
class Plotter():
def __init__(self,fig):
t = threading.Thread(target=self.PlottingThread,args=(fig,))
t.start()
def PlottingThread(self,fig):
#This line shows fig1 AND fig2 from below. I want it to show fig ONLY.
matplotlib.pyplot.show()
if __name__ == "__main__":
fig1 = matplotlib.pyplot.figure()
fig2 = matplotlib.pyplot.figure()
Plotter(fig1)
fig1.gca().clear()
fig1.gca().plot([1,2,3])
fig1.canvas.draw()
I think I got it:
import Tkinter
import threading
import matplotlib.backends.backend_tkagg
root = Tkinter.Tk()
class Plotter():
def __init__(self,fig):
t = threading.Thread(target=self.PlottingThread,args=(fig,))
t.start()
def PlottingThread(self,fig):
canvas = matplotlib.backends.backend_tkagg.FigureCanvasTkAgg(fig, master=root)
canvas.show()
canvas.get_tk_widget().pack(side=Tkinter.TOP, fill=Tkinter.BOTH, expand=1)
toolbar = matplotlib.backends.backend_tkagg.NavigationToolbar2TkAgg(canvas, root)
toolbar.update()
canvas._tkcanvas.pack(side=Tkinter.TOP, fill=Tkinter.BOTH, expand=1)
Tkinter.mainloop()
if __name__ == "__main__":
import time
fig1 = matplotlib.figure.Figure(figsize=(5,4), dpi=100)
fig1.gca().plot([1,2,3])
fig2 = matplotlib.figure.Figure(figsize=(5,4), dpi=100)
fig2.gca().plot([3,2,1])
#Shows fig1 and not fig2, just like it's supposed to
Plotter(fig1)
time.sleep(1)
#I can still plot to fig1 from my main thread
fig1.gca().clear()
fig1.gca().plot([5,2,7])
fig1.canvas.draw()
The only thing is if you try to create two instances of Plotter the whole thing crashes. That isn't too important for my application, but it probably means I'm using Tkinter wrong. Suggestions/corrections are welcome.

Categories