| 作者: | 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