windows中使用wxpython和py2exe开发python的gui程序的实例教程

python是支持可视化编程,即编写gui程序,你可以用它来编写自己喜欢的桌面程序。使用wxpython来做界面非常的简单,只是不能像c#一样拖动控件,需要自行写代码布局。在完成编写之后,由于直接的py文件不能再没有安装python的电脑上运行,能否有一个打包成在任意电脑都能运行的工具,网上找找发现了py2exe正好可以完成这个功能。wxpython和py2exe都是开源免费软件。

环境配置
wxpython: sourceforge项目页https://sourceforge.net/projects/py2exe/files/py2exe/0.6.9/
下载后双击安装即可,安装程序会自动安装到对应python\scripts下。
py2exe:官方下载主页https://www.wxpython.org/download.php
同样双击即可安装,注意下载要对应使用的python版本。
下面分别示例说明wxpython和py2exe的简单使用。

基本示例
文件名:wxtest.py:

# -*- coding: cp936 -*-
”’mainwindow类完成最简单的编辑功能,添加一个主菜单,两个子菜单(about和exit)”’
import wx
class mainwindow(wx.frame):
”’定义一个窗口类”’
def __init__(self, parent, title):
wx.frame.__init__(self, parent, title=title, size=(300, 300))
self.control = wx.textctrl(self, style=wx.te_multiline)
self.setupmenubar()
self.show(true)
def setupmenubar(self):
self.createstatusbar()
menubar = wx.menubar()
menufile = wx.menu()
mnuabout = menufile.append(wx.id_about, ‘&about’, ‘about this shit’)
mnuexit = menufile.append(wx.id_exit, ‘e&xit’, ‘end program’)
menubar.append(menufile, ‘&file’)
#事件绑定
self.bind(wx.evt_menu, self.onabout, mnuabout)
self.bind(wx.evt_menu, self.onexit, mnuexit)
self.setmenubar(menubar)
def onabout(self, evt):
”’点击about的事件响应”’
dlg = wx.messagedialog(self, ‘this app is a simple text editor’, ‘about my app’, wx.ok)
dlg.showmodal()
dlg.destroy()
def onexit(self, evt):
”’点击退出”’
self.close(true)
app = wx.app(false)
frame = mainwindow(none, ‘small editor’)
app.mainloop() #循环监听事件

编辑好改文件后,使用py2exe将python脚本编译成windows可执行文件,这样就不需要python解释器了。要使用py2exe,首先要编写一个编译脚本,然后通过python运行编译脚本即可将其他的脚本编译成可执行文件。以下实例是将要编译成可执行文件的脚本,文件名:setup.py

import distutils
import py2exe
distutils.core.setup(windows=[‘wxtest.py’])

在setup.py中除了导入必需的模块以外,只有一条语句:

distutils.core.setup(windows=[‘wxtest.py’])

方括号中就是要编译的脚本名,前边的windows 表示将其编译成gui程序。如果要编译命令行界面的可执行文件,只要将windows改为console,如果需要将脚本编译成windows服务,则可以使用service选项。
都编辑好之后,将wxtest.py和setup.py放在同一个路径下,cmd进入该路径,输入:

setup.py py2exe

如果在运行时报以下错误:

error: msvcp90.dll: no such file or directory

是因为没有找到msvcp90.dll,在windows目录下搜索msvcp90.dll这个文件,然后拷到python安装目录的dlls下就可以了。
当打包pyqt项目时,可能会报以下错误

importerror: no module named sip

这时只需要在打包时加上–includes sip就行啦,如:

setup.py py2exe –includes sip

运行结束之后,会在路径下生成dist和 build两个目录。其中dist目录中就是编译生成的文件。如果要在其他未安装python的机器上运行编译好的程序,只要将dist目录复制到其他机器上即可。双击运行wxtest.exe,如图:

2016711164500179.jpg (300×300)

使用wxpython建立一个计算文件md5的gui工具
小工具最终是下面这个样子,将文件拖到上面会自动计算其md5与size

2016711164609816.png (443×334)

下面是全部的代码

