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.
Related
I am using Python 3.5.3 on Debian 9.
Tkinter window crashes when clicking the button "Create Plot" to save a plot created using plotnine. The plot however gets successfully saved in the working directory. The code block stated below is a simple recreation of the aforementioned error I am encountering in a larger tkinter application. I am a newbie to python programming and generally use it for bioinformatics data analysis. Please help me.
import tkinter as tk
from fpdf import FPDF
import pandas as pd
from plotnine import *
def createPlot():
df = {"dates":[1,2,3,4,5,6], "amount":[21,22,18,19,25,15]}
df = pd.DataFrame(df)
plot = ggplot(aes(x="dates", y="amount"), data=df) + xlab("Dates") + ylab("Amount") #Create the base plot and axes
plot = plot + scale_x_continuous(breaks = list(df.dates)) #Format the axes
plot = plot + geom_line(aes(x=list(df.dates), y=list(df.amount)), data=df) #Create the actual data plot
plot.save(filename = 'plot.png', dpi=300, width=12, height=7, units="in") #Save the plot as a PNG image
# The GUI Mainloop
root=tk.Tk()
root.title("Test")
root.minsize(width=200,height=200)
root.maxsize(width=200,height=200)
CreatePlotButton=tk.Button(root,text="Create Plot",command= createPlot) #Button to create plots
CreatePlotButton.pack()
CreatePlotButton.place(x=20,y=100)
root.mainloop()
This is the warning that is generated from the terminal
/usr/local/lib/python3.5/dist-packages/plotnine/ggplot.py:706: UserWarning: Saving 12 x 7 in image.
from_inches(height, units), units))
/usr/local/lib/python3.5/dist-packages/plotnine/ggplot.py:707: UserWarning: Filename: plot.png
warn('Filename: {}'.format(filename))
I have also tried to run the application by commenting out the plot.save() line as shown in the code block below. The application does not crash on clicking the "Create Plot". It seems that the error is creeping in while I am trying to save the plot and not during generation of the plot using plotnine.
import tkinter as tk
from fpdf import FPDF
import pandas as pd
from plotnine import *
def createPlot():
df = {"dates":[1,2,3,4,5,6], "amount":[21,22,18,19,25,15]}
df = pd.DataFrame(df)
plot = ggplot(aes(x="dates", y="amount"), data=df) + xlab("Dates") + ylab("Amount") #Create the base plot and axes
plot = plot + scale_x_continuous(breaks = list(df.dates)) #Format the axes
plot = plot + geom_line(aes(x=list(df.dates), y=list(df.amount)), data=df) #Create the actual data plot
#plot.save(filename = 'plot.png', dpi=300, width=12, height=7, units="in") #Save the plot as a PNG image
# The GUI Mainloop
root=tk.Tk()
root.title("Test")
root.minsize(width=200,height=200)
root.maxsize(width=200,height=200)
CreatePlotButton=tk.Button(root,text="Create Plot",command= createPlot) #Button to create plots
CreatePlotButton.pack()
CreatePlotButton.place(x=20,y=100)
root.mainloop()
My larger application is almost ready except for this nagging error I am encountering. I would like my plot to be saved and the Tkinter window to remain open. I would be very thankful for any suggestions.
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()
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.
I am trying to use Pyvisa to capture data from one of my channels of my Keithly 2701 DMM.
I get a static onetime response via temp = keithly.ask('SCPI COmmand'), but what I want to do is constantly print new data without setting any predefined size, i.e capture 300 data points.
I want to determine when to stop the capture if I have seen a trend over 10000 data points, or, in another experiment, I might see a trend after 2500 data points.
from pylab import *
from visa import instrument
inst = SerialInstument(args)
while new data:
print inst.aks('channel')
while True:
print inst.ask('channel')
time.sleep(1)
You can then ctrl-c to stop the loop when you see fit.
The above script is simple - it just puts numbers on the screen until you kill it. I find it useful to plot the data from PyVISA in real time, using matplotlib. I found this to be buggy in pyplot mode (I got lots of blank screens when I turned off interactive mode, ymmv) so I embedded it into a tkinter window, as follows:
import matplotlib
matplotlib.use('TkAgg') # this has to go before the other imports
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure
import Tkinter as Tk
import visa
# set up a PyVISA instrument and a list for the data
data = []
keithley = visa.instrument('GPIB0::whatever')
# make a Tkinter window
root = Tk.Tk()
# add a matplotlib figure to the Tk window
fig = Figure()
ax = fig.add_subplot(111)
canv = FigureCanvasTkAgg(fig, master=root)
canv.show()
canv.get_tk_widget().pack(fill='both', expand=True)
# a function that is called periodically by the event loop
def plot_update():
# add a new number to the data
data.append(keithley.ask('SCPI:COMM:AND?'))
# replot the data in the Tk window
ax.clear()
ax.plot(data)
fig.tight_layout()
canv.draw()
# wait a second before the next plot
root.after(1000, plot_update)
root.after(1000, plot_update)
root.mainloop()
It may not seem like much, but we gradually developed a short script like this into a rather capable instrument control program ;)
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.