Helidad0
Helidad0

Reputation: 3

Clearing a subplot to update with a new one

I have some radiobuttons to select colour and width of a linestring in an embedded plot. I can't get the plot to clear when I select a new colour or width and the new plot is placed below the original plot. I have probably complicated things by using tkinter, matplotlib and shapely. I have been successful in destroying the entire window and rebuilidng it but it seemed pretty clunky. How can I remove the old plot or figure so I can update the colour and width?

I tried plt.clf() in the Selection method and it didn't remove it at all. I also tried putting plt.clf() at the end of the initial plot. This removed the plot but the new plot was still placed below the blank figure.

from tkinter import *
import tkinter as tk
import matplotlib.pyplot as plt
from shapely.geometry import LineString
from shapely.plotting import plot_line, plot_points
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import (FigureCanvasTkAgg,NavigationToolbar2Tk)


root =tk.Tk()
root.title("DATA")
topFrame=tk.Frame(root)
bottomFrame=tk.Frame(root)
topFrame.pack(side='top')
bottomFrame.pack(side='bottom')


#Method and call to create initial plot
def CreatePlot(lineColour,lineWidth):
        width = lineWidth
        colour = "#"+ lineColour
        peekFigure = Figure(figsize=(6, 4), dpi=100)
        fig = plt.figure (dpi = 90,clear=True)
        print("fig num: ", peekFigure)
        ax = fig.add_subplot (111)
        sample = LineString([(1.0,1.0), (2.0,2.0)])
        plot_line(sample, ax=ax, add_points=False,  alpha=0.7, color = colour,linewidth = lineWidth)
        axes = plt.gca()
        axes.set_aspect('equal')
        plt.axis('off')
        canvas = FigureCanvasTkAgg(fig, master = root) 
        canvas.draw()
        canvas.get_tk_widget().pack()
        
width = 5
colour = 'ff0000'
CreatePlot(colour,width)
#plt.clf()
        
#Method for change in radiobutton selection 
def Selection():
        plt.clf()
        CreatePlot(cVar.get(),wVar.get())
 

#Create buttons and labels
buttonExit =Button (bottomFrame, text = "Exit",command = root.destroy,width = 20)
buttonExit.pack()
labelColour = tk.Label(topFrame, text=" Colour")
labelColour.grid(row=0, column=1, sticky = 'w',padx=3, pady=2)
label3 = tk.Label(topFrame, text="Line Width")
label3.grid(row = 0, column = 2,padx=3, pady=2)


#Create radio buttons for line colour
colours = [{"colour":'red',"value":'ff0000'},{"colour":'blue',"value":"0000ff"},\
               {"colour": 'amber',"value":'ffa500'},{"colour":'magenta',"value":'ff00ff'}]
cVar = tk.StringVar()
rowNo=0
radC={}   
for c in colours:
        columnNo = 1
        rowNo=rowNo+1
        radC[f'{rowNo}']= tk.Radiobutton(topFrame,text=(c["colour"]),value=(c["value"]),\
            variable=cVar,command = Selection)
        radC[f'{rowNo}'].grid(row = rowNo, column = columnNo,sticky='w',padx=3, pady=2)
        if rowNo==1:
                radC[f'{rowNo}'].select()



# Create radio buttons for line widths
widths = ["5", "10", "20","30"]
wVar = tk.IntVar()
rowNo=0
WBoxList = []
radW={}
for width in widths:
    columnNo = 2
    rowNo=rowNo+1
    radW[f'{rowNo}']= tk.Radiobutton(topFrame,text=(width),value=(width),\
                        variable=wVar,command = Selection)
    radW[f'{rowNo}'].grid(row = rowNo, column = columnNo,sticky='w',padx=3, pady=2)
    if rowNo == 1:
        radW[f'{rowNo}'].select()

root.mainloop()

Upvotes: 0

Views: 70

Answers (1)

pippo1980
pippo1980

Reputation: 3096

My attempt:

from tkinter import *
import tkinter as tk
import matplotlib.pyplot as plt
from shapely.geometry import LineString
from shapely.plotting import plot_line, plot_points
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import (FigureCanvasTkAgg,NavigationToolbar2Tk)


root =tk.Tk()
root.title("DATA")
topFrame=tk.Frame(root)
bottomFrame=tk.Frame(root)
topFrame.pack(side='top')
bottomFrame.pack(side='bottom')


#Method and call to create initial plot
def CreatePlot(lineColour,lineWidth):
    
        width = lineWidth
        colour = "#"+ lineColour
        # peekFigure = Figure(figsize=(6, 4), dpi=100) not needed
        
        fig = plt.figure(num=1, dpi = 90,clear=True)
        
        print('fig : ', fig, fig.number)
        fig.clear()
        
        # print("fig num: ", peekFigure)    #not needed 
        ax = fig.add_subplot (111)
        
        sample = LineString([(1.0,1.0), (2.0,2.0)])
        plot_line(sample, ax=ax, add_points=False,  alpha=0.7, color = colour,linewidth = lineWidth)
        axes = plt.gca()
        axes.set_aspect('equal')
        plt.axis('off')
        
        
        # print('root : ', root ,)
        
        # for i in  dir(root):
        #     print(i)
 
        print(root.focus_get())   
        
        if root.focus_get():
        
            root.focus_get().destroy()
                
        else: 
            
            pass
 
        canvas = FigureCanvasTkAgg(fig, master = root) 
    
        canvas.draw()
        
        canvas.get_tk_widget().pack()
            
   
        
width = 5
colour = 'ff0000'
CreatePlot(colour,width)
#plt.clf()
        
#Method for change in radiobutton selection 
def Selection():
        # plt.clf() # non funza 
        
        # plt.cla() # non funza 
        
        # plt.close() #non funza
        
        CreatePlot(cVar.get(),wVar.get())
 

#Create buttons and labels
buttonExit =Button (bottomFrame, text = "Exit",command = root.destroy,width = 20)
buttonExit.pack()
labelColour = tk.Label(topFrame, text=" Colour")
labelColour.grid(row=0, column=1, sticky = 'w',padx=3, pady=2)
label3 = tk.Label(topFrame, text="Line Width")
label3.grid(row = 0, column = 2,padx=3, pady=2)


#Create radio buttons for line colour
colours = [{"colour":'red',"value":'ff0000'},{"colour":'blue',"value":"0000ff"},\
               {"colour": 'amber',"value":'ffa500'},{"colour":'magenta',"value":'ff00ff'}]
cVar = tk.StringVar()
rowNo=0
radC={}   
for c in colours:
        columnNo = 1
        rowNo=rowNo+1
        radC[f'{rowNo}']= tk.Radiobutton(topFrame,text=(c["colour"]),value=(c["value"]),\
            variable=cVar,command = Selection)
        radC[f'{rowNo}'].grid(row = rowNo, column = columnNo,sticky='w',padx=3, pady=2)
        if rowNo==1:
                radC[f'{rowNo}'].select()



# Create radio buttons for line widths
widths = ["5", "10", "20","30"]
wVar = tk.IntVar()
rowNo=0
WBoxList = []
radW={}
for width in widths:
    columnNo = 2
    rowNo=rowNo+1
    radW[f'{rowNo}']= tk.Radiobutton(topFrame,text=(width),value=(width),\
                        variable=wVar,command = Selection)
    radW[f'{rowNo}'].grid(row = rowNo, column = columnNo,sticky='w',padx=3, pady=2)
    if rowNo == 1:
        radW[f'{rowNo}'].select()

root.mainloop()

I just added:

        print(root.focus_get())   
        
        if root.focus_get():
        
            root.focus_get().destroy()
                
        else: 
            
            pass

before:

 `canvas = FigureCanvasTkAgg(fig, master = root)`

in def CreatePlot(lineColour,lineWidth):

enter image description here

But I suspect your code is not the correct way to implement what are you trying to achieve. I'm not an expert though.

While using the

Shapely’s plot_line returns a Matplotlib PathPatch object. That has set_edgecolor and set_linewidth methods, so you can update the colour and width without needing to clear/destroy anything.

The approach suggested in comments I've:

from tkinter import *
import tkinter as tk
import matplotlib.pyplot as plt
from shapely.geometry import LineString
from shapely.plotting import plot_line, plot_points
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import (FigureCanvasTkAgg,NavigationToolbar2Tk)


root =tk.Tk()
root.title("DATA")
topFrame=tk.Frame(root)
bottomFrame=tk.Frame(root)
topFrame.pack(side='top')
bottomFrame.pack(side='bottom')


