1、准备工具
1.1 cython、mingw(gcc)并添加到系统环境变量中
1.2 wxpython 模块
缺点:需要python环境和相应的包
# coding:utf-8
import re
import os
import wx
import subprocess
import shutil
import time
class MainWindow(wx.Frame):
def __init__(self, parent, title, pos, size):
super(MainWindow, self).__init__(parent, title=title, pos=pos, size=size, style=wx.DEFAULT_FRAME_STYLE)
self.__comList = [] # combox下拉列表
# 获取Python路径
self.GetDefaultPath()
# 初始化界面
self.InitUI()
# 事件绑定
self.BindEvent()
def InitUI(self):
"""
初始化界面
"""
self.panel = wx.Panel(self)
self.sizer = wx.GridBagSizer(0, 0)
self.sbox1 = wx.StaticText(self.panel, wx.ID_ANY, u'python路径:')
self.sizer.Add(self.sbox1, pos=(0, 0), span=(1, 1), flag=wx.ALL, border=5)
self.combox = wx.ComboBox(self.panel, wx.ID_ANY, choices=self.__comList, style=wx.CB_READONLY)
self.m_btnAdd1 = wx.Button(self.panel, wx.ID_ANY, u"添 加")
self.sizer.Add(self.combox, pos=(1, 0), span=(1, 8), flag=wx.EXPAND | wx.ALL, border=5)
self.sizer.Add(self.m_btnAdd1, pos=(1, 8), span=(1, 1), flag=wx.ALL, border=5)
self.sbox2 = wx.StaticText(self.panel, wx.ID_ANY, u'需要打包exe的py文件:')
self.sizer.Add(self.sbox2, pos=(2, 0), span=(1, 1), flag=wx.ALL, border=5)
self.m_btnAdd2 = wx.Button(self.panel, wx.ID_ANY, u"添 加")
self.m_txtCtrl2 = wx.TextCtrl(self.panel, wx.ID_ANY, style=wx.TE_LEFT | wx.TE_READONLY)
self.sizer.Add(self.m_txtCtrl2, pos=(3, 0), span=(1, 8), flag=wx.EXPAND | wx.ALL, border=5)
self.sizer.Add(self.m_btnAdd2, pos=(3, 8), span=(1, 1), flag=wx.ALL, border=5)
self.sbox4 = wx.StaticText(self.panel, wx.ID_ANY, u'需要打包pyd的py文件:')
self.sizer.Add(self.sbox4, pos=(4, 0), span=(1, 1), flag=wx.ALL, border=5)
self.m_listBox = wx.ListBox(self.panel, wx.ID_ANY, style=wx.LB_EXTENDED)
self.sizer.Add(self.m_listBox, pos=(5, 0), span=(0, 9), flag=wx.EXPAND | wx.ALL, border=5)
self.m_btnImport = wx.Button(self.panel, label=u"添 加")
self.m_btnDelete = wx.Button(self.panel, label=u"删 除")
self.m_btnPack = wx.Button(self.panel, label=u"打 包")
self.sizer.Add(self.m_btnImport, pos=(6, 6), flag=wx.ALL, border=5)
self.sizer.Add(self.m_btnDelete, pos=(6, 7), flag=wx.ALL, border=5)
self.sizer.Add(self.m_btnPack, pos=(6, 8), flag=wx.ALL, border=5)
self.sizer.AddGrowableRow(5)
self.sizer.AddGrowableCol(2)
# 状态栏
self.CreateStatusBar()
if len(self.__comList) > 0:
self.getSysInfo(self.__comList[0])
self.combox.SetValue(self.__comList[0])
self.SetStatusText(self.sysName + " python:" + self.pyVersion + "_" + self.pyBit)
else:
self.SetStatusText("Welcome...")
self.panel.SetBackgroundColour(wx.Colour(240, 255, 255))
self.panel.SetSizerAndFit(self.sizer)
self.Centre()
self.Raise()
def BindEvent(self):
"""
绑定事件
"""
self.Bind(wx.EVT_BUTTON, self.onAdd1, self.m_btnAdd1)
self.Bind(wx.EVT_BUTTON, self.onAdd2, self.m_btnAdd2)
self.Bind(wx.EVT_BUTTON, self.onImport, self.m_btnImport)
self.Bind(wx.EVT_BUTTON, self.onDelete, self.m_btnDelete)
self.Bind(wx.EVT_BUTTON, self.onPack, self.m_btnPack)
self.Bind(wx.EVT_LISTBOX_DCLICK, self.onListboxDoubleClick, self.m_listBox)
self.Bind(wx.EVT_COMBOBOX, self.comboxSelcet, self.combox)
def GetDefaultPath(self):
"""
获取默认的python路径,目前只使用与windows
"""
path = os.environ['path']
lst = path.split(';')
for dir in lst:
try:
files = os.listdir(dir)
for file in files:
if file == "python.exe":
self.__comList.append(dir)
except:
pass
def getSysInfo(self, pythonPath):
"""
指定python路径时收集系统信息
"""
cmd = pythonPath + "\python.exe -c "
cmd += "\"import platform; print(platform.architecture())\""
p = subprocess.run(cmd, stdin=subprocess.PIPE, startupinfo=startupinfo,
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
rtn = p.stdout.decode()
if '32' in rtn:
self.pyBit = "32"
elif "64" in rtn:
self.pyBit = "64"
else:
self.pyBit = ""
cmd = pythonPath + "\python.exe -c "
cmd += "\"import platform; print(platform.system())\""
p = subprocess.run(cmd, stdin=subprocess.PIPE, startupinfo=startupinfo,
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
rtn = p.stdout.decode()
if "Windows" in rtn:
self.sysName = "Windows"
elif "Linux" in rtn:
self.sysName = "Linux"
else:
self.sysName = ""
cmd = pythonPath + "\python.exe -c "
cmd += "\"import platform; print(platform.python_version())\""
p = subprocess.run(cmd, stdin=subprocess.PIPE, startupinfo=startupinfo,
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
rtn = p.stdout.decode()
if rtn is not None:
self.pyVersion = rtn[:3]
else:
self.pyVersion = ""
def chkCython(self, path):
'''
检查是否安装了cython
'''
path += "\Scripts"
files = os.listdir(path)
for file in files:
if file == "cython.exe":
return True
return False
def addComboxList(self, path):
'''
增加combox的list选项
'''
for str in self.__comList:
if str == path:
self.combox.SetValue(str)
return
self.__comList.append(path)
self.combox.Append(path)
self.combox.SetValue(path)
def comboxSelcet(self, event):
"""
combox的选择事件
"""
path = self.combox.GetStringSelection()
self.getSysInfo(path)
self.SetStatusText(self.sysName + " python:" + self.pyVersion + "_" + self.pyBit)
def onAdd1(self, event):
"""
添加python.exe路径
"""
dlg = wx.DirDialog(self, u"选择文件夹", style=wx.DD_DEFAULT_STYLE)
if dlg.ShowModal() == wx.ID_OK:
pythonPath = dlg.GetPath()
if pythonPath is not None:
if self.checkPythonPath(pythonPath):
self.addComboxList(pythonPath)
else:
self.SetStatusText(u"添加python.exe目录错误,请重新选择...")
dlg.Destroy()
return
dlg.Destroy()
self.getSysInfo(pythonPath)
self.SetStatusText(self.sysName + " python:" + self.pyVersion + "_" + self.pyBit)
def checkPythonPath(self, pythonPath):
"""
检查添加的python路径是否正确
"""
for fp in os.listdir(pythonPath):
if fp == "python.exe":
return True
return False
def onAdd2(self, event):
"""
添加打包exe的py文件
"""
file_wildcard = "python files(*.py)|*.py"
dlg = wx.FileDialog(self, "", os.getcwd(), wildcard=file_wildcard)
if dlg.ShowModal() == wx.ID_OK:
filename = dlg.GetPath()
if filename is not None:
self.m_txtCtrl2.SetValue(filename)
self.SetStatusText(u"添加打包exe的python文件")
dlg.Destroy()
def onImport(self, event):
"""
导入需要打包pyd的py文件
"""
file_wildcard = "python files(*.py)|*.py"
dlg = wx.FileDialog(self, "", os.getcwd(), wildcard=file_wildcard, style=wx.FD_MULTIPLE)
if dlg.ShowModal() == wx.ID_OK:
filename = dlg.GetPaths()
num = self.m_listBox.GetCount()
tmp = filename.copy()
for name in tmp:
if self.m_listBox.FindString(name) != wx.NOT_FOUND:
filename.remove(name)
if len(filename):
self.m_listBox.InsertItems(filename, num)
self.SetStatusText(u"添加打包pyd的python文件")
dlg.Destroy()
def onListboxDoubleClick(self, event):
"""
双击Listbox元素删除
"""
listItems = self.m_listBox.GetSelections()
self.m_listBox.Delete(listItems[0])
self.SetStatusText(u"删除打包pyd的python文件")
def onDelete(self, event):
"""
删除listbox中选中的item
"""
allItem = self.m_listBox.GetItems()
listItems = self.m_listBox.GetSelections()
for i in listItems:
allItem.remove(self.m_listBox.GetString(i))
if len(listItems):
self.m_listBox.Clear()
self.m_listBox.InsertItems(allItem, 0)
self.SetStatusText(u"删除打包pyd的python文件")
def getLibName(self, path):
"""
得到python的库名(如 python36)
"""
path += '\libs'
for fp in os.listdir(path):
rtn = re.match(r'python...lib', fp)
if rtn is not None:
return fp[:len(fp) - 4]
def modifyWmain(self, filePath):
"""
修改*.c文件中的wmain-->main
32位gcc并没有 -municode 选项,不能识别 wmain
"""
fopen = open(filePath, "r")
str = ""
for line in fopen:
if re.search("wmain\(int", line):
line = re.sub("wmain", "main", line)
str += line
else:
str += line
wopen = open(filePath, "w")
wopen.write(str)
fopen.close()
wopen.close()
def onPack(self, event):
"""
打包
"""
self.SetStatusText(u"开始打包...")
pythonPath = self.combox.GetStringSelection()
allItem = self.m_listBox.GetItems()
if pythonPath == "":
self.SetStatusText(u"未添加python执行路径")
return
elif len(allItem):
if not self.packPydFiles(pythonPath):
self.SetStatusText(u"Pyd打包完成...")
elif self.m_txtCtrl2.GetLineText(0):
if not self.packEXE(pythonPath):
if not self.copyDLL():
self.SetStatusText(u"EXE打包完成...")
else:
self.SetStatusText(u"未选择文件...")
def packEXE(self, pythonPath):
"""
打包exe
"""
packFile = self.m_txtCtrl2.GetLineText(0)
if packFile == "":
self.SetStatusText(u"未添加打包exe的python文件,跳过exe打包...")
return False
mingw = ""
if self.sysName == "Windows":
mingw = "mingw"
else:
pass # linux暂时没有实现
strPath = self.combox.GetStringSelection()
if not self.chkCython(strPath):
self.SetStatusText(u"没有安装cython,请安装...")
return False
# 生成*.c文件
pos1 = packFile.rfind('\\')
pos2 = packFile.rfind(".")
filePath = packFile[:pos1]
str = packFile[pos1 + 1:pos2]
str_c = str + ".c"
str_exe = str + ".exe"
cmd = 'cython {} --embed '.format(packFile)
p = subprocess.run(cmd, stdin=subprocess.PIPE,startupinfo=startupinfo,
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
rtn = p.returncode
if rtn == 0:
self.SetStatusText(u"成功生成.c文件:" + str_c)
else:
self.SetStatusText(u"未能生成.c文件:" + str_c)
return False
# 生成exe文件
curPath = os.getcwd()
os.chdir(filePath)
if self.pyBit == "64":
cmd = 'gcc {} -static -mwindows -o {} -municode -DMS_WIN64 -I "{}\include" -L "{}\libs" -l {}'.format(
str_c, str, pythonPath,
pythonPath,
self.getLibName(
pythonPath))
elif self.pyBit == "32":
if self.pyVersion[0] == "3":
self.modifyWmain(filePath + "\\" + str_c) # 修改wmain
cmd = 'gcc {} -static -mwindows -m32 -o {} -I "{}\include" -L "{}\libs" -l {}'.format(str_c, str,
pythonPath,
pythonPath,
self.getLibName(
pythonPath))
rtn2 = subprocess.run(cmd, stdin=subprocess.PIPE,startupinfo=startupinfo,
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
rtn2 = rtn2.returncode
res = subprocess.run("cmd /C del {}\{}".format(curPath, str_c), stdin=subprocess.PIPE,startupinfo=startupinfo,
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
if rtn2 == 0:
self.SetStatusText(u"成功生成EXE文件:" + str_exe)
else:
self.SetStatusText(u"未能生成EXE文件:" + str_exe)
os.chdir(curPath)
return False
os.chdir(curPath)
return True
def packPydFiles(self, pythonPath):
"""
打包pyd文件
"""
curPath = os.getcwd()
nItem = self.m_listBox.GetCount()
for i in range(nItem):
str = self.m_listBox.GetString(i)
pos1 = str.rfind('\\')
pos2 = str.rfind(".")
filePath = str[:pos1]
fileName = str[pos1 + 1:pos2]
# 生成*.c文件
cmd = 'cython {0}'.format(str)
rtn2 = subprocess.run(cmd, stdin=subprocess.PIPE,startupinfo=startupinfo,
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
rtn = rtn2.returncode
str_c = fileName + ".c"
if rtn == 0:
self.SetStatusText(u"成功生成.c文件:" + str_c)
else:
self.SetStatusText(u"未能生成.c文件:" + str_c)
# 生成*.pyd文件
mingw = ""
if self.sysName == "Windows":
mingw = "mingw"
elif self.sysName == "Linux":
pass # linux暂时没有实现
str_pyd = fileName + ".pyd"
os.chdir(filePath)
if self.pyBit == "64":
cmd = 'gcc {} -o {} -shared -DMS_WIN64 -I "{}\include" -L "{}\libs" -l {}'.format(str_c, str_pyd,
pythonPath,
pythonPath,
self.getLibName(
pythonPath))
elif self.pyBit == "32":
cmd = 'gcc {} -m32 -o {} -shared -I "{}\include" -L "{}\libs" -l {}'.format(str_c, str_pyd, pythonPath,
pythonPath,
self.getLibName(
pythonPath))
else:
cmd = ""
rtn2 = subprocess.run(cmd, stdin=subprocess.PIPE,startupinfo=startupinfo,
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
rtn = rtn2.returncode
res = subprocess.run("cmd /C del {}\{}".format(curPath, str_c), stdin=subprocess.PIPE,startupinfo=startupinfo,
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
if rtn == 0:
self.SetStatusText(u"成功生成pyd文件:" + str_pyd)
else:
self.SetStatusText(u"未能生成pyd文件:" + str_pyd)
os.chdir(curPath)
return False
os.chdir(curPath)
return True
def copyDLL(self):
"""
复制DLL到exe生成目录
"""
fileName = self.m_txtCtrl2.GetLineText(0)
pos = fileName.rfind('\\')
filePath = fileName[:pos]
pythonPath = self.combox.GetStringSelection()
dllName = "python" + self.pyVersion[0] + self.pyVersion[2] + ".dll"
flag = False
for fp in os.listdir(pythonPath):
if fp == dllName:
flag = True
break
if not flag:
self.SetStatusText(pythonPath + u"下不存在" + dllName)
return False
pythonPath += "\\" + dllName
shutil.copy(pythonPath, filePath)
return True
if __name__ == "__main__":
startupinfo = subprocess.STARTUPINFO()
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
startupinfo.wShowWindow = subprocess.SW_HIDE
app = wx.App()
frm = MainWindow(None, "程序打包", (300, 200), (700, 500))
frm.Show()
app.MainLoop()
知识点:
1.运行系统命令subprocess会出现黑窗口:
解决办法:
添加如下信息
startupinfo = subprocess.STARTUPINFO()
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
startupinfo.wShowWindow = subprocess.SW_HIDE
subprocess.run(cmd, stdin=subprocess.PIPE,startupinfo=startupinfo,
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
网友评论