模拟DDE功能的Socket实现

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

Contents

DDE是动态数据交换,原本为Windows下的一个进程间数据交换的功能,在 wxWindows 中提到了已经实现了些功能,在 Windows 下说是使用DDE,在Linux下使用Socket来模拟实现,不过我没有找到,最后学习了一些Socket和线程方面的编程终于简陋地实现了。我为什么要这个功能,原因是:

  1. 使用DDE可以避免启动多个 NewEdit 实例。
  2. 当在命令行打开文件时,可以让已经存在的 NewEdit 实例打开此文件。
  3. 在Windows下可以放到资源管理员中的右键发送菜单中,在打开其它目录下的文件时更加方便。

下面是我实现的过程:

一、创建DDE模块--DDE.py

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,表示已经存在服务器程序。

二、DDE模块的调用--DDESupport.py

这里只是简单说明调用的地点和处理:

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

[返回]