I am making a GUI using PySimpleGui
for a project. Below is the code replicating the key structure - it consists of two columns, and the LHS
column has two tabs. In one of these tabs, I ask for input from the GUI user, in order to plot a graph, that is to be embedded within the same tab, iteratively being updated if different inputs are submitted. So I asked for coefficients b and a for the equation b\*x^2 a\*x
. I am using matplotlib.use('TkAgg)
and FigureCanvas TkAgg
, but at the line of code where I wish to draw the plot, and update the GUI
figure_canvas_agg = FigureCanvasTkAgg(figure, canvas)
I get the error: AttributeError: 'Canvas' object has no attribute 'set_canvas'
. Please could I get some help on how to fix? I provide below the code for the GUI, and then a plotter class, where the the error is in the class method draw_plot. This class was on a separate script and imported into the main GUI script, thank you!!
##### MAIN GUI Script ####
from GUI_helpers import * # this was where the Plotter class was stored
import PySimpleGUI as sg
profile = Plotter()
def refresh(window):
window.refresh()
sg.theme("Black")
sg.set_options(font=("Avenir Next", 16))
tab_1 = [
[sg.Text("PLOTTER FOR bx^2 ax",expand_x=True,justification="center",font =("Avenir Next", 16,'bold'))],
[sg.Text("Coefficeint for x (a)",font =("Avenir Next", 14)),sg.Push(),
sg.Input(key='-x-', size=(3, 1)),
sg.Button("Submit",key = 'x',font =("Avenir Next", 14))],
[sg.Text("Coefficient for x^2 (b) ",font =("Avenir Next", 14)), sg.Push(),
sg.Input(key='-x_squared-', size=(3, 1),),
sg.Button("Submit",key = 'x_squared', font =("Avenir Next", 14))],
[sg.Canvas(size=(20,20), key='plot-canvas')]]
tab_2 = [
[sg.Text("RANDOM TEXT",expand_x=True,justification="center",font =("Avenir Next", 16,'bold'))],
[sg.T(' '*5),sg.Button("RANDOM BUTTON", key='random_1',size=(20,1))],
[sg.T(' '*5),sg.Button("RANDOM BUTTON", key='random_2',size=(20,1))],
]
side_1 = [[sg.TabGroup([[sg.Tab('Tab 1', tab_1),
sg.Tab('Tab 2', tab_2)]], tab_location='centertop', border_width=5,size= (300,780),enable_events=True)]]
side_2 = [[sg.Text("SIDE 2 HERE:", expand_x=True, expand_y=True)]]
layout = [[sg.Column(side_1, element_justification='top', size= (320,900),justification='center'),
sg.VSeperator(),
sg.Column(side_2, expand_x=True, expand_y=True,size= (1100,900),),
]]
window = sg.Window("Test GUI", layout, resizable=True, finalize=True, use_default_focus=False, return_keyboard_events=True)
refresh_thread = None
fig = None
fig_agg = None
while True:
event, values = window.read()
if event == 'x':
if fig_agg is not None:
profile.delete_plot(fig_agg)
profile.update_x(values['-x-'])
fig = profile.update_plot(window)
fig_agg = profile.draw_plot(window['plot-canvas'].TKCanvas, fig)
window.refresh()
if event == 'x_squared':
if fig_agg is not None:
profile.delete_plot(fig_agg)
profile.update_xsquared(values['-x_squared-'])
fig = profile.update_plot(window)
fig_agg = profile.draw_plot(window['plot-canvas'].TKCanvas, fig)
window.refresh()
window.close()
print('Transfer ended')
###### PLOTTER CLASS (separate script) #####
import numpy as np
import matplotlib
matplotlib.use('TkAgg')
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
class Plotter:
def __init__(self):
self.a = 0
self.b = 0
def update_plot(self,window):
x_axis = np.linspace(-10,10)
x_comp = [self.a * x for x in x_axis]
x2_comp = [self.b*(x**2) for x in x_axis]
y_axis = np.add(x_comp,x2_comp)
plt.ioff()
plt.plot(x_axis,y_axis)
plt.xlabel('x')
plt.ylabel('bx^2 ax')
return plt.gcf()
def draw_plot(canvas, figure, loc=(0, 0)):
figure_canvas_agg = FigureCanvasTkAgg(figure, canvas)
figure_canvas_agg.draw()
figure_canvas_agg.get_tk_widget().pack(side='top', fill='both', expand=1)
return figure_canvas_agg
def delete_plot(fig_agg):
fig_agg.get_tk_widget().forget()
plt.close('all')
def update_x(self, state):
self.a= float(state)
def update_xsquared(self,state):
self.b = float(state)
I am also open to alternative ways to embedding the plot in the GUI!
CodePudding user response:
It is caused by wrong arguments when passed to a method of an instance.
def draw_plot(canvas, figure, loc=(0, 0)):
figure_canvas_agg = FigureCanvasTkAgg(figure, canvas)
figure_canvas_agg.draw()
figure_canvas_agg.get_tk_widget().pack(side='top', fill='both', expand=1)
return figure_canvas_agg
def delete_plot(fig_agg):
fig_agg.get_tk_widget().forget()
plt.close('all')
Often, the first argument of a method is called
self
. This is nothing more than a convention: the name self has absolutely no special meaning to Python.
Arguments in Method of an instance should be started from self
, so above code revised to
def draw_plot(self, canvas, figure, loc=(0, 0)):
figure_canvas_agg = FigureCanvasTkAgg(figure, canvas)
figure_canvas_agg.draw()
figure_canvas_agg.get_tk_widget().pack(side='top', fill='both', expand=1)
return figure_canvas_agg
def delete_plot(self, fig_agg):
fig_agg.get_tk_widget().forget()
plt.close('all')