Windows中使用wxPython和py2exe開(kāi)發(fā)Python的GUI程序的實(shí)例教程
Python是支持可視化編程,即編寫(xiě)gui程序,你可以用它來(lái)編寫(xiě)自己喜歡的桌面程序。使用wxPython來(lái)做界面非常的簡(jiǎn)單,只是不能像C#一樣拖動(dòng)控件,需要自行寫(xiě)代碼布局。在完成編寫(xiě)之后,由于直接的py文件不能再?zèng)]有安裝python的電腦上運(yùn)行,能否有一個(gè)打包成在任意電腦都能運(yùn)行的工具,網(wǎng)上找找發(fā)現(xiàn)了py2exe正好可以完成這個(gè)功能。wxPython和py2exe都是開(kāi)源免費(fèi)軟件。
環(huán)境配置
wxPython: sourceforge項(xiàng)目頁(yè)https://sourceforge.net/projects/py2exe/files/py2exe/0.6.9/
下載后雙擊安裝即可,安裝程序會(huì)自動(dòng)安裝到對(duì)應(yīng)python\Scripts下。
py2exe:官方下載主頁(yè)https://www.wxpython.org/download.php
同樣雙擊即可安裝,注意下載要對(duì)應(yīng)使用的Python版本。
下面分別示例說(shuō)明wxPython和py2exe的簡(jiǎn)單使用。
基本示例
文件名:wxTest.py:
# -*- coding: cp936 -*- '''MainWindow類完成最簡(jiǎn)單的編輯功能,添加一個(gè)主菜單,兩個(gè)子菜單(about和exit)''' import wx class MainWindow(wx.Frame): '''定義一個(gè)窗口類''' 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): '''點(diǎn)擊about的事件響應(yīng)''' dlg = wx.MessageDialog(self, 'This app is a simple text editor', 'About my app', wx.OK) dlg.ShowModal() dlg.Destroy() def onExit(self, evt): '''點(diǎn)擊退出''' self.Close(True) app = wx.App(False) frame = MainWindow(None, 'Small Editor') app.MainLoop() #循環(huán)監(jiān)聽(tīng)事件
編輯好改文件后,使用py2exe將Python腳本編譯成Windows可執(zhí)行文件,這樣就不需要Python解釋器了。要使用py2exe,首先要編寫(xiě)一個(gè)編譯腳本,然后通過(guò)Python運(yùn)行編譯腳本即可將其他的腳本編譯成可執(zhí)行文件。以下實(shí)例是將要編譯成可執(zhí)行文件的腳本,文件名:setup.py
import distutils import py2exe distutils.core.setup(windows=['wxTest.py'])
在setup.py中除了導(dǎo)入必需的模塊以外,只有一條語(yǔ)句:
distutils.core.setup(windows=['wxTest.py'])
方括號(hào)中就是要編譯的腳本名,前邊的windows 表示將其編譯成GUI程序。如果要編譯命令行界面的可執(zhí)行文件,只要將windows改為console,如果需要將腳本編譯成Windows服務(wù),則可以使用service選項(xiàng)。
都編輯好之后,將wxTest.py和setup.py放在同一個(gè)路徑下,cmd進(jìn)入該路徑,輸入:
setup.py py2exe
error: MSVCP90.dll: No such file or directory
當(dāng)打包PyQt項(xiàng)目時(shí),可能會(huì)報(bào)以下錯(cuò)誤
ImportError: No module named sip
setup.py py2exe --includes sip
運(yùn)行結(jié)束之后,會(huì)在路徑下生成dist和 build兩個(gè)目錄。其中dist目錄中就是編譯生成的文件。如果要在其他未安裝Python的機(jī)器上運(yùn)行編譯好的程序,只要將dist目錄復(fù)制到其他機(jī)器上即可。雙擊運(yùn)行wxTest.exe,如圖:

使用wxPython建立一個(gè)計(jì)算文件md5的GUI工具
小工具最終是下面這個(gè)樣子,將文件拖到上面會(huì)自動(dòng)計(jì)算其md5與size

