使用PythonTkinter开发GPGGA的坐标转换工具

  • 需求
  • 软件环境
  • 安装环境
  • Python2安装
  • sandglass安装
  • py2exe安装
  • 实际功能
  • 全部源码
  • 运行效果图
  • 要点总结
  • 改进建议


需求

最近负责Cors系统售后工作的同事提出了一个需求,他们在做算法精度比对工作时,需要一个很方便的转换GPGGA到经纬度的工具,他在市面上没找到,需要我们帮忙开发一款,我简单了解了一下GPGGA,其实很简单,麻烦的但是如果将程序放到Web程序下,web程序需要向外网服务,而考虑的系统的安全性,不应该内部系统暴露到外网。最终我决定用Python开发一款C/S架构的工具,而看重Python的原因是:简单,代码量少,支持直接发布为exe程序,不需要安装运行环境。

软件环境

  • Python 2.7.14(推荐没入坑的朋友还是要学习Python3,Python2要被放弃支持了,因为我之前学习、写工具用的都是Python2,相对比较熟悉,这里选用Python2进行开发)
  • py2exe Python的exe打包工具
  • sandglass 目前我用过最好用的时间包

安装环境

Python2安装

网上有大量的Python安装文档,这里不赘述了,提醒大家安装的时候一定要看一下系统环境是32位还是64位,再安装相应的程序

sandglass安装

安装好Python后,cmd运行命令pip install sandglass,自动安装就完成。

py2exe安装

下载路径:sourceforge下载路径这里再次提醒一下,一定要下载对应版本的py2exe,要和你的操作系统环境和Python版本对应起来,不然后续打包会报错,
比如:我的python版本是python2.7,操作系统是64位,我需要下载
py2exe-0.6.9.win64-py2.7.amd64.exe

实际功能

最终完成程序开发支持几个功能:

  • 输入GGA转换为坐标
  • 支持度显示和度分秒显示
  • 支持文件批量导入,批量生成坐标,以文件形式导出

全部源码

全部源码如下:

#!/usr/local/bin/Python
# -*- coding: UTF-8 -*-
"""Displays the keysym for each KeyPress event as you type."""
import Tkinter as tk
import tkFileDialog
from sandglass import *

window = tk.Tk()
window.title('获取坐标工具')
window.geometry('400x300')
ggaVar = tk.StringVar()
latLngVar = tk.StringVar()
typeVar = tk.IntVar()
fileVar = tk.StringVar()
pathVar = tk.StringVar()

ggaText = tk.Entry(window,show=None,textvariable = ggaVar)
ggaText.pack(pady=20,padx = 20,fill="x")

latLngText = tk.Entry(window,show=None,textvariable = latLngVar)
latLngText.pack(pady = 10,padx = 20,fill = "x")
latLngText['state'] = 'readonly'

#度类型
DOC_TYPE = 0
#度分秒类型
DOC_MIN_SEC_TYPE = 1

NO_FILE = "NO_FILE"



def format_lat_lng():
    try:
    	if pathVar.get() != NO_FILE:
    		loadFile(pathVar.get(),typeVar.get(),"./"+ben().strftime("%Y%m%d%H%M%S")+".txt")
    		return

    	if typeVar.get() == DOC_MIN_SEC_TYPE:
    		latLngVar.set(getLLDocMinSec(ggaVar.get()))
    	if typeVar.get() == DOC_TYPE:
    		latLngVar.set(getLLDoc(ggaVar.get()))
    except Exception as e:
    	latLngVar.set("GPGGA异常:"+str(e))

def getLLDoc(gga):
	splitMsg = gga.strip().split(",")
	latStr = splitMsg[2]
	latFlag = splitMsg[3]
	lngStr = splitMsg[4]
	lngFlag = splitMsg[5]
	latInt = int(latStr[0:2])
	latMin = float(latStr[2:-1])/60
	lat = latInt + latMin
	lat = lat if latFlag == "N" else -lat
	lngInt = int(lngStr[0:3])
	lngMin = float(lngStr[3:-1])/60
	lng = lngInt + lngMin
	lng = lng if lngFlag == "E" else -lng
	return str(lat)+","+str(lng)