#Method and call to create initial plot
# def CreatePlot(lineColour,lineWidth, line):
    
def CreatePlot(lineColour,lineWidth):
        width = lineWidth
        colour = "#"+ lineColour
        # colour = lineColour
        
        # peekFigure = Figure(figsize=(6, 4), dpi=100)
        fig = plt.figure (dpi = 90,clear=True)
        # print("fig num: ", peekFigure)
        ax = fig.add_subplot (111)
        sample = LineString([(1.0,1.0), (2.0,2.0)])
        line = plot_line(sample, ax=ax, add_points=False,  alpha=0.7, color = colour,linewidth = lineWidth)
        axes = plt.gca()
        axes.set_aspect('equal')
        plt.axis('off')
        canvas = FigureCanvasTkAgg(fig, master = root) 
        canvas.draw()
        canvas.get_tk_widget().pack()
        
        return line, canvas
        
width = 5
colour = 'ff0000'
# sample = LineString([(1.0,1.0), (2.0,2.0)])
# line = plot_line(sample, ax=ax, add_points=False,  alpha=0.7, color = colour,linewidth = lineWidth)
# CreatePlot(colour,width, line)

line = CreatePlot(colour,width)

#plt.clf()
        
#Method for change in radiobutton selection 
def Selection(line):
    
        print('line :', line)    
    
        # plt.clf()
        # CreatePlot(cVar.get(),wVar.get())
        
        print('cVar.get() : ', cVar.get())
        print('wVar.get() : ', wVar.get())
        
        
        line[0].set_edgecolor('#'+cVar.get())
        line[0].set_linewidth(wVar.get())
        
        line[1].draw()
 

#Create buttons and labels
buttonExit =Button (bottomFrame, text = "Exit",command = root.destroy,width = 20)
buttonExit.pack()
labelColour = tk.Label(topFrame, text=" Colour")
labelColour.grid(row=0, column=1, sticky = 'w',padx=3, pady=2)
label3 = tk.Label(topFrame, text="Line Width")
label3.grid(row = 0, column = 2,padx=3, pady=2)


#Create radio buttons for line colour
colours = [{"colour":'red',"value":'ff0000'},{"colour":'blue',"value":"0000ff"},\
               {"colour": 'amber',"value":'ffa500'},{"colour":'magenta',"value":'ff00ff'}]
cVar = tk.StringVar()
rowNo=0
radC={}   
for c in colours:
        columnNo = 1
        rowNo=rowNo+1
        # radC[f'{rowNo}']= tk.Radiobutton(topFrame,text=(c["colour"]),value=(c["value"]),\
        #     variable=cVar,command = Selection)
        
        radC[f'{rowNo}']= tk.Radiobutton(topFrame,text=(c["colour"]),value=(c["value"]),\
            variable=cVar,command=lambda: Selection(line))
        
        radC[f'{rowNo}'].grid(row = rowNo, column = columnNo,sticky='w',padx=3, pady=2)
        if rowNo==1:
                radC[f'{rowNo}'].select()



# Create radio buttons for line widths
widths = ["5", "10", "20","30"]
wVar = tk.IntVar()
rowNo=0
WBoxList = []
radW={}
for width in widths:
    columnNo = 2
    rowNo=rowNo+1
    # radW[f'{rowNo}']= tk.Radiobutton(topFrame,text=(width),value=(width),\
    #                     variable=wVar,command = Selection)
    
    radW[f'{rowNo}']= tk.Radiobutton(topFrame,text=(width),value=(width),\
                        variable=wVar,command=lambda: Selection(line))
    
    radW[f'{rowNo}'].grid(row = rowNo, column = columnNo,sticky='w',padx=3, pady=2)
    if rowNo == 1:
        radW[f'{rowNo}'].select()

root.mainloop()

enter image description here

After having the answer as accepted I tried to better structure the code, ended up with:

from tkinter import *
import tkinter as tk
import matplotlib.pyplot as plt
from shapely.geometry import LineString
from shapely.plotting import plot_line, plot_points
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import (FigureCanvasTkAgg,NavigationToolbar2Tk)



colours = [{"colour":'red',"value":'ff0000'},{"colour":'blue',"value":"0000ff"},\
                {"colour": 'amber',"value":'ffa500'},{"colour":'magenta',"value":'ff00ff'}]




