0

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!

1 Answer 1

1

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')
Sign up to request clarification or add additional context in comments.

1 Comment

Thank you so much, really appreciate it! Can't believe I missed that...

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.