在 tkinter/python 中嵌入 Slippy 地图
Embed Slippy Map in tkinter/python
我正在寻找一种能力 generate/embed 将一张灵活的地图(即 OpenStreetMaps)放入 Tkinter 应用程序中,然后我可以在其上绘制大地坐标和椭圆数据(来自 pyodbc 连接到 *. mdb 文件)。用户需要地理信息(城市名称等)进行更有效的分析。
我查看了 mapnik、Leaflet、pyrender、TileMill、TileStache 等来实现这个白日梦,none 似乎确实符合要求。数据需要是交互式的(气球信息、select-able 等)和动态的(当新的用户定义 sql 解析数据以进行过滤时更新)。原因是,这个程序的另一半是 运行 通过 Tkinter 和 MatPlotLib。全面的系统检修是我试图避免的。
如能为我指明正确的方向,我们将不胜感激。
下面提供了我的一些代码:
(对于任何错误或白痴,我提前表示歉意。我在过去 3 个月里只在 python 中写作...全部自学)
#!/usr/bin/env python
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg
import matplotlib.image as mpimg
from matplotlib.backend_bases import key_press_handler
from matplotlib.figure import Figure
from tkFileDialog import askopenfilename
from pylab import *
import tkMessageBox as mb
import pyodbc
import sys
import ttk
if sys.version_info[0] < 3:
import Tkinter as Tk
else:
import tkinter as Tk
def connect_db():
global c
conn = pyodbc.connect('DRIVER={Microsoft Access Driver (*.mdb)};DBQ=%s' % filename)
c = conn.cursor()
def query_db():
global time
global freq
global pri
global pw
freq=[]
time=[]
pri = []
pw = []
c.execute("SELECT utc_usec_time_stamp, freq_mhz, pri, pw FROM SampleData")
rows = c.fetchall()
for row in rows:
time.append(row[0])
freq.append(row[1])
pri.append(row[2])
pw.append(row[3])
progressbar.step(0.0008)
root.update_idletasks()
def plot():
global sb1
global sb2
global sb3
global canvas
f = Figure()
f.clf()
f.subplots_adjust(bottom=0.05,top=0.98,left=0.08,right=0.98,hspace=0.1)
sb1 = f.add_subplot(3,1,1)
sb1.scatter(time,freq)
setp(sb1.get_xticklabels(), visible=False)
sb1.set_ylabel("FREQ (MHz)")
sb2 = f.add_subplot(3,1,2, sharex=sb1)
sb2.scatter(time,pri)
setp(sb2.get_xticklabels(), visible=False)
sb2.set_ylabel("PRI (usec)")
sb3 = f.add_subplot(3,1,3, sharex=sb1)
sb3.scatter(time,pw)
setp(sb3.get_xticklabels(), visible=False)
sb3.set_xlabel("TIME")
sb3.set_ylabel("PW (usec)")
canvas = FigureCanvasTkAgg(f, master=root)
logo.pack_forget()
canvas.show()
canvas.get_tk_widget().pack(side=Tk.RIGHT, fill=Tk.BOTH, expand=1)
toolbar = NavigationToolbar2TkAgg( canvas, root )
toolbar.update()
canvas._tkcanvas.pack(side=Tk.TOP, fill=Tk.BOTH, expand=1)
def test():
global pri
p = Tk.Toplevel(root)
f = Tk.Frame(p)
f.pack(side="top")
f.grid_rowconfigure(1, weight=1)
f.grid_columnconfigure(4, weight=1)
lbl = Tk.Label(f, text="label")
lbl.grid(row=1, column=1)
e = Tk.Entry(f)
e.grid(row=1, column=2)
b = Tk.Button(f, text="Button")
b.grid(row=1, column=3)
lbl2 = Tk.Label(f, text=" ")
lbl2.grid(row=1, column=4, padx=100)
fig = Figure()
sb1 = fig.add_subplot(1,1,1)
sb1.hist(pri)
canvas = FigureCanvasTkAgg(fig, master=p)
canvas.show()
canvas.get_tk_widget().pack(side=Tk.RIGHT, fill=Tk.BOTH, expand=1)
toolbar = NavigationToolbar2TkAgg( canvas, p )
toolbar.update()
canvas._tkcanvas.pack(side=Tk.TOP, fill=Tk.BOTH, expand=1)
def __init__():
global root
global rmi
global rma
global prmi
global prma
global pwmi
global pwma
global pdf
global progressbar
global tf
global logo
root = Tk.Tk()
root.wm_title("Airborne Tactical Analysis System (ATLAS)")
root.wm_state('zoomed')
menubar = Tk.Menu(root)
File = Tk.Menu(menubar, tearoff=0)
File.add_command(label="Open", command=open)
File.add_separator()
File.add_command(label="Exit", command=_quit)
menubar.add_cascade(label="File", menu=File)
Options = Tk.Menu(menubar, tearoff=0)
Options.add_command(label="-Coming Soon-")
menubar.add_cascade(label="Options", menu=Options)
PlotData = Tk.Menu(menubar, tearoff=0)
PlotData.add_command(label="FREQ v PW")
PlotData.add_command(label="FREQ v PRI")
PlotData.add_command(label="PRI v PW")
PlotData.add_command(label="FREQ HISTOGRAM")
PlotData.add_command(label="PRI HISTOGRAM", command=test)
PlotData.add_command(label="PW HISTOGRAM")
menubar.add_cascade(label="Plot Data", menu=PlotData)
Geo = Tk.Menu(menubar, tearoff=0)
Geo.add_command(label="Generate Map")
menubar.add_cascade(label="Geo", menu=Geo)
root.config(menu=menubar)
pdf = Tk.Frame(borderwidth=1, relief="sunken")
pdf.pack(side="left", fill="y")
pdf.grid_rowconfigure(7, weight=1)
pdf.grid_columnconfigure(1, weight=1)
tf = Tk.Frame(borderwidth=1, relief="sunken")
tf.pack(side="bottom", fill="x")
progl = Tk.Label(text=" Processing: ")
progl.pack(in_=tf, side="left", ipady=10)
progressbar = ttk.Progressbar(orient='horizontal', length=200, mode='determinate')
progressbar.pack(in_=tf, side="left")
fl = Tk.Label(text="Current File:")
fl.pack(in_=tf, side="left", ipadx=10)
b1 = Tk.Button(pdf, text="Submit", command=filter_builder)
b1.grid(in_=pdf, row=2, columnspan=3, pady=10)
buf = Tk.Label(text=" ")
mil = Tk.Label(text="MIN")
mal = Tk.Label(text="MAX")
rfl = Tk.Label(text="RF:")
pwl = Tk.Label(text="PW:")
prl = Tk.Label(text="PRI:")
prmi = Tk.Entry()
prma = Tk.Entry()
pwmi = Tk.Entry()
pwma = Tk.Entry()
rmi = Tk.Entry()
rma = Tk.Entry()
buf.grid(in_=pdf, column=3,row=3)
mil.grid(in_=pdf, column=1,row=3, pady=4)
mal.grid(in_=pdf, column=2,row=3)
rfl.grid(in_=pdf, column=0,row=4)
rmi.grid(in_=pdf, column=1,row=4)
rma.grid(in_=pdf, column=2,row=4)
prl.grid(in_=pdf, column=0,row=5)
prmi.grid(in_=pdf, column=1,row=5)
prma.grid(in_=pdf, column=2,row=5)
pwl.grid(in_=pdf, column=0,row=6)
pwmi.grid(in_=pdf, column=1,row=6)
pwma.grid(in_=pdf, column=2,row=6)
photo = Tk.PhotoImage(file="image.gif")
logo = Tk.Label(image=photo)
logo.image = photo
logo.pack(side="top", fill="both", pady=100)
root.mainloop()
__init__()
所以,经过大量研究和修改(阅读:深夜和挫折),我终于想出了最好的方法来做到这一点:
1) 利用 mplleaflet,利用 matplotlib 的绘图功能在光滑的地图上渲染数据点。这是我的前端(API).
2) 我在 GitHub 上偶然发现了 python-mbtiles,它实际上符合通过 Tornado 离线提供 *.mbtiles png 数据的要求。我操纵了 mplleaflet 的 url 以指向我的本地主机端口,Tornado 正在为要渲染的图块提供服务。
3) 两者都完成后,我意识到 Tkinter 无法以任何方式解析 html,所以我最终将它们全部废弃并使用更强大的 PySide,因为它包含我曾经使用过的 QWebKit将它包含在我的 GUI 中而不是实际的网络浏览器中 window。
总而言之,这是一个很长但很有启发性的过程。我希望其他人也能从中吸取教训。
这是一个老问题,但如果有人想在他们的应用程序中使用交互式 OpenStreetMap 地图,我编写了一个名为 TkinterMapView 的库来显示基于图块的地图,如 OpenStreetMap 或 Google-Maps 在 Tkinter 中:
文档:https://github.com/TomSchimansky/TkinterMapView
安装:pip3 install tkintermapview
您只需创建一个 TkinterMapView 小部件,就像您创建一个框架或按钮一样,您就有了一个交互式地图,您还可以在其中设置位置标记或路径。您可以使用坐标将地图聚焦在特定位置,或将地址字符串传递给小部件,如下例所示:
import tkinter
from tkintermapview import TkinterMapView
root_tk = tkinter.Tk()
root_tk.geometry(f"{600}x{400}")
root_tk.title("map_view_simple_example.py")
# create map widget
map_widget = TkinterMapView(root_tk, width=600, height=400, corner_radius=0)
map_widget.pack(fill="both", expand=True)
map_widget.set_address("Berlin Germany", marker=True)
root_tk.mainloop()
上面的代码会给你以下内容window:
我正在寻找一种能力 generate/embed 将一张灵活的地图(即 OpenStreetMaps)放入 Tkinter 应用程序中,然后我可以在其上绘制大地坐标和椭圆数据(来自 pyodbc 连接到 *. mdb 文件)。用户需要地理信息(城市名称等)进行更有效的分析。
我查看了 mapnik、Leaflet、pyrender、TileMill、TileStache 等来实现这个白日梦,none 似乎确实符合要求。数据需要是交互式的(气球信息、select-able 等)和动态的(当新的用户定义 sql 解析数据以进行过滤时更新)。原因是,这个程序的另一半是 运行 通过 Tkinter 和 MatPlotLib。全面的系统检修是我试图避免的。
如能为我指明正确的方向,我们将不胜感激。
下面提供了我的一些代码:
(对于任何错误或白痴,我提前表示歉意。我在过去 3 个月里只在 python 中写作...全部自学)
#!/usr/bin/env python
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg
import matplotlib.image as mpimg
from matplotlib.backend_bases import key_press_handler
from matplotlib.figure import Figure
from tkFileDialog import askopenfilename
from pylab import *
import tkMessageBox as mb
import pyodbc
import sys
import ttk
if sys.version_info[0] < 3:
import Tkinter as Tk
else:
import tkinter as Tk
def connect_db():
global c
conn = pyodbc.connect('DRIVER={Microsoft Access Driver (*.mdb)};DBQ=%s' % filename)
c = conn.cursor()
def query_db():
global time
global freq
global pri
global pw
freq=[]
time=[]
pri = []
pw = []
c.execute("SELECT utc_usec_time_stamp, freq_mhz, pri, pw FROM SampleData")
rows = c.fetchall()
for row in rows:
time.append(row[0])
freq.append(row[1])
pri.append(row[2])
pw.append(row[3])
progressbar.step(0.0008)
root.update_idletasks()
def plot():
global sb1
global sb2
global sb3
global canvas
f = Figure()
f.clf()
f.subplots_adjust(bottom=0.05,top=0.98,left=0.08,right=0.98,hspace=0.1)
sb1 = f.add_subplot(3,1,1)
sb1.scatter(time,freq)
setp(sb1.get_xticklabels(), visible=False)
sb1.set_ylabel("FREQ (MHz)")
sb2 = f.add_subplot(3,1,2, sharex=sb1)
sb2.scatter(time,pri)
setp(sb2.get_xticklabels(), visible=False)
sb2.set_ylabel("PRI (usec)")
sb3 = f.add_subplot(3,1,3, sharex=sb1)
sb3.scatter(time,pw)
setp(sb3.get_xticklabels(), visible=False)
sb3.set_xlabel("TIME")
sb3.set_ylabel("PW (usec)")
canvas = FigureCanvasTkAgg(f, master=root)
logo.pack_forget()
canvas.show()
canvas.get_tk_widget().pack(side=Tk.RIGHT, fill=Tk.BOTH, expand=1)
toolbar = NavigationToolbar2TkAgg( canvas, root )
toolbar.update()
canvas._tkcanvas.pack(side=Tk.TOP, fill=Tk.BOTH, expand=1)
def test():
global pri
p = Tk.Toplevel(root)
f = Tk.Frame(p)
f.pack(side="top")
f.grid_rowconfigure(1, weight=1)
f.grid_columnconfigure(4, weight=1)
lbl = Tk.Label(f, text="label")
lbl.grid(row=1, column=1)
e = Tk.Entry(f)
e.grid(row=1, column=2)
b = Tk.Button(f, text="Button")
b.grid(row=1, column=3)
lbl2 = Tk.Label(f, text=" ")
lbl2.grid(row=1, column=4, padx=100)
fig = Figure()
sb1 = fig.add_subplot(1,1,1)
sb1.hist(pri)
canvas = FigureCanvasTkAgg(fig, master=p)
canvas.show()
canvas.get_tk_widget().pack(side=Tk.RIGHT, fill=Tk.BOTH, expand=1)
toolbar = NavigationToolbar2TkAgg( canvas, p )
toolbar.update()
canvas._tkcanvas.pack(side=Tk.TOP, fill=Tk.BOTH, expand=1)
def __init__():
global root
global rmi
global rma
global prmi
global prma
global pwmi
global pwma
global pdf
global progressbar
global tf
global logo
root = Tk.Tk()
root.wm_title("Airborne Tactical Analysis System (ATLAS)")
root.wm_state('zoomed')
menubar = Tk.Menu(root)
File = Tk.Menu(menubar, tearoff=0)
File.add_command(label="Open", command=open)
File.add_separator()
File.add_command(label="Exit", command=_quit)
menubar.add_cascade(label="File", menu=File)
Options = Tk.Menu(menubar, tearoff=0)
Options.add_command(label="-Coming Soon-")
menubar.add_cascade(label="Options", menu=Options)
PlotData = Tk.Menu(menubar, tearoff=0)
PlotData.add_command(label="FREQ v PW")
PlotData.add_command(label="FREQ v PRI")
PlotData.add_command(label="PRI v PW")
PlotData.add_command(label="FREQ HISTOGRAM")
PlotData.add_command(label="PRI HISTOGRAM", command=test)
PlotData.add_command(label="PW HISTOGRAM")
menubar.add_cascade(label="Plot Data", menu=PlotData)
Geo = Tk.Menu(menubar, tearoff=0)
Geo.add_command(label="Generate Map")
menubar.add_cascade(label="Geo", menu=Geo)
root.config(menu=menubar)
pdf = Tk.Frame(borderwidth=1, relief="sunken")
pdf.pack(side="left", fill="y")
pdf.grid_rowconfigure(7, weight=1)
pdf.grid_columnconfigure(1, weight=1)
tf = Tk.Frame(borderwidth=1, relief="sunken")
tf.pack(side="bottom", fill="x")
progl = Tk.Label(text=" Processing: ")
progl.pack(in_=tf, side="left", ipady=10)
progressbar = ttk.Progressbar(orient='horizontal', length=200, mode='determinate')
progressbar.pack(in_=tf, side="left")
fl = Tk.Label(text="Current File:")
fl.pack(in_=tf, side="left", ipadx=10)
b1 = Tk.Button(pdf, text="Submit", command=filter_builder)
b1.grid(in_=pdf, row=2, columnspan=3, pady=10)
buf = Tk.Label(text=" ")
mil = Tk.Label(text="MIN")
mal = Tk.Label(text="MAX")
rfl = Tk.Label(text="RF:")
pwl = Tk.Label(text="PW:")
prl = Tk.Label(text="PRI:")
prmi = Tk.Entry()
prma = Tk.Entry()
pwmi = Tk.Entry()
pwma = Tk.Entry()
rmi = Tk.Entry()
rma = Tk.Entry()
buf.grid(in_=pdf, column=3,row=3)
mil.grid(in_=pdf, column=1,row=3, pady=4)
mal.grid(in_=pdf, column=2,row=3)
rfl.grid(in_=pdf, column=0,row=4)
rmi.grid(in_=pdf, column=1,row=4)
rma.grid(in_=pdf, column=2,row=4)
prl.grid(in_=pdf, column=0,row=5)
prmi.grid(in_=pdf, column=1,row=5)
prma.grid(in_=pdf, column=2,row=5)
pwl.grid(in_=pdf, column=0,row=6)
pwmi.grid(in_=pdf, column=1,row=6)
pwma.grid(in_=pdf, column=2,row=6)
photo = Tk.PhotoImage(file="image.gif")
logo = Tk.Label(image=photo)
logo.image = photo
logo.pack(side="top", fill="both", pady=100)
root.mainloop()
__init__()
所以,经过大量研究和修改(阅读:深夜和挫折),我终于想出了最好的方法来做到这一点:
1) 利用 mplleaflet,利用 matplotlib 的绘图功能在光滑的地图上渲染数据点。这是我的前端(API).
2) 我在 GitHub 上偶然发现了 python-mbtiles,它实际上符合通过 Tornado 离线提供 *.mbtiles png 数据的要求。我操纵了 mplleaflet 的 url 以指向我的本地主机端口,Tornado 正在为要渲染的图块提供服务。
3) 两者都完成后,我意识到 Tkinter 无法以任何方式解析 html,所以我最终将它们全部废弃并使用更强大的 PySide,因为它包含我曾经使用过的 QWebKit将它包含在我的 GUI 中而不是实际的网络浏览器中 window。
总而言之,这是一个很长但很有启发性的过程。我希望其他人也能从中吸取教训。
这是一个老问题,但如果有人想在他们的应用程序中使用交互式 OpenStreetMap 地图,我编写了一个名为 TkinterMapView 的库来显示基于图块的地图,如 OpenStreetMap 或 Google-Maps 在 Tkinter 中:
文档:https://github.com/TomSchimansky/TkinterMapView
安装:pip3 install tkintermapview
您只需创建一个 TkinterMapView 小部件,就像您创建一个框架或按钮一样,您就有了一个交互式地图,您还可以在其中设置位置标记或路径。您可以使用坐标将地图聚焦在特定位置,或将地址字符串传递给小部件,如下例所示:
import tkinter
from tkintermapview import TkinterMapView
root_tk = tkinter.Tk()
root_tk.geometry(f"{600}x{400}")
root_tk.title("map_view_simple_example.py")
# create map widget
map_widget = TkinterMapView(root_tk, width=600, height=400, corner_radius=0)
map_widget.pack(fill="both", expand=True)
map_widget.set_address("Berlin Germany", marker=True)
root_tk.mainloop()
上面的代码会给你以下内容window: