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()
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):
But I suspect your code its not the correct way to implement what are you trying to achieve. Not an expert though.
While using the
Shapely’s
plot_line
returns a MatplotlibPathPatch
object. That hasset_edgecolor
andset_linewidth
methods, so you can update the colour and width without needing to clear/destroy anything. – RuthC Commented 12 hours ago
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()
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?