def getLLDocMinSec(gga):
	splitMsg = gga.strip().split(",")
	latStr = splitMsg[2]
	latFlag = splitMsg[3]
	lngStr = splitMsg[4]
	lngFlag = splitMsg[5]
	latDoc = latStr[0:2]
	latDoc = latDoc if latFlag == "N" else ("-"+latDoc)
	latMinList = latStr[2:-1].split(".")
	latMin = latMinList[0]
	latSec = float("0."+latMinList[1]) * 60
	lat = "%s°%s′%s″"%(latDoc,latMin,latSec)
	lngDoc = int(lngStr[0:3])
	lngDoc = lngDoc if lngFlag == "E" else ("-"+lngDoc)
	lngMinList = lngStr[3:-1].split(".")
	lngMin = lngMinList[0]
	lngSec = float("0." + lngMinList[1]) * 60
	lng = "%s°%s′%s″"%(lngDoc,lngMin,lngSec)
	return str(lat) + "," + str(lng)

def loadFile(path,curtype,writeFilePath):
	with open(path, 'r') as file :
		with open(writeFilePath, 'w') as wfile:
			while 1:
				wlines = []
				lines = file.readlines(10000)
				if not lines:
					break
				for line in lines:
					result = "\n"
					if curtype == DOC_TYPE:
						try:
							result = getLLDoc(line) + "\n"
						except Exception as e:
							print e
					if curtype == DOC_MIN_SEC_TYPE:
						try:
							result = getLLDocMinSec(line) + "\n"
						except Exception as e:
							print e
					wlines.append(result)
				wfile.writelines(wlines)



def chageType():
	print(typeVar.get())


docButton = tk.Radiobutton(window, text="度", value=DOC_TYPE, command=chageType, variable=typeVar)
docMinSecButton = tk.Radiobutton(window, text="度分秒", value=DOC_MIN_SEC_TYPE, command=chageType, variable=typeVar)
docButton.pack()
docMinSecButton.pack()
typeVar.set(DOC_TYPE)

fileVar.set(u"您没有选择任何文件")
pathVar.set(NO_FILE)
lb = tk.Label(window,textvariable = fileVar)


lb.pack(padx = 10,pady = 10)

def uploadFile():
	filename = tkFileDialog.askopenfilename()
	if filename != "":
		#lb.config(text = u"您选择的文件是:" + filename)
		fileVar.set(u"您选择的文件是:" + filename)
		pathVar.set(filename)

def clearUploadFile():
	fileVar.set(u"您没有选择任何文件")
	pathVar.set(NO_FILE)


b2 = tk.Button(window,text = "上传文件",width=15,height = 2,command = uploadFile)

b2.pack(side='left',padx = 10)
b1 = tk.Button(window,text='获取坐标',width=15,
            height=2,command=format_lat_lng)
b1.pack(side='right',padx = 10)

b3 = tk.Button(window,text = "清除文件",width=15,height = 2,command = clearUploadFile)
b3.pack(side="left",padx = 10)


window.mainloop()

运行效果图

python 给定经纬度生成周围地图 python 经纬度转换_tkFileDialog

要点总结

下面开始要点总结:

  1. Tkinter使用了类似数据绑定的功能进行更新,如:
ggaVar = tk.StringVar()
ggaText = tk.Entry(window,show=None,textvariable = ggaVar)

上面文本框的内容通过ggaVar这个变量进行关联,监控ggaVar可以方便的了解到文本框的内容变更,同时也可以通过ggaVar.set()函数对文本框内的内容进行修改。
2. Tkinter的组件如果显示,必须要条用pack函数,同时pack函数可以对布局产生影响
3. 注意Tkinter包的引入,网上很多程序Tkinter包引入时是tkinter小写,python2.7在使用时Tkinter包已经改成首字母大写
4. 文件路径获取组件使用tkFileDialog不在Tkinter包内,需要单独引用
5. StringVar中set为None,再获取时获取的字符串“None”,而不是None对象

改进建议

整体来说,代码写的非常的丑