现在的位置: 首页 > 综合 > 正文

用 HTML 作为 Python 的图形界面

2013年10月10日 ⁄ 综合 ⁄ 共 2666字 ⁄ 字号 评论关闭

想用 Python 写点小程序,以前一直都是命令行界面的,想用图像界面,但又没多少选择。wxPython 貌似不错,可是不支持 Py3k。而且小程序,越简单越好,用不着带一个巨大的第三方库了。

今天突然想到一个思路。用 HTML 做一个界面,可以有各种表单按钮什么的,通过 Ajax 和 Server 通讯。Server 当然就是 Python 了。Server 收到 Client 发过来的请求,就转交给相应的模块执行。好处是纯脚本,无需编译,跨平台跨浏览器,以后还可以用 HTML5,CSS 之类的做出各种特效,当然 JS 特效就原生支持啦。

因为是简单的 GUI,不想用巨大的 jQuery 了。上网找到一个 Ajax 教程,follow 一下:http://www.w3school.com.cn/ajax/index.asp

建一个简单的测试用 HTML 文件:

网上很多旧教程说要判断 IE6,因为 IE6 要用另一种方法创建 XMLHttpRequest,不过 IE6 早就被淘汰了,根本不用去管,可以节省好多代码。(这就是标准化的好处啊!)

<HTML>
	<HEAD>
		<SCRIPT type="text/javascript">
			function test() {
				var xmlhttp = new XMLHttpRequest();
				xmlhttp.onreadystatechange = function() {
					if(this.readyState == 4 && this.status == 200) {
						document.getElementById("log").innerHTML = this.responseText;
					}
				};
				xmlhttp.open("GET", "?cmd=1&opt=2.0", true);
				xmlhttp.send();
			}
		</SCRIPT>
	</HEAD>
	<BODY>
		<BUTTON onclick="test()">test</BUTTON>
		<DIV>
			<TEXTAREA id="log" rows="10" cols="40" readonly=true></TEXTAREA>
		</DIV>
	</BODY>
</HTML>

用 Python 写一个简单的 Server,封装起来

import http.server

callback = None

class AjaxHandler(http.server.BaseHTTPRequestHandler):
	def do_GET(self):
		contenttype = callback(self.path)
		self.send_response(200)
		self.send_header("Content-type", contenttype[1])
		self.end_headers()
		self.wfile.write(contenttype[0])

callback 是一个回调函数,Server 收到请求之后就调用它,然后它把命令 dispatch 到相应的 module。

import http.server
import AjaxHandler

PORT = 8080

def AjaxHandlerCallbackFunc(path):
	content = b"None"
	contenttype = "text/plain"
	pathlist = path.split('?', 1)
	if(pathlist[0] == "/"):
		if(len(pathlist) == 1):
			f = open("test.html", "rb")
			content = f.read()
			contenttype = "text/html"
			f.close()
		else:
			args = pathlist[1].split('&')
			cmd = args[0].split('=')
			if cmd[0] == "cmd":
				params = {}
				if len(args) > 1:
					for arg in args[1:]:
						param = arg.split('=')
						val = param[1]
						try:
							val = int(val)
						except ValueError:
							try:
								val = float(val)
							except ValueError:
								pass
						params[param[0]] = val
				content = DispatchCmd(cmd[1], params)
	elif(pathlist[0] == "/favicon.ico"):
		f = open("test.png", "rb")
		content = f.read()
		contenttype = "image/png"
		f.close()
	return (content, contenttype)

def DispatchCmd(cmd, args):
	return str({cmd:args}).encode()

if __name__ == "__main__":
	try:
		AjaxHandler.callback = AjaxHandlerCallbackFunc
		server = http.server.HTTPServer(("", PORT), AjaxHandler.AjaxHandler)
		print("HTTP server is starting at port "+repr(PORT)+'...')
		print("Press ^C to quit")
		server.serve_forever()
	except KeyboardInterrupt:
		print("^Shutting down server...")
		server.socket.close()

几点说明:

1. Ajax 原生不支持跨域调用。如果直接打开本地 HTML 文件,然后向 HTTP 服务器发送请求,那么 responseText 会一直返回空。虽然网上有很多解决方案可以实现跨域 Ajax (包括 jQuery),但我觉得最简单的方法就是不从本地打开 HTML 文件,而是从 Server 打开,就好了。所以当访问根目录的时候,Server 就读取那个网页文件并返回。

2. Client 会经常请求 favicon.ico,看着烦,就随便找了个图标返回了。

3. 传进来的参数,从字符串转换成数字,要判断是 int 还是 float。比较 fancy 的做法是用正则表达式。这里用了简单粗暴 ugly 的实现方法,一个不行就试另一个。

4. transmission 时间可以忽略,但是网页的 load 和 parse 需要时间。所以第一次打开网页的时候可能会有延时,不过不是很重要啦。

对 Ajax 不熟悉,也从来没写过 HTTP Server,以上代码现学现编加调试花了我快一天时间。代码框架已经出来了,以后慢慢扩展 DispatchCmd() 就可以了。

抱歉!评论已关闭.