@shellex说: RT : 广告之拉拉情缘,SISLEY为主

这个年代居然是VB6最王道

NND。这个年代居然是VB6最王道。

“博学女生”竞赛委员会的Huanyu MM委托Shellex开发一款抢答器。Shellex用Python + GTK快速搞了一个。TCPServer + Socket Client,简洁又优雅(汗)。

但是往Windows移植的时候(用py2exe),Vista能运行Server端不能运行Client端,log提示是<string>的错误(??!!!)。XP则是什么都运行不了,直接死那儿了,也没有log。NND。

然后跟Rod同学探讨这个事情,我说,你看现在为Windows开发程序什么最快速部署方便?他说,.Net?我说.Net还需要.Net Framework,就现在XP上的版本和覆盖水准实在不行,我还不如直接去配置pygtk呢。

也是,什么好呢?Java需要JVM,Borland用VCL那套没有兴趣,MFC…我不如直接用Win32 SDK呢,况且手头也没有VS(话说我连个正常的Windows都没有,不愿折腾VS)。Adobe AIR貌似是个不错的选择,但是我还不会。Mozilla的extension,时间不够。

最后我说,看现在搞得,Windows作为一个最流行的桌面系统,为它开发一些GUI小工具这么麻烦…要全是Linux Desktop就好了。Windows上还是VB6最王道…Rod同学也得同意吧。

后记:

VB6出来的程序一直87运行时错误…据说是与卡巴有关,话说我身边还没有没装卡巴的Windows机器(这世道!),Shellex不玩了,去网上找一个现成的拉倒。Huanyu Zhao MM,非常抱歉。

下面是我的python代码,大家看看就得了,写得乱七八糟,不必太在意。
Server:

# -*- coding=utf-8 -*-
import SocketServer
import gtk, gtk.glade
import threading
import gobject
import sys
import threading
from random import randint

players = {
    1: (1, "Shellex"),
    2: (2, "小明"),
    3: (3, "大名"),
    4: (4, "大白菜"),
    5: (5, "李鹏"),
    6: (6, "普京"),
    7: (7, "拉登"),
    8: (8, "卡拉赞"),
    9: (9, "太懒得"),
    10: (10, "大泉"),
    11: (11, "解决"),
    12: (12, "可看"),
    13: (13, "懒了"),
    14: (14, "文我"),
    15: (15, "轻轻"),
    16: (16, "仍然"),
    17: (17, "一样"),
    18: (18, "喔喔"),
    19: (19, "批评"),
    20: (20, "信息"),
}

father = None

class waiter(SocketServer.BaseRequestHandler):
    def handle(self):
        global father
        print "handle:"
        while 1:
            data = self.request.recv(32)
            if data.strip() == 'bye':
                return
            if data.strip().isdigit():
                father.add(int(data.strip()))

    def finish(self):
        print "finish"
    def setup(self):
        print "setup", self.client_address

class server(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self, name='server')
        self.father = None
        self.working = False
    def run(self):
        self.working = True
        s = SocketServer.ThreadingTCPServer(("", 1234), waiter);
        while self.working:
            s.handle_request()

class main_win():
    def refresh_btn_clicked_cb(self, widget):
        self.queue = []
        self.ui.get_widget('first_label').set_markup('%s' % '☣')
        self.update()

    def update(self):
        model = self.ui.get_widget('queue_view').get_model()
        model.clear()
        for idx, each in enumerate(self.queue):
            n, name = each
            if idx == 0:
                self.ui.get_widget('first_label').set_markup('%s' % self.num[n])
            print each
            col = model.append()
            model.set(col, 0, n, 1, name, 2, "shit")

    def add(self, id):
        if not players.has_key(id):
            return
        if self.queue.count(self.players[id]) == 1:
            pass
        else:
            self.queue.append(self.players[id])
            print self.queue[-1]
        self.update()

    def start_server(self):
        self.s = server()
        self.s.win = self.ui
        self.s.queue = self.queue
        self.s.setDaemon(True)
        self.s.start()

    def toolbutton1_clicked_cb(self, widget):
        self.add(randint(1, 20))
        self.update()

    def quit_me(self, args):
        self.s.working = False
        gtk.main_quit()

    def init_tree_view(self):
        # create columns
        queue_view = self.ui.get_widget('queue_view')
        renderer = gtk.CellRendererText()
        queue_view.insert_column_with_attributes(-1, 'No.', renderer, text=0)
        queue_view.insert_column_with_attributes(-1, 'Name', renderer, text=1)
        queue_view.insert_column_with_attributes(-1, 'Time', renderer, text=2)
        # create model
        list_stroe = gtk.ListStore(
            gobject.TYPE_STRING,
            gobject.TYPE_STRING,
            gobject.TYPE_STRING)

        iter = list_stroe.append()
        queue_view.set_model(list_stroe)

    def __init__(self):
        global father
        # load ui and connect signals
        self.ui = gtk.glade.XML('server.glade')
        self.ui.signal_autoconnect(self)
        self.win = self.ui.get_widget('main_win')
        self.win.show()
        self.win.connect("destroy", self.quit_me)

        self.init_tree_view()

        self.num = ['○','①','②','③','④','⑤','⑥','⑦','⑧','⑨','⑩','⑪','⑫','⑬','⑭','⑮','⑯','⑰','⑱','⑲','⑳']
        self.players = players
        self.queue = []
        father = self
        self.start_server()
        try:
            gtk.main()
        except:
            print "Shit. A fucking exception caught. Dying a death."
            sys.exit(1)