class TkinterApp(tk.Tk):

    def __init__(self):
        tk.Tk.__init__(self)
        self.title("DATA")
        self.topFrame=tk.Frame()
        self.bottomFrame=tk.Frame()
        self.topFrame.pack(side='top')
        self.bottomFrame.pack(side='bottom')
        
        #Create buttons and labels
        self.buttonExit =Button (self.bottomFrame, text = "Exit",command = self.destroy,width = 20)
        self.buttonExit.pack()
        self.labelColour = tk.Label(self.topFrame, text=" Colour")
        self.labelColour.grid(row=0, column=1, sticky = 'w',padx=3, pady=2)
        self.label3 = tk.Label(self.topFrame, text="Line Width")
        self.label3.grid(row = 0, column = 2,padx=3, pady=2)
        
        
        #about plot and selection
        self.Selection = Selection
        
        
        self.linewidth = 5
        self.linecolour = 'ff0000'        
        self.sample = LineString([(1.0,1.0), (2.0,2.0)])
        
        self.CreatePlot = CreatePlot
        line = self.CreatePlot(self.linecolour, self.linewidth,  self.sample, self)
       

        #Create radio buttons for line colour
        self.colours = colours
        

        self.cVar = tk.StringVar()
        
        # self.cVar.set('ff0000' )
        self.cVar.set(self.linecolour)
        
        self.rowNo=0
        self.radC={}   
        for c in self.colours:
                self.columnNo = 1
                self.rowNo=self.rowNo+1
                
                self.radC[f'{self.rowNo}']= tk.Radiobutton(self.topFrame,text=(c["colour"]),value=(c["value"]),\
                    variable=self.cVar,command=lambda: self.Selection(line, self.cVar,  self.wVar))
                
                self.radC[f'{self.rowNo}'].grid(row = self.rowNo, column = self.columnNo,sticky='w',padx=3, pady=2)
                if self.rowNo==1:
                        self.radC[f'{self.rowNo}'].select()



        # Create radio buttons for line widths
        self.widths = ["5", "10", "20","30"]
        self.wVar = tk.IntVar()
        
        # self.wVar.set(5)
        self.wVar.set(self.linewidth)
        
        self.rowNo=0
        self.WBoxList = []
        self.radW={}
        for width in self.widths:
            self.columnNo = 2
            self.rowNo=self.rowNo+1
            
            self.radW[f'{self.rowNo}']= tk.Radiobutton(self.topFrame,text=(width),value=(width),\
                                variable=self.wVar,command=lambda: self.Selection(line, self.cVar,  self.wVar))
            
            self.radW[f'{self.rowNo}'].grid(row = self.rowNo, column = self.columnNo,sticky='w',padx=3, pady=2)
            if self.rowNo == 1:
                self.radW[f'{self.rowNo}'].select()
                
       
# #Method and call to create initial plot
def CreatePlot(lineColour,lineWidth, sample, root):
        width = lineWidth
        colour = "#"+ lineColour

        fig = plt.figure (dpi = 90,clear=True)

        ax = fig.add_subplot (111)

        line = plot_line(sample, ax=ax, add_points=False,  alpha=0.7, color = colour,linewidth = width)
        axes = plt.gca()
        axes.set_aspect('equal')
        plt.axis('off')
        canvas = FigureCanvasTkAgg(fig, master = root) 
        canvas.draw()
        canvas.get_tk_widget().pack()
        
        return line, canvas
        

def Selection(line, cVar , wVar):
    
        print('line :', line, '\n',type(line),'\n---------------')    
    
        
        print('cVar.get() : ', cVar.get())
        print('wVar.get() : ', wVar.get())
        
        
        line[0].set_edgecolor('#'+cVar.get())
        line[0].set_linewidth(wVar.get())
        
        line[1].draw()
 


root = TkinterApp()
root.mainloop()

But still I think this kind of apps could be better structured, what I do not get at all is how to pass different shapely shapes (in this case is plot_line but I guess could be different) to my def CreatePlot because plot_line needs an axes (ax = fig.add_subplot(111) in this case) to be instantiated.

Probably I should split def CreatePlot to have my main TkinterApp Class to provide the space/figure/frame to it maybe leaving def CreatePlot to retrieve the sample data and plot them as lines or points (ie shapely plot_line or plot_points). See this answer in How do I plot Shapely polygons and objects using Matplotlib?

Upvotes: 0

Related Questions