Python是支援視覺化編程,即編寫gui程序,你可以用它來編寫自己喜歡的桌面程式。使用wxPython來做介面非常的簡單,只是不能像C#一樣拖曳控件,需要自行寫程式碼佈局。在完成編寫之後,由於直接的py檔案不能再沒有安裝python的電腦上運行,能否有一個打包成在任意電腦都能運行的工具,網上找找發現了py2exe正好可以完成這個功能。 wxPython和py2exe都是開源免費軟體。
環境配置
wxPython: sourceforge專案頁https://sourceforge.net/projects/py2exe/files/py2exe/0.6.9/
下載後雙擊安裝即可,安裝程式會自動安裝到對應pythonScripts下。
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
ImportError: No module named sip
setup.py py2exe --includes sip
運行結束之後,會在路徑下產生dist和 build兩個目錄。其中dist目錄中就是編譯產生的檔案。如果要在其他未安裝Python的機器上執行編譯好的程序,只要將dist目錄複製到其他機器上即可。雙擊運行wxTest.exe,如圖:
使用wxPython建立一個計算檔md5的GUI工具
小工具最後是下面這個樣子,將檔案拖曳到上面會自動計算其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' #线程函数 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))
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等參數在佈局中使用需要一些經驗,還有它的很多控件和與之綁定的方法,要想熟練掌握還需要下一些工夫,下面兩個網站算是介紹比較詳細,要多查閱