I have snippets of code to generate a basemap, as well as the (very rough) start of a GUI. However, I cannot find that "one-liner" will allow me to display the map as a figure in the GUI. Code snippets are as follows; ideally, I would like a couple of things to happen:
An image of the 'basemap' to relace the plot of sin(2*pi*t)
I would really like for the code to record the (pixel) location on the graphic, if I were to click on the plot shown (the hope is that you could click anywhere on the map, and the script would record the latitude and longitude of where you clicked).
Regarding the 1st step, I've tried things such as setting the figure variable, f, equal to the Basemap; this killed the GUI portion altogether, and simply showed an image of the map in another window.
I've tried to address #2 by trying to implement a couple routines I found on stackexchange, but never got it to work fully.
Basemap:
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.basemap import Basemap
## User-chosen datapoint
#lons = [10]; lats = [20];
# Define map projection
m = Basemap(projection='cyl',llcrnrlat=-90,urcrnrlat=90,\
llcrnrlon=-180,urcrnrlon=180,resolution='c')
m.drawcoastlines()
Rough GUI:
import matplotlib
matplotlib.use('TkAgg')
from numpy import arange, sin, pi
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg
# implement the default mpl key bindings
from matplotlib.backend_bases import key_press_handler
from Tkinter import *
from PIL import Image
import numpy as np
import matplotlib.pyplot as plt
from PIL import Image
from mpl_toolkits.basemap import Basemap
from matplotlib.figure import Figure
import sys
if sys.version_info[0] < 3:
import Tkinter as Tk
else:
import tkinter as Tk
root = Tk.Tk()
root.wm_title("Embedding in TK")
f = Figure(figsize=(5, 4), dpi=100)
a = f.add_subplot(111)
t = arange(0.0, 3.0, 0.01)
s = sin(2*pi*t)
a.plot(t, s)
# a tk.DrawingArea
canvas = FigureCanvasTkAgg(f, master=root)
canvas.show()
canvas.get_tk_widget().pack(side=Tk.TOP, fill=Tk.BOTH, expand=1)
toolbar = NavigationToolbar2TkAgg(canvas, root)
toolbar.update()
canvas._tkcanvas.pack(side=Tk.TOP, fill=Tk.BOTH, expand=1)
def on_key_event(event):
print('you pressed %s' % event.key)
key_press_handler(event, canvas, toolbar)
canvas.mpl_connect('key_press_event', on_key_event)
def _quit():
root.quit() # stops mainloop
root.destroy() # this is necessary on Windows to prevent
# Fatal Python Error: PyEval_RestoreThread: NULL tstate
button = Tk.Button(master=root, text='Quit', command=_quit)
button.pack(side=Tk.BOTTOM)
Tk.mainloop()
The question how to include a basemap plot into Tkinter has actually not been answered yet. So the idea is to create an axes ax in a figure and add the Basemap to the axes. This is done with the ax argument,
m = Basemap(..., ax=ax)
The figure is then added to the canvas in the usual way; below is a complete example.
from mpl_toolkits.basemap import Basemap
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg
from matplotlib.figure import Figure
import sys
if sys.version_info[0] < 3:
import Tkinter as Tk
else:
import tkinter as Tk
root = Tk.Tk()
root.wm_title("Embedding in TK")
fig = Figure(figsize=(5, 4), dpi=100)
ax = fig.add_subplot(111)
m = Basemap(projection='cyl',llcrnrlat=-90,urcrnrlat=90,\
llcrnrlon=-180,urcrnrlon=180,resolution='c', ax=ax)
m.drawcoastlines()
# a tk.DrawingArea
canvas = FigureCanvasTkAgg(fig, master=root)
canvas.show()
canvas.get_tk_widget().pack(side=Tk.TOP, fill=Tk.BOTH, expand=1)
toolbar = NavigationToolbar2TkAgg(canvas, root)
toolbar.update()
canvas._tkcanvas.pack(side=Tk.TOP, fill=Tk.BOTH, expand=1)
def _quit():
root.quit() # stops mainloop
root.destroy() # this is necessary on Windows to prevent
# Fatal Python Error: PyEval_RestoreThread: NULL tstate
button = Tk.Button(master=root, text='Quit', command=_quit)
button.pack(side=Tk.BOTTOM)
Tk.mainloop()
Note: In newer versions of matplotlib you should use NavigationToolbar2Tk instead of NavigationToolbar2TkAgg.
One simple technique is to define an on_click function, show an image, then overwrite that image. The on_click function will still read the mouse click location, despite the image change.
A sample code follows, demonstrating this is as shown:
import matplotlib.pyplot as plt
from mpl_toolkits.basemap import Basemap
def on_click(event):
if event.inaxes is not None:
print event.xdata, event.ydata
else:
print 'Clicked ouside axes bounds but inside plot window'
fig, ax = plt.subplots()
fig.canvas.callbacks.connect('button_press_event', on_click)
plt.show()
#Define map projection
m = Basemap(projection='cyl', llcrnrlat=-90, urcrnrlat=90,
llcrnrlon=-180, urcrnrlon=180, resolution='c')
m.drawcoastlines()
Related
Newbie here, i am working on a program which allows the users to see the animation of spring extension after adjusting the slider(higher frequency=higher extension). However, based on what i did, the spring will move immediately after the slider is moved. How should i change my code so that the spring will show its extension from 0 to the desired length like a full animation?
from tkinter import *
import math
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk
from matplotlib.figure import Figure
import matplotlib.patches as patches
window = Tk()
window.geometry("700x700")
window.configure(bg="white")
fig= Figure(figsize=(3, 3), dpi=100)
pg = fig.add_subplot(111)
def plot_graph():
pg.axis([0,800,-500,1000])
pg.get_yaxis().set_visible(False)
pg.set_title("Mass Spring Simulation ")
pg.set_xlabel("Spring extension (m)")
pg.set_ylabel("Displacement")
pg.grid()
def sine_graph():
pg.clear()
coorx=[]
coory=[]
for x in range(0,round(62.5*frequency)):
coorx.append(x)
y = amplitude*math.sin(x/(frequency))+250
coory.append(y)
rect = patches.Rectangle((coorx[-1], 0), 80,500, color='black')
pg.add_patch(rect)
Line= pg.plot(coorx,coory,label='Spring')
plot_graph()
fig.canvas.draw()
def update_fre(value):
global frequency
frequency = slider2.get()
sine_graph()
amplitude,frequency = 250,1
canvas = FigureCanvasTkAgg(fig, window)
canvas.get_tk_widget().pack(side=TOP, fill=BOTH, expand=1)
toolbar = NavigationToolbar2Tk(canvas,window)
toolbar.update()
L2 = Label(window,text="Force")
L2.pack()
slider2 = Scale(window,from_=1,to=10,orient=HORIZONTAL,command=update_fre)
slider2.pack()
sc= Label(window,text="Spring Constant = 0.0143N/m")
sc.pack()
sine_graph()
window.mainloop()
what i have tried (clearing the page and redrawing),but my computer will crash automatically when adjusting slider:
def sine_graph():
pg.clear()
coorx=[]
coory=[]
z=1
for i in range (frequency):
for x in range(1,round(62.5*z)):
pg.clear()
coorx.append(x)
y = amplitude*math.sin(x/(frequency))+250
coory.append(y)
z+=1
I have:
def create_plot():
df = pd.read_json("my_final_data.json")
small_df = df[df.small_airport.isin(['Y'])]
medium_df = df[df.medium_airport.isin(['Y'])]
large_df = df[df.large_airport.isin(['Y'])]
plt.figure(figsize=(35,10))
ax = sns.distplot(small_df['frequency_mhz'], color='red', label='Small Airports')
sns.distplot(medium_df['frequency_mhz'], color='green', ax=ax, label='Medium Airports')
sns.distplot(large_df['frequency_mhz'], ax=ax, label='Large Airports')
plt.legend(loc="upper right")
graph = plt.show()
return graph
#Generating tkinter window
window1 = tk.Tk()
figure = create_plot()
canvas = FigureCanvasTkAgg(figure, master=window1)
canvas.draw()
canvas.get_tk_widget().pack()
tk.mainloop()
Which launches 2(?) empty tkinter windows besides the canvas in one, but the IDE shows the actual graph im trying to print so I know the function is doing its job.
How do I make that returned graph stick to the window?
Youre getting 2 empty windows because you used plt.show() which is not intended to be used from within a tkinter application. The other one is an empty tkinter window (generated via tk.Tk()), without any content.
You also missed to give us sample data (my_final_data.json).
A little bit of research would have brought you a lot of examples for seaborn integration into tkinter
sns.distplot is deprecated as mentioned here, i would recommend using sns.displot or sns.histplot instead (some nice histplot examples)
This example should get you started:
import tkinter as tk
import seaborn as sns
from matplotlib import pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk
def create_plot(root):
# create random seaborn displot; replace this part with your own data
figure, ax = plt.subplots(figsize=(6, 6))
penguins = sns.load_dataset("penguins")
sns.histplot(data=penguins, x="flipper_length_mm", ax=ax, hue="species")
# create tkinter canvas from figure
canvas = FigureCanvasTkAgg(figure, master=root)
canvas.draw()
canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=1)
# optional: create toolbar
toolbar = NavigationToolbar2Tk(canvas, root)
toolbar.update()
canvas._tkcanvas.pack(side=tk.TOP, fill=tk.BOTH, expand=1)
# create your application
window1 = tk.Tk()
# call function to create plot
create_plot(window1)
# mainloop
tk.mainloop()
I am trying to plot data on tkinter canvas using matplotlib "imshow()" function. When I am running the code the data is getting plotted onto the canvas and in the navigation toolbar pixel coordinates (x and y coordinates) are getting displayed along with pixel values (in bracket). Issue is I want to display only the pixel coordinates and hide pixel values which is getting displayed in the navigation toolbar.
The code which I am using is:
import tkinter
import numpy as np
from matplotlib.backends.backend_tkagg import (
FigureCanvasTkAgg, NavigationToolbar2Tk)
from matplotlib.figure import Figure
import matplotlib.pyplot as plt
import tkinter
import numpy as np
from matplotlib.backends.backend_tkagg import (
FigureCanvasTkAgg, NavigationToolbar2Tk)
from matplotlib.figure import Figure
import matplotlib.pyplot as plt
root = tkinter.Tk()
fig = Figure(figsize=(5, 4), dpi=100)
fig.subplots_adjust(bottom=0, right=1, top=1, left=0, wspace=0, hspace=0)
ax = fig.add_subplot(111)
class Formatter(object):
def __init__(self, im):
self.im = im
def __call__(self, x, y):
return 'x={:.01f}, y={:.01f}'.format(x, y)
data = np.random.random((10,10))
im = ax.imshow(data, interpolation='none')
ax.format_coord = Formatter(im)
plt.show()
canvas1 = FigureCanvasTkAgg(fig, master=root)
canvas1.draw()
toolbar = NavigationToolbar2Tk(canvas1,root)
toolbar.update()
toolbar.pack(side=tkinter.TOP, fill=tkinter.X, padx=8)
canvas1.get_tk_widget().pack(side=tkinter.TOP, fill=tkinter.BOTH, expand=1, padx=10, pady=5)
canvas1._tkcanvas.pack(side=tkinter.TOP, fill=tkinter.BOTH, expand=1, padx=10, pady=5)
root.mainloop()
Kindly suggest how to hide pixel values getting displayed in the navigation toolbar (inside brackets) and display only pixel coordinates (x & y coordinates).
The value in the navigation toolbar is created by the images' format_cursor_data method. You can replace that method to return an empty string.
im = ax.imshow(data, interpolation='none')
im.format_cursor_data = lambda e: ""
One way is to override the method mouse_move:
class Navigator(NavigationToolbar2Tk):
def mouse_move(self, event):
self._set_cursor(event)
if event.inaxes and event.inaxes.get_navigate():
try:
s = event.inaxes.format_coord(event.xdata, event.ydata)
self.set_message(s)
except (ValueError, OverflowError):
pass
else:
self.set_message(self.mode)
...
toolbar = Navigator(canvas1,root)
...
I'm writing a script using tkinter and matplotlib for data processing, some parts of the code requires polygon selector to choose a region of interest. However, PolygonSelector fails to detect the motion of cursor.
It should be noted that this issue occurs when the interactive mode of matplotlib figure is on.
Simplified code and result are shown below:
#!/usr/bin/env python3
import matplotlib
matplotlib.use("TkAgg")
import tkinter as tk
import matplotlib.pyplot as plt
from matplotlib.widgets import PolygonSelector
root = tk.Tk()
def draw():
fig = plt.figure()
ax = fig.add_subplot(111)
plt.ion() # interactive mode is on
plt.show()
def onselect(data_input):
print(data_input)
PS = PolygonSelector(ax, onselect)
tk.Button(root, text='draw', command=draw).pack()
root.mainloop()
This is the plot after clicking 'draw' button on tkinter GUI, the starting point of polygon stucks at (0,0), it is expected to move with cursor:
When I call draw() outside of tkinter, PolygonSelector works fine:
def draw():
fig = plt.figure()
ax = fig.add_subplot(111)
plt.ion() # interactive mode is on
plt.show()
def onselect(data_input):
print(data_input)
PS = PolygonSelector(ax, onselect)
a = input() # prevent window from closing when execution is done
draw()
The simple solution would be to make sure you make your Polygon Selector a global variable. This will keep the selector visually updating.
#!/usr/bin/env python3
import tkinter as tk
import matplotlib
import matplotlib.pyplot as plt
from matplotlib.widgets import PolygonSelector
matplotlib.use("TkAgg")
root = tk.Tk()
ps = None
def draw():
global ps
fig = plt.figure()
ax = fig.add_subplot(111)
plt.ion()
plt.show()
ps = PolygonSelector(ax, on_select)
def on_select(data_input):
print(data_input)
tk.Button(root, text='draw', command=draw).pack()
root.mainloop()
If you build this into a class then you can avoid the use of global and get the behavior you want by apply the Polygon Selector as a class attribute.
#!/usr/bin/env python3
import tkinter as tk
import matplotlib
import matplotlib.pyplot as plt
from matplotlib.widgets import PolygonSelector
matplotlib.use("TkAgg")
class GUI(tk.Tk):
def __init__(self):
super().__init__()
self.ps = None
tk.Button(self, text='draw', command=self.draw).pack()
def draw(self):
fig = plt.figure()
ax = fig.add_subplot(111)
plt.ion()
plt.show()
self.ps = PolygonSelector(ax, self.on_select)
def on_select(self, data_input):
print(data_input)
if __name__ == "__main__":
GUI().mainloop()
Results:
I modified some matplotlib examples for test,this is code
#!/usr/bin/env python
import matplotlib
matplotlib.use('TkAgg')
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure
import matplotlib.pyplot as plt
import sys
if sys.version_info[0] < 3:
import Tkinter as Tk
else:
import tkinter as Tk
root = Tk.Tk()
root.wm_title("test in TK")
f = plt.figure(figsize=(3,3),dpi=98)
labels = 'Frogs', 'Hogs', 'Dogs', 'Logs'
sizes = [15, 30, 45, 10]
colors = ['yellowgreen', 'gold', 'lightskyblue', 'lightcoral']
explode = (0, 0.1, 0, 0) # only "explode" the 2nd slice (i.e. 'Hogs')
plt.pie(sizes, explode=explode, labels=labels, colors=colors,
autopct='%1.1f%%', shadow=True, startangle=90)
plt.axis('equal')
canvas = FigureCanvasTkAgg(f, master=root)
canvas.show()
canvas.get_tk_widget().pack(side=Tk.TOP, fill=Tk.BOTH, expand=1)
canvas._tkcanvas.pack(side=Tk.TOP, fill=Tk.BOTH, expand=1)
button = Tk.Button(master=root, text='Quit', command=sys.exit)
button.pack(side=Tk.BOTTOM)
Tk.mainloop()
I put a pie in tk,it can work,if I press 'Quit' button program will exit normally,if I press tk's 'X' the window will close,but this program in CMD window always waiting,not exit,I need use Ctrl+Break to close it,why?
The pyplot library provides a MATLAB-like plotting framework. It will make life easier for you by creating threads in the background, so that you can communicate with the pyplot user interface at the same time as using a CLI or some other interface. I guess what is happening is that this helper thread does not terminate when you press quit, and therefore the program does not exit. You should probably avoid using pyplot when making your own GUI.
One workaround could be this:
def exit():
plt.close('all')
sys.exit()
which would close all pyplot plots. But the best thing would probably be not to use pyplot in this case.