3

The code below shows one Page of a tkinter GUI I'm currently working on.

What I want the 'Clear Plot Field'-Button to do is what it says: Clear the canvas, because if I plot again the new plot is packed below.

OR: How can I overwrite an existing plot, so I can get rid of the button itself?

class PlotPage(tk.Frame):
"""Page containing the matplotlib graphs"""

    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        label = ttk.Label(self, text="PlotPage", font=LARGE_FONT)
        label.pack(pady=5)

        button1 = ttk.Button(self, text="Back to Start Page", command=lambda: controller.show_frame(StartPage))
        button1.pack(pady=5)

        buttonPlot = ttk.Button(self, text="Show Plot", command=self.plot)
        buttonPlot.pack(pady=5)

        buttonClear = ttk.Button(self, text="Clear Plot Field", command=lambda: controller.show_frame(PlotPage))
        buttonClear.pack(pady=5)

    def plot(self):
    """generate a simple matplotlib graph for multiple profiles"""

        f = Figure(figsize=(8,4), dpi=100)   
        a = f.add_subplot(111)

        for profile in listofProfiles:
            X_list=profile.indexList
            Y_list=profile.VL_List

            [...] Some lines related to plotting [...]

            a.plot(X_list, Y_list, lw=0.3,)

        canvas = FigureCanvasTkAgg(f, self)
        canvas.show()
        canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=True)

        toolbar = NavigationToolbar2TkAgg(canvas, self)
        toolbar.update()
        canvas._tkcanvas.pack(side=tk.TOP, fill=tk.BOTH, expand=True)

        print("Stuff has been plotted")

    def clearPlotPage(self):
    """cleares the whole plot field"""     

        # ???

        print("Plot Page has been cleared")
Tybald
  • 167
  • 1
  • 1
  • 12

3 Answers3

2

Simply Googling "clear tkinter canvas" got me this, this, this and this.

Short answer: calling canvas.delete('all') will clear the entire canvas.

ChumiestBucket
  • 868
  • 4
  • 22
  • 51
  • You could simply delete the canvas and recreate also. – Mike - SMT Aug 15 '18 at 14:59
  • I found and tried this as well, but the problem is: The canvas does not exist within the clear-method. So how can I get around this? – Tybald Aug 15 '18 at 14:59
  • @Tybald you can make `canvas` a class attribute by adding `self.` as a prefix. This will allow your `clearPlotPage(self)` method the ability to work with `self.canvas` and you can delete it from there and then call `self.plot()` again to recreate. – Mike - SMT Aug 15 '18 at 15:05
  • @Mike-SMT if you delete the canvas you need to redo any formatting or attribute setting again (assuming you've saved that info somewhere). It also might mess with your layout depending on which manager you're using – en_Knight Aug 15 '18 at 15:28
  • @en_Knight it wont mess with the layout if you use a frame to hold the canvas. If you create the canvas in the first place within a function then its just a matter of deleted the canvas and rerunning that function to created it. My app does it this way with no issues. – Mike - SMT Aug 15 '18 at 16:17
2

I would destroy() the canvas and then rerun plot(). To do this you need to make canvas a class attribute like this self.canvas. Now that we have a class attribute any of your methods can work with self.canvas without issue.

Take a look at this code I modified from your question and let me know if you have any questions.

class PlotPage(tk.Frame):
    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)

        self.canvas = None

        label = ttk.Label(self, text="PlotPage", font=LARGE_FONT)
        label.pack(pady=5)
        button1 = ttk.Button(self, text="Back to Start Page", command=lambda: controller.show_frame(StartPage))
        button1.pack(pady=5)
        buttonPlot = ttk.Button(self, text="Show Plot", command=self.plot)
        buttonPlot.pack(pady=5)
        buttonClear = ttk.Button(self, text="Clear Plot Field", command=lambda: controller.show_frame(PlotPage))
        buttonClear.pack(pady=5)

    def plot(self):
        if self.canvas == None:
            f = Figure(figsize=(8,4), dpi=100)   
            a = f.add_subplot(111)

            for profile in listofProfiles:
                X_list=profile.indexList
                Y_list=profile.VL_List
                # [...] Some lines related to plotting [...]
                a.plot(X_list, Y_list, lw=0.3,)

            self.canvas = FigureCanvasTkAgg(f, self)
            self.canvas.show()
            self.canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=True)

            toolbar = NavigationToolbar2TkAgg(self.canvas, self)
            toolbar.update()
            self.canvas._tkcanvas.pack(side=tk.TOP, fill=tk.BOTH, expand=True)

            print("Stuff has been plotted")
        else:
            print("Plot already plotted please clear first")

    def clearPlotPage(self):
        self.canvas.destroy()
        self.canvas = None
        self.plot()
        print("Plot Page has been cleared")
Mike - SMT
  • 14,784
  • 4
  • 35
  • 79
  • Hi Mike, I'm sure this solution works, I'm just wondering what the benefit of destroying the canvas and then rebuilding it instead of using delete? The downsides seem to be the risk that your layout might be tough to recreate if you're using a manger that's order-dependent (or if the user has manually resized some widgets), and that if you are running this at a very high frequency you might see the widgets jumping around trying to resize quickly – en_Knight Aug 15 '18 at 17:25
  • @en_Knight It depends on the goal. For simple refresh like a button there should be no problem as long as your canvas is inside of a frame. For higher volume there are update methods for matplotlib you can use. Like clearing the plot/subplot and then re-plotting. – Mike - SMT Aug 15 '18 at 17:32
  • @Mike-SMT I learned what a class variable is and why it makes sense. This is good, but apparently `destroy()` does not exist: _AttributeError: 'FigureCanvasTkAgg' object has no attribute 'destroy'_ So I dont really know how to move on. There are also no `delete()` and `clear()` methods which I find kind of confusing. – Tybald Aug 16 '18 at 08:45
2

For anyone still dealing with AttributeError: 'FigureCanvasTkAgg' object has no attribute 'destroy'.

I solved the issue by creating a "dummy" container frame to wrap the FigureCanvas which can be destroyed, in turn destroying FigureCanvas.

codeMonkey
  • 21
  • 1