#coding:gbk
import wx
import optparse
import time,hashlib
import threading
import os
def checkmd5(pefile):
try:
f = open(pefile,’rb’)
data = f.read()
m = hashlib.md5()
m.update(data)
f.close()
return m.hexdigest()
except:
return ‘error’
def getfilesize(filename):
try:
size = int(os.path.getsize(filename))
return size
except:
return ‘error’
#线程函数
class functhread(threading.thread):
def __init__(self, func, *params, **parammap):
threading.thread.__init__(self)
self.func = func
self.params = params
self.parammap = parammap
self.rst = none
self.finished = false
def run(self):
self.rst = self.func(*self.params, **self.parammap)
self.finished = true
def getresult(self):
return self.rst
def isfinished(self):
return self.finished
def dointhread(func, *params, **parammap):
t_setdaemon = none
if ‘t_setdaemon’ in parammap:
t_setdaemon = parammap[‘t_setdaemon’]
del parammap[‘t_setdaemon’]
ft = functhread(func, *params, **parammap)
if t_setdaemon != none:
ft.setdaemon(t_setdaemon)
ft.start()
return ft
class filedroptarget(wx.filedroptarget):
def __init__(self, filetext,md5tx,filesizetx):
wx.filedroptarget.__init__(self)
self.filepath = filetext
self.md5tx = md5tx
self.filesizetx = filesizetx
def ondropfiles(self, x, y, filenames):
filename = filenames[0].encode(‘gbk’)
print filename
print type(filename)
self.filepath.setvalue(filename)
md5 = dointhread(checkmd5,filename)
filesize = dointhread(getfilesize,filename)
while true:
if not md5.isfinished():
time.sleep(0.5)
else:
self.md5tx.setvalue(md5.getresult())
break
while true:
if not filesize.isfinished():
time.sleep(0.5)
else:
self.filesizetx.setvalue(str(filesize.getresult()))
break
class frame(wx.frame): #frame 进行初始化
def __init__(self,title):
wx.frame.__init__(self,none,title=title,size = (400,300))
boxsizer = wx.boxsizer(wx.vertical)
self.panel = wx.panel(self)
# boxsizer.add(self.panel,1,wx.expand|wx.all) #wx.all 周围的距离,expand扩充到全部
filepath = wx.statictext(self.panel,-1,”filedir(请将文件拖到本对话框中)”)
filetext = wx.textctrl(self.panel,-1,””,size=(350,20))
md5st = wx.statictext(self.panel,-1,”md5″)
md5tx = wx.textctrl(self.panel,-1,size=(250,20))
filesizest = wx.statictext(self.panel,-1,’filesize’)
filesizetx = wx.textctrl(self.panel,-1,size=(250,20))
# hashst = wx.statictext(self.panel,-1,’hash’)
# hashtx = wx.textctrl(self.panel,-1,size=(250,20))
boxsizer.add(filepath,0,wx.expand|wx.left|wx.top,border=10)
boxsizer.add(filetext,0,wx.left|wx.top,border=10)
boxsizer.add((-1,20))
boxsizer.add(md5st,0,wx.left|wx.top,border=10)
boxsizer.add(md5tx,0,wx.left|wx.top,border=10)
boxsizer.add((-1,10))
boxsizer.add(filesizest,0,wx.left|wx.top,border=10)
boxsizer.add(filesizetx,0,wx.left|wx.top,border=10)
# boxsizer.add((-1,10))
# boxsizer.add(hashst,0,wx.left|wx.top,border=10)
# boxsizer.add(hashtx,0,wx.left|wx.top,border=10)
droptarget = filedroptarget(filetext,md5tx,filesizetx)
self.panel.setdroptarget( droptarget )
self.panel.setsizer(boxsizer)
class app(wx.app): ##继承wx.app
def oninit(self): ##还没有调起来的时候读取初始化
self.frame = frame(‘md5&size信息’)
self.frame.centre()
self.frame.show(true)
return true
def killself(evt = none):
os.system(‘taskkill /f /t /pid %d >nul 2>nul’ % win32process.getcurrentprocessid())
if __name__ == ‘__main__’:
parser = optparse.optionparser()
parser.add_option(‘-x’, ‘–no-update’, dest = ‘test’, action = ‘store_true’, help = ‘start without update’)
parser.add_option(‘-t’, ‘–no-update-test’, dest = ‘test2’, action = ‘store_true’, help = ‘start without update debug’)
options, args = parser.parse_args()
if options.test:
print(“-x param”)
if options.test2:
print(“-t param”)
app(redirect = false).mainloop()

一点点的解释:

class app与app().mainloop()是固定写法,在class app下有一个def oninit方法来初始化主的frame,将其居中并且show()出来,没什么好说的,主要看一下frame的定义

这个小工具使用的是boxsizer来布局,为了简单我只使用了一个boxsizer,将里面的所有控件采用vertical(垂直)的方式来布局,如果想要将md5与后面的文本框放在同一行,那么就需要添加一个水平的boxsizer,然后那将这个水平的boxsizer再放入主的boxsizer

boxsizer = wx.boxsizer(wx.vertical) #初始化一个垂直的boxsizer,也是整个框架的主sizer
self.panel = wx.panel(self) #初始化一个panel,这个panel是放了放之后的控件的
filepath = wx.statictext(self.panel,-1,”filedir(请将文件拖到本对话框中)”)
filetext = wx.textctrl(self.panel,-1,””,size=(350,20))
md5st = wx.statictext(self.panel,-1,”md5″)
md5tx = wx.textctrl(self.panel,-1,size=(250,20))
filesizest = wx.statictext(self.panel,-1,’filesize’)
filesizetx = wx.textctrl(self.panel,-1,size=(250,20))

上面是初始化相应的静态文本与文本框,方法中的第一个参数是其所在的父类窗口,这里也就是self.panel,其实也可以不用panel,而是将其直接放入到boxsizer中

boxsizer.add(filepath,0,wx.expand|wx.left|wx.top,border=10)

将filepath加入到主的boxsizer中,这里一开始我有一些困惑,一开始我一直以为先将所有的控件放入到panel中,然后再将panel放入到boxsizer中,但是这样是不对的,而应该是直接就入到boxsizer中,将该控件的父类设置为panel,之后就没有将panel放入boxsizer这一步操作,wx.left|wx.top,border=10 这个参数表示的是该控件距离上来左各有10个像素的距离,再使用wx.expand来使其充分的填充其所在的区域,我曾经想,可否设置成距离上10px,左20px,但是貌似不能这样设置,add函数里只能有一个border参数,换句话说只能设置相同的数值,之后我再找找是否可以实现。

boxsizer.add((-1,20)) #这个是添加一个空距离,距离上20px
droptarget = filedroptarget(filetext,md5tx,filesizetx)
self.panel.setdroptarget( droptarget )

这个是放该窗口类添加一个拖拽方法,也是比较固定的写法

上面的class filedroptarget中的__init__与ondropfiles方法也是固定的方法,只是里面的处理函数不同。

wxpython中的一些style与flag等参数在布局中使用需要一些经验,还有它的很多控件和与之绑定的方法,要想熟练掌握还需要下一些工夫,下面两个网站算是介绍比较详细,要多多查阅

Posted in 未分类

发表评论