Windows中使用wxPython和py2exe开发Python的GUI程序的实例教程

作者:Kevin_Yang 时间:2021-07-07 08:52:17 

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,如图:

Windows中使用wxPython和py2exe开发Python的GUI程序的实例教程

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

Windows中使用wxPython和py2exe开发Python的GUI程序的实例教程

下面是全部的代码


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

标签:wxPython,py2exe
0
投稿

猜你喜欢

  • Python编程实现凯撒密码加密示例

    2021-04-22 01:58:56
  • Python使用pymongo库操作MongoDB数据库的方法实例

    2023-06-04 06:20:22
  • Python实现类似比特币的加密货币区块链的创建与交易实例

    2022-03-19 15:49:49
  • Python多线程应用于自动化测试操作示例

    2021-04-09 19:20:58
  • 用Python生成会跳舞的美女

    2023-04-04 11:52:09
  • PHP实现图片合并的示例详解

    2023-06-08 14:14:07
  • Python 操作 PostgreSQL 数据库示例【连接、增删改查等】

    2021-12-14 00:54:08
  • 从ThinkPHP3.2.3过渡到ThinkPHP5.0学习笔记图文详解

    2023-09-04 10:21:28
  • 5步让你的CSS样式表成功减肥

    2009-08-02 21:27:00
  • Python+Socket实现基于TCP协议的客户与服务端中文自动回复聊天功能示例

    2023-07-18 20:11:01
  • jupyter notebook中美观显示矩阵实例

    2023-06-06 18:13:35
  • Python3学习urllib的使用方法示例

    2021-09-23 00:08:08
  • 利用Python将数值型特征进行离散化操作的方法

    2023-12-02 02:43:36
  • CSS Sprites (CSS图像拼合技术)教程工具

    2009-05-26 15:30:00
  • python爬虫爬取微博评论案例详解

    2022-12-23 13:31:53
  • 概述javascript在Google IE中的调试技巧

    2023-08-08 11:50:29
  • 微信小程序顶部导航栏可滑动并选中放大

    2023-08-23 18:49:59
  • ASP幻灯片

    2009-09-04 18:05:00
  • 如何更快更好地调试ASP程序代码?

    2009-11-23 20:13:00
  • python实现图书管理系统

    2022-03-11 01:22:54
  • asp之家 网络编程 m.aspxhome.com