下面是全部的代碼
#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'
#線程函數(shù)
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 進(jìn)行初始化
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擴(kuò)充到全部
filepath = wx.StaticText(self.panel,-1,"FileDir(請(qǐng)將文件拖到本對(duì)話框中)")
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): ##還沒(méi)有調(diào)起來(lái)的時(shí)候讀取初始化
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()
一點(diǎn)點(diǎn)的解釋:
class App與App().MainLoop()是固定寫(xiě)法,在class App下有一個(gè)def OnInit方法來(lái)初始化主的Frame,將其居中并且Show()出來(lái),沒(méi)什么好說(shuō)的,主要看一下Frame的定義
這個(gè)小工具使用的是boxSizer來(lái)布局,為了簡(jiǎn)單我只使用了一個(gè)boxSizer,將里面的所有控件采用VERTICAL(垂直)的方式來(lái)布局,如果想要將MD5與后面的文本框放在同一行,那么就需要添加一個(gè)水平的boxSizer,然后那將這個(gè)水平的boxSizer再放入主的boxSizer
boxSizer = wx.BoxSizer(wx.VERTICAL) #初始化一個(gè)垂直的boxSizer,也是整個(gè)框架的主Sizer self.panel = wx.Panel(self) #初始化一個(gè)panel,這個(gè)panel是放了放之后的控件的 filepath = wx.StaticText(self.panel,-1,"FileDir(請(qǐng)將文件拖到本對(duì)話框中)") 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))
上面是初始化相應(yīng)的靜態(tài)文本與文本框,方法中的第一個(gè)參數(shù)是其所在的父類窗口,這里也就是self.panel,其實(shí)也可以不用panel,而是將其直接放入到boxSizer中
boxSizer.Add(filepath,0,wx.EXPAND|wx.LEFT|wx.TOP,border=10)
將filepath加入到主的boxSizer中,這里一開(kāi)始我有一些困惑,一開(kāi)始我一直以為先將所有的控件放入到panel中,然后再將panel放入到boxSizer中,但是這樣是不對(duì)的,而應(yīng)該是直接就入到boxSizer中,將該控件的父類設(shè)置為panel,之后就沒(méi)有將panel放入boxSizer這一步操作,wx.LEFT|wx.TOP,border=10 這個(gè)參數(shù)表示的是該控件距離上來(lái)左各有10個(gè)像素的距離,再使用wx.EXPAND來(lái)使其充分的填充其所在的區(qū)域,我曾經(jīng)想,可否設(shè)置成距離上10px,左20px,但是貌似不能這樣設(shè)置,Add函數(shù)里只能有一個(gè)border參數(shù),換句話說(shuō)只能設(shè)置相同的數(shù)值,之后我再找找是否可以實(shí)現(xiàn)。
boxSizer.Add((-1,20)) #這個(gè)是添加一個(gè)空距離,距離上20px dropTarget = FileDropTarget(filetext,md5tx,filesizetx) self.panel.SetDropTarget( dropTarget )
這個(gè)是放該窗口類添加一個(gè)拖拽方法,也是比較固定的寫(xiě)法
上面的class FileDropTarget中的__init__與OnDropFiles方法也是固定的方法,只是里面的處理函數(shù)不同。
wxPython中的一些style與flag等參數(shù)在布局中使用需要一些經(jīng)驗(yàn),還有它的很多控件和與之綁定的方法,要想熟練掌握還需要下一些工夫,下面兩個(gè)網(wǎng)站算是介紹比較詳細(xì),要多多查閱
相關(guān)文章
Python實(shí)現(xiàn)將羅馬數(shù)字轉(zhuǎn)換成普通阿拉伯?dāng)?shù)字的方法
這篇文章主要介紹了Python實(shí)現(xiàn)將羅馬數(shù)字轉(zhuǎn)換成普通阿拉伯?dāng)?shù)字的方法,簡(jiǎn)單分析了羅馬數(shù)字的構(gòu)成并結(jié)合實(shí)例形式給出了Python轉(zhuǎn)換羅馬數(shù)字為阿拉伯?dāng)?shù)字的實(shí)現(xiàn)方法,需要的朋友可以參考下2017-04-04
Python反爬實(shí)戰(zhàn)掌握酷狗音樂(lè)排行榜加密規(guī)則
最新的酷狗音樂(lè)反爬來(lái)襲,本文介紹如何利用Python掌握酷狗排行榜加密規(guī)則,本章內(nèi)容只限學(xué)習(xí),切勿用作其他用途!!?。?! 有需要的朋友可以借鑒參考下2021-10-10
Python實(shí)現(xiàn)在PyPI上發(fā)布自定義軟件包的方法詳解
在Python中我們經(jīng)常使用pip來(lái)安裝第三方Python軟件包,其實(shí)我們每個(gè)人都可以免費(fèi)地將自己寫(xiě)的Python包發(fā)布到PyPI上。本文我們就將詳細(xì)介紹如何發(fā)布測(cè)試包,需要的可以參考一下2022-06-06
使Python代碼流暢無(wú)縫連接的鏈?zhǔn)秸{(diào)用技巧
鏈?zhǔn)秸{(diào)用是一種編程風(fēng)格,它允許將多個(gè)方法調(diào)用連接在一起,形成一個(gè)連貫的操作鏈,在Python中,鏈?zhǔn)秸{(diào)用常常用于使代碼更簡(jiǎn)潔、易讀,尤其在處理數(shù)據(jù)處理和函數(shù)式編程中應(yīng)用廣泛2024-01-01
python爬蟲(chóng)爬取指定內(nèi)容的解決方法
這篇文章主要介紹了python爬蟲(chóng)爬取指定內(nèi)容,爬取一些網(wǎng)站下指定的內(nèi)容,一般來(lái)說(shuō)可以用xpath來(lái)直接從網(wǎng)頁(yè)上來(lái)獲取,但是當(dāng)我們獲取的內(nèi)容不唯一的時(shí)候我們無(wú)法選擇,我們所需要的、所指定的內(nèi)容,需要的朋友可以參考下2022-06-06
Python+Selenium實(shí)現(xiàn)網(wǎng)站滑塊拖動(dòng)操作
這篇文章主要為大家詳細(xì)介紹了如何利用Python+Selenium模擬實(shí)現(xiàn)登錄某網(wǎng)站的滑塊拖動(dòng)操作,文中的示例代碼講解詳細(xì),需要的可以參考一下2022-09-09
Python如何使用type()函數(shù)查看數(shù)據(jù)的類型
這篇文章主要介紹了Python如何使用type()函數(shù)查看數(shù)據(jù)的類型,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-05-05
Python實(shí)現(xiàn)的三層BP神經(jīng)網(wǎng)絡(luò)算法示例
這篇文章主要介紹了Python實(shí)現(xiàn)的三層BP神經(jīng)網(wǎng)絡(luò)算法,結(jié)合完整實(shí)例形式分析了Python三層BP神經(jīng)網(wǎng)絡(luò)算法的具體實(shí)現(xiàn)與使用相關(guān)操作技巧,需要的朋友可以參考下2018-02-02

