Python은 시각적 프로그래밍, 즉 GUI 프로그램 작성을 지원합니다. 이를 사용하여 즐겨 사용하는 데스크톱 프로그램을 작성할 수 있습니다. wxPython을 사용하여 인터페이스를 만드는 것은 매우 간단하지만 C#과 같은 컨트롤을 드래그할 수 없으며 고유한 코드 레이아웃을 작성해야 합니다. 작성을 마친 후 Python이 설치되지 않은 컴퓨터에서는 직접 py 파일을 실행할 수 없기 때문에 어떤 컴퓨터에서든 실행할 수 있도록 패키징할 수 있는 도구가 있습니까? 온라인에서 검색해 보니 py2exe가 이 기능만 완료할 수 있는 것으로 나타났습니다. wxPython과 py2exe는 모두 오픈 소스 무료 소프트웨어입니다.
환경구성
wxPython: sourceforge 프로젝트 페이지 https://sourceforge.net/projects/py2exe/files/py2exe/0.6.9/
다운로드 후 두 번 클릭하면 설치 프로그램이 해당 PythonScript에 자동으로 설치됩니다.
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'])
대괄호는 컴파일할 스크립트의 이름이고 앞에 있는 창은 GUI 프로그램으로 컴파일된다는 의미입니다. 명령줄 인터페이스용 실행 파일을 컴파일하려면 창을 콘솔로 변경하면 됩니다. 스크립트를 Windows 서비스로 컴파일해야 하는 경우 서비스 옵션을 사용할 수 있습니다.
모두 편집한 후 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와 크기가 자동으로 계산됩니다.
다음은 전체 코드입니다
#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()
약간의 설명:
클래스 App과 App().MainLoop()는 고정된 작성 방법입니다. 클래스 App 아래에 메인 프레임을 초기화하고 중앙에 배치하고 Show()하는 def OnInit 메서드가 있습니다. 주로 살펴보겠습니다. 프레임 정의
이 가젯은 레이아웃을 위해 boxSizer를 사용합니다. 저는 단지 하나의 boxSizer를 사용하고 그 안의 모든 컨트롤을 수직(수직) 방식으로 레이아웃합니다. MD5와 다음 텍스트 상자를 같은 곳에 넣으려면 OK입니다. 수평 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)
메인 boxSizer에 파일 경로를 추가합니다. 처음에는 모든 컨트롤을 먼저 패널에 넣은 다음 패널을 boxSizer에 넣을 것이라고 항상 생각했지만 이것은 잘못된 것입니다. 그러나 boxSizer에 직접 입력해야 하며 컨트롤의 상위 클래스를 패널로 설정하면 패널을 boxSizer에 넣을 필요가 없습니다. 매개변수 wx.LEFT|wx.TOP, border=10은 컨트롤을 나타냅니다. 위쪽과 왼쪽에서 10픽셀 떨어져 있고 wx.EXPAND를 사용하여 그것이 위치한 영역을 완전히 채울 수 있는지 생각해 본 적이 있습니다. 위쪽에서 10px, 왼쪽에서 20px로 설정할 수는 없을 것 같습니다. 이렇게 설정할 수는 없습니다. Add 함수에는 하나의 테두리 매개변수만 있을 수 있습니다. 즉, 동일한 값만 설정할 수 있는지 나중에 구현해 보겠습니다.
boxSizer.Add((-1,20)) #这个是添加一个空距离,距离上20px dropTarget = FileDropTarget(filetext,md5tx,filesizetx) self.panel.SetDropTarget( dropTarget )
윈도우 클래스에 드래그 앤 드롭 방식을 추가하는 것인데, 이 역시 비교적 고정된 작성 방식입니다
위 FileDropTarget 클래스의 __init__ 및 OnDropFiles 메서드도 고정 메서드이지만 내부 처리 기능이 다릅니다.
wxPython의 일부 스타일 및 플래그 매개변수는 레이아웃에 사용하기 위한 경험이 필요하며 이를 능숙하게 익히려면 많은 노력이 필요합니다. 더 자세한 내용은 다음 두 웹사이트를 참조하세요. , 자세한 내용을 읽어주세요