Tkinter crashes when trying to save plot created using plotnine - python

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.

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.

Closing tkinter windows doesn't kill the python program when using matplotlib to plot graph

I am currently working on a GUI using python tkinter and matplotlib. However, after adding the matplotlib features to my GUI, I couldn't kill the program by closing all the windows anymore. I was able to do that previously when I experimented with some simple matplotlib plots. I would really appreciate if anyone could help me by giving some suggestions to try. Thank you! Here is the code that is relevant to my problem:
import tkinter as tk
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk
from matplotlib.backend_bases import MouseEvent, key_press_handler
import sys
sys.path.append('/Users/YangQing/Desktop/eMPRess')
import empress
class App:
def plot_cost_landscape(self):
"""Plots the cost landscape using matplotlib and embeds the graph in a tkinter window."""
# creates a new tkinter window
plt_window = tk.Toplevel(self.master)
plt_window.geometry("550x550")
plt_window.title("Matplotlib Graph DEMO")
# creates a new frame
plt_frame = tk.Frame(plt_window)
plt_frame.pack(fill=tk.BOTH, expand=1)
plt_frame.pack_propagate(False)
recon_input = empress.read_input("./examples/heliconius.newick")
cost_region = empress.compute_cost_region(recon_input, 0.5, 10, 0.5, 10) # create
cost_region.draw_to_file('./examples/cost_poly.png') # draw to a file
fig = cost_region.draw() # draw to figure (creates matplotlib figure)
canvas = FigureCanvasTkAgg(fig, plt_frame)
canvas.draw()
canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=True)
toolbar = NavigationToolbar2Tk(canvas, plt_frame)
toolbar.update()
canvas.get_tk_widget().pack(side=tk.TOP)
# prints the x,y coordinates clicked by the user inside the graph otherwise prints an error message
fig.canvas.callbacks.connect('button_press_event', self.get_xy_coordinates)
Try adding os._exit(0) at the end of the script!! For this you also need to import the os module into your script.os._exit() method in Python is used to exit the process with specified status.

Jupyter Notebook: duplicated scatter plot using when using ipywidgets

I'm trying to control the display of a scatter plot with a checkbox. When I built it using the interact function it worked as expected. The plot was shown or hidden based on the value in the checkbox.
import matplotlib.pyplot as plt
from ipywidgets import interact, widgets
%matplotlib inline
def on_change(Display):
if Display == True:
plt.scatter(x,y)
plt.show()
return Display
interact(on_change, Display=False);
When I tried to do the same thing using the observe function every time I clicked on the checkbox I get an additional plot displayed below. What do I need to do to get it to redraw the same plot so it works like the example above?
I suppose something in the interact example is clearing the display but it's not clear how to do this manually.
import matplotlib.pyplot as plt
from ipywidgets import interact, widgets
%matplotlib inline
x = [1,2,3,4,5,6,7,8]
y = [5,2,4,2,1,4,5,2]
def on_change(change):
if change['new'] == True:
scat = plt.scatter(x,y)
plt.show()
cb = widgets.Checkbox(False, description = "Display")
cb.observe(on_change, names='value')
display(cb)
A couple of alterations I made to your example to hopefully demonstrate what you want. I have taken a more object-oriented route, not sure if you specifically wanted to avoid it but it helps achieve your desired outcome, it seems like you are moving towards a simple GUI here.
1) Include an Output widget (out) - basically a cell output which you can display like a normal widget. You can use a context manager block (with out:) when you want to print to that specific output widget. You can also clear the widget with out.clear_output()
2) Use the object oriented interface in matplotlib rather than using plt. I find this easier to control which plots are displayed and in which location at the right times.
temporarily suspend the interactive matplotlib with plt.ioff()
Create your figure and axis with fig, ax = plt.subplots(). NB figures can have multiple axes/subplots but we only need one.
'plot' the scatter data to your axis using ax.scatter(x,y), but this won't cause it to appear.
Explicitly display the figure with display(fig).
I'm assuming you want your figure to be replotted each time you check the box, so I have included it in the observe function. If your figure doesn't change, it would make sense to move it outside of the loop.
import matplotlib.pyplot as plt
from ipywidgets import interact, widgets
%matplotlib inline
out = widgets.Output()
x = [1,2,3,4,5,6,7,8]
y = [5,2,4,2,1,4,5,2]
def on_change(change):
if change['new'] == True:
with out:
plt.ioff()
fig,ax = plt.subplots()
ax.scatter(x,y)
display(fig)
else:
out.clear_output()
cb = widgets.Checkbox(False, description = "Display")
cb.observe(on_change, names='value')
display(cb)
display(out)

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.

PyVisa and Printing New Data

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 ;)

Categories