if __name__ == '__main__':
    gtk.gdk.threads_init()
    mw = main_win()

Client:

import gtk, gtk.glade
import sys
import socket
class main_win():
    def rush_btn_clicked_cb(self, widget):
        if self.s != None:
            self.s.send(self.id + '\n')
    def connect_tbtn_clicked_cb(self, widget):
        print self.do_connection(self.host, self.port)

    def quit_me(self, args):
        gtk.main_quit()

    def do_connection(self, host, port):
        res = socket.getaddrinfo(host, port, socket.AF_UNSPEC, socket.SOCK_STREAM)[0]
        af, socktype, proto, canonname, sa = res
        try:
            self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM, proto)
        except socket.error,(errno, err_msg):
            self.s = None
            self.ui.get_widget('status_label').set_text( err_msg)
        try:
            self.s.connect(sa)
        except socket.error, (errno, err_msg):
            self.s.close()
            self.s = None
            self.ui.get_widget('status_label').set_text(err_msg)
        if self.s != None:
            self.ui.get_widget('status_label').set_text('Connected!')     

        return self.s

    def __init__(self):
        global father
        # load ui and connect signals
        self.ui = gtk.glade.XML('client.glade')
        self.ui.signal_autoconnect(self)
        self.win = self.ui.get_widget('main_win')
        self.win.show()
        self.win.connect("destroy", self.quit_me)
        # id
        if len(sys.argv) < 2:
            sys.exit()
        self.id = sys.argv[1]
        # connect
        self.host = 'localhost'
        self.port = 1234
        self.do_connection('localhost', 1234)
        try:
            gtk.main()
        except:
            print "Shit. A fucking exception caught. Dying a death."
            sys.exit(1)

if __name__ == '__main__':
    gtk.gdk.threads_init()
    mw = main_win()
  1. On October 27, 2008 at 11:27 pm

    沙发想说一句话:
    有个叫jsmooth的东西,可以将java程序包装成exe,在运行时自动下载JVM(也就是sun的JRE)。

    Notify
  2. On October 28, 2008 at 1:53 am
    Dustman :

    Shit. A fucking exception caught. Dying a death.

    我看了2页代码 只看到这句话..

    脑子对脏字太敏感了..

    Notify
  3. On October 28, 2008 at 10:28 pm

    @abettor,
    那还是需要JRE的说。
    那篇论文我看了,我觉得很有道理哦。

    Notify
  4. On October 28, 2008 at 10:28 pm

    @Dustman,
    我也是。

    Notify
  5. On October 31, 2008 at 9:21 pm
    huanyu :

    你那开头怎么回事?给我改了

    Shellex不玩了,去网上找一个现成的 拉倒 。

    就这样啊,原来,本来还很感谢你,现在告诉你,不谢。

    Notify
  6. On November 22, 2008 at 12:27 am
    华华 :

    Windows 下运行不了?
    猫猫不会是 Linux 下使用 py2exe 生成 exe 的吧,
    然后 wine 通过后就丢 windows 测试?

    如果是这样,就是又遇上 wine py2exe 的问题了,

    这儿有个绕过了 wine 问题的 winepy2exe
    http://groups.google.com/group/python-cn/browse_thread/thread/c2e53d3c2e0668d

    可以先用 winepywine.sh 来安装 PyGTk 模块,
    完了就可以 winepy2exe.sh 了

    注意这个没有特别为 PyGtk 配置,所以生成 dist 目录后,
    需要手工从 Gtk 安装目录拷贝 etc lib share 文件夹到 dist/

    为节省空间,可以删掉 dist/share/locale/ 多余翻译

    而 -x 参数是生成单一 exe 文件,方便分发

    我这儿简单的 PyGtk 程序打包为单一 exe 体积是 4M 上下
    (比如 Hiweed 安装程序)

    Notify
  7. On November 22, 2008 at 11:08 am
    shellex :

    @华华,
    我在Windows下py3exe的。我也不知道为什么,pyGTK到Windows下GUI会死掉(XP上,2k3和Vista没问题)。如果遇到多线程则会出现各种问题…好长时间没看到你了?

    Notify

Leave a Reply