作者: | limodou |
---|---|
联系: | limodou@gmail.com |
版本: | dde.txt 42 2005-09-28 05:19:21Z limodou |
主页: | http://wiki.woodpecker.org.cn/moin/NewEdit |
BLOG: | http://www.donews.net/limodou |
版权: | GPL |
DDE是动态数据交换,原本为Windows下的一个进程间数据交换的功能,在 wxWindows 中提到了已经实现了些功能,在 Windows 下说是使用DDE,在Linux下使用Socket来模拟实现,不过我没有找到,最后学习了一些Socket和线程方面的编程终于简陋地实现了。我为什么要这个功能,原因是:
下面是我实现的过程:
1 from socket import * 2 import threading 3 import traceback 4 5 ADDR = '127.0.0.1' 6 PORT = 50000 7 8 server = None 9 10 def init(): 11 server = socket(AF_INET, SOCK_STREAM) 12 try: 13 server.bind((ADDR, PORT)) 14 except: 15 # traceback.print_exc() 16 server = None 17 return server 18 return server 19 20 def start(server, app=None): 21 server.listen(1) 22 # print 'server starting' 23 while True: 24 conn = server.accept()[0] 25 try: 26 data = conn.recv(256) 27 if data: 28 lines = data.splitlines() 29 cmd = lines[0] 30 if cmd == 'data': 31 # print 'data' 32 if app: 33 app.frame.readfiles = lines[1:] 34 elif cmd == 'stop': 35 # print 'stop' 36 break 37 except: 38 # traceback.print_exc() 39 pass 40 41 42 def sendraw(cmd, data): 43 sendSock = socket(AF_INET, SOCK_STREAM) 44 sendSock.connect((ADDR, PORT)) 45 sendSock.send(cmd+'\n'+data) 46 sendSock.close() 47 48 def stop(): 49 sendraw('stop', '') 50 51 def senddata(data): 52 sendraw('data', data) 53 54 def run(app=None): 55 server = init() 56 if server: 57 threading.Thread(target=start, args=(server, app,)).start() 58 return True 59 else: 60 return False
DDE.py是一个利用Socket来模拟实现DDE的功能。它使用50000端口,不知道会不会与其它程序有冲突。
init()函数是将IP地址进行绑定,准备生成一个Server,如果成功,则说明以前没有启动过NewEdit实例,如果失败,则 说明以前启动过,则当前实例可以作为一个客户端,将向服务器发送文件名信息。
start()函数是当init()函数成功时,进入服务器的监听循环。它实现了两个简单的协议:
data\n数据行 这里数据行为文件,如果有多个文件名,中间用\n隔开 stop 用于通知服务器关闭
sendraw()是一个发送数据的最底层函数
stop()用于通知服务器退出
senddata()用于发送文件名
run()用来启动服务器,如果init()成功,则启动线程进行服务器监听循环,返回True,表示服务器启动成功。如果 init()失败,则返回False,表示已经存在服务器程序。
这里只是简单说明调用的地点和处理:
1 import DDE 2 import Mixin 3 import wx 4 5 def init(app, filenames): 6 if app.ddeflag: 7 if not DDE.run(app): 8 # print "send data" 9 DDE.senddata('\n'.join(filenames)) 10 return True, False 11 Mixin.setPlugin('app', 'init', init, Mixin.HIGH, 0) 12 13 def afterclosewindow(win): 14 if win.app.ddeflag: 15 DDE.stop() 16 Mixin.setPlugin('mainframe', 'afterclosewindow', afterclosewindow) 17 18 def init(win): 19 win.readfiles = [] 20 Mixin.setPlugin('mainframe', 'init', init) 21 22 def on_idle(win, event): 23 if win.readfiles: 24 for filename in win.readfiles: 25 win.editctrl.new(filename) 26 win.readfiles = [] 27 win.Show() 28 Mixin.setPlugin('mainframe', 'on_idle', on_idle)
大家可能对NewEdit 的结构不熟悉,没关系,我只简单地说明一下。
DDE的处理是在生成主窗口之前进行的,上面的init()函数。如果调用DDE.run()成功,则什么都不做。如果不成功,则调 用senddata()将命令行中的文件名发给上一个实例(服务器)。ddeflag表示是否启动DDE功能,此值由命令行选项-n来指定的。
NewEdit对接收的文件名首先是在DDE.py模块中的start()函数(33行)处理的。可以看到:
33 app.frame.readfiles = lines[1:]
它是把文件名存入到了主窗体的readfiles变量中,并不直接打开。(lines[1:]索引从1开始是因为引一个元素是data命令 )。原来我是直接写的打开操作,但wxPython报错,说是不可以在线程中执行某些操作,只可以在主线程中进行。没有变法, 我想了一个变通的方法:我先将文件名保存,然后在IDLE事件激活时,此时系统不忙,再描扫是否变量有值,有则打开,打 开后,再将readfiles值置为[]即可。因此你会看到on_idle()函数的处理,就是完成这一功能。
当退出NewEdit时,必须要关闭掉线程。如果不关闭,NewEdit无法正常结束。于是在关闭NewEdit事件中加上通知服务器 线程关闭的命令,就是afterclosewindow()所做的工做。
做完这些工作,NewEdit就可以只启动一个实例,并且当打开一个文件时,如果已经存在一个实例,则当前实例不会继续运 行,而是将文件名发到上一个实例,则它打开文件。
在Windows上,你可以在sendto菜单中增加一个快捷方式,这样在使用资源管理器时可以方便的使用右键发送功能打开这个 文件。我的环境下的快捷方式的命令行为:
C:\Python23\python.exe D:\program\newedit\NewEdit.py