用py2exe打包PyQt5+Matplotlib出错
打包Python3.4+PyQt5+Matplotlib程序,使用的py2exe打包,打包生成exe文件,运行出错,生成mymain1.log文件。错误信息
程序代码:
Traceback (most recent call last): File "mymain1.py", line 9, in <module> File "<frozen importlib._bootstrap>", line 2237, in _find_and_load File "<frozen importlib._bootstrap>", line 2226, in _find_and_load_unlocked File "<frozen importlib._bootstrap>", line 1191, in _load_unlocked File "<frozen importlib._bootstrap>", line 1161, in _load_backward_compatible File "C:\Python34\lib\site-packages\zipextimporter.py", line 86, in load_module return zipimport.zipimporter.load_module(self, fullname) File "C:\Python34\lib\site-packages\matplotlib\backends\backend_qt5agg.py", line 30, in <module> _decref = ctypes.pythonapi.Py_DecRef File "C:\Python34\lib\ctypes\__init__.py", line 364, in __getattr__ func = self.__getitem__(name) File "C:\Python34\lib\ctypes\__init__.py", line 369, in __getitem__ func = self._FuncPtr((name_or_ordinal, self)) AttributeError: function 'Py_DecRef' not found
打包setup.py文件
代码如下
程序代码:
from distutils.core import setup import py2exe import sys # this allows to run it with a simple double click. sys.argv.append('py2exe') py2exe_options = { "includes": ["sip"], # 如果打包文件中有PyQt代码,则这句为必须添加的 "dll_excludes": ["MSVCP90.dll", ], # 这句必须有,不然打包后的程序运行时会报找不到MSVCP90.dll,如果打包过程中找不到这个文件,请安装相应的库 "compressed": 1, "optimize": 2, "ascii": 0, "bundle_files": 1, # 关于这个参数请看第三部分中的问题(2) } setup( name='PyQt Demo', version='1.0', windows=['mymain1.py', ], # 括号中更改为你要打包的代码文件名 data_files=[("", [r"C:\Python34\Lib\site-packages\PyQt5\libEGL.dll"]), ("platforms", [r"C:\Python34\Lib\site-packages\PyQt5\plugins\platforms\qwindows.dll"])], zipfile=None, options={'py2exe': py2exe_options} )
用这个文件打包只有Python3.4+PyQt5的程序,没有问题
看错误信息 在ctypes库里没找到Py_DecRef这个函数
以下是要打包的代码
程序代码:
# -*- coding: utf-8 -*- import sys import time import minimalmodbus from PyQt5 import QtCore, QtWidgets, QtGui import matplotlib matplotlib.use("Qt5Agg") from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas from matplotlib.figure import Figure from numpy import arange, sin, pi class MyMplCanvas(FigureCanvas): """这是一个窗口部件,即QWidget(当然也是FigureCanvasAgg)""" def __init__(self, parent=None, width=5, height=4, dpi=100): fig = Figure(figsize=(width, height), dpi=dpi) self.axes1 = fig.add_axes([0.15, 0.55, 0.8, 0.4]) self.axes2 = fig.add_axes([0.15, 0.1, 0.8, 0.4]) # 每次plot()调用的时候,我们希望原来的坐标轴被清除(所以False) self.axes1.hold(False) self.axes2.hold(False) () # #super(MyMplCanvas, self).__init__(self, fig) FigureCanvas.__init__(self, fig) self.setParent(parent) FigureCanvas.setSizePolicy(self, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) FigureCanvas.updateGeometry(self) def compute_initial_figure(self): pass class MyStaticMplCanvas(MyMplCanvas): """静态画布:一条正弦线""" def compute_initial_figure(self): t = arange(0.0, 3.0, 0.01) s = sin(2*pi*t) self.axes1.plot(t, s) class MyDynamicMplCanvas(MyMplCanvas): """动态画布:每秒自动更新,更换一条折线。""" x = [] y1 = [] y2 = [] def __init__(self, *args, **kwargs): MyMplCanvas.__init__(self, *args, **kwargs) timer = QtCore.QTimer(self) timer.timeout.connect(self.update_figure) timer.start(1000) def compute_initial_figure(self): if len(self.x) == len(self.y1) and len(self.x) == len(self.y2): self.axes1.plot(self.x, self.y1, color="red", linewidth=2) self.axes2.plot(self.x, self.y2, "b--", label=r"瞬时流量") self.axes2.set_xlabel("time") self.axes1.set_ylabel("Volt") self.axes1.set_title("PyPlot First Example") self.axes1.set_xticks([]) #self.axes1.set_ylim(-1.2, 1.2) #self.axes1.legend() def update_figure(self): if len(self.x) == len(self.y1) and len(self.x) == len(self.y2): self.axes1.plot(self.x,self.y1,label=r"Instantaneous flow rate",color="red",linewidth=2) self.axes2.plot(self.x,self.y2, "b--", label=r"Cumulative flow") self.axes2.set_xlabel("time") self.axes1.set_ylabel("Instantaneous flow rate") self.axes2.set_ylabel("Cumulative flow") self.axes1.set_title("Record of Flow rate") self.axes1.set_xticks([]) #self.axes1.set_ylim(-1.2, 1.2) #self.axes1.legend() self.draw() class Myalert(QtWidgets.QWidget): def __init__(self): super(Myalert,self).__init__() class errorDialog(QtWidgets.QDialog): def __init__(self): super(errorDialog, self).__init__() self.resize(451, 250) self.setSizeGripEnabled(True) self.setModal(True) icon = QtGui.QIcon() icon.addPixmap(QtGui.QPixmap("cross-octagon.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.setWindowIcon(icon) #以下三句设置网格放置Widget 位于底部 self.layoutWidget = QtWidgets.QWidget(self) self.layoutWidget.setGeometry(QtCore.QRect(20, 190, 411, 33)) self.layoutWidget.setObjectName("layoutWidget") #以下四句设置底部水平网格HBoxLayout self.hboxlayout = QtWidgets.QHBoxLayout(self.layoutWidget) self.hboxlayout.setContentsMargins(0, 0, 0, 0) self.hboxlayout.setSpacing(6) self.hboxlayout.setObjectName("hboxlayout") #以下两句设置okButton前的空格 spacerItem = QtWidgets.QSpacerItem(71, 30, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) self.hboxlayout.addItem(spacerItem) #以下三句设置okButton self.okButton = QtWidgets.QPushButton(self.layoutWidget) self.okButton.setObjectName("okButton") self.hboxlayout.addWidget(self.okButton) # 以下三句设置cancelButton self.cancelButton = QtWidgets.QPushButton(self.layoutWidget) self.cancelButton.setObjectName("cancelButton") self.hboxlayout.addWidget(self.cancelButton) # 以下两句设置cancelButton后的空格 spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) self.hboxlayout.addItem(spacerItem1) # 以下三句设置groupBox,用于放置绘制的TEXT self.groupBox = QtWidgets.QGroupBox(self) self.groupBox.setGeometry(QtCore.QRect(20, 10, 410, 170)) self.groupBox.setTitle("") self.groupBox.setObjectName("groupBox") # 以下三句设置groupBox,用于放置绘制的TEXT self.label = QtWidgets.QLabel(self.groupBox) self.setlabel(self.label) self.label.setGeometry(QtCore.QRect(230, 10, 171, 131)) self.label.setObjectName("label") self.label_2 = QtWidgets.QLabel(self.groupBox) self.setlabel(self.label_2) self.label_2.setGeometry(QtCore.QRect(10, 10, 221, 41)) self.label_2.setObjectName("label_2") self.label_3 = QtWidgets.QLabel(self.groupBox) self.label_3.setGeometry(QtCore.QRect(50, 50, 131, 111)) self.label_3.setText("") self.label_3.setPixmap(QtGui.QPixmap("error")) self.label_3.setObjectName("label_3") # 下面一句调用retranslateUi()设置部件的文字 self.retranslateUi(self) # 以下三句设置事件触发 self.okButton.clicked.connect(self.accept) self.cancelButton.clicked.connect(self.reject) QtCore.QMetaObject.connectSlotsByName(self) def setlabel(self,label): sizePolicy = QtWidgets.QSizePolicy( QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) sizePolicy.setHorizontalStretch(100) sizePolicy.setVerticalStretch(100) sizePolicy.setHeightForWidth(label.sizePolicy().hasHeightForWidth()) font = QtGui.QFont() font.setFamily("宋体") font.setPointSize(14) font.setBold(True) font.setWeight(75) label.setFont(font) pe = QtGui.QPalette() pe.setColor(QtGui.QPalette.WindowText, QtCore.Qt.red) label.setPalette(pe) def retranslateUi(self, Dialog): _translate = QtCore.QCoreApplication.translate self.setWindowTitle(_translate("Dialog", "ERROR!")) self.okButton.setText(_translate("Dialog", "&OK")) self.cancelButton.setText(_translate("Dialog", "&Cancel")) self.groupBox.setTitle(_translate("Dialog", "")) self.text = '串口设置错误!' self.label.setText(_translate("Dialog", "<html><head/><body><p><span style=\" font-size:11pt;\">" "1、串口是否连接正确</span></p><p><span style=\" " "font-size:11pt;\">" "2、串口号设置是否正确</span></p><p><span style=\" " "font-size:11pt;\">" "3、波特率是否设置正确</span></p><p><span style=\" " "font-size:11pt;\">" "4、设备地址是否正确</span></p></body></html>")) self.label_2.setText(_translate("Dialog", "串口连接错误!请检查:")) class ApplicationWindow(QtWidgets.QMainWindow): def __init__(self): QtWidgets.QMainWindow.__init__(self) self.setAttribute(QtCore.Qt.WA_DeleteOnClose) self.setWindowTitle("程序主窗口") self.resize(650, 700) self.mysetwidgets() def mysetfont(self,fontnum): if fontnum == 1: font = QtGui.QFont("微软雅黑 Light",14) font.setFamily("微软雅黑 Light") font.setPointSize(14) font.setBold(True) font.setWeight(75) elif fontnum == 2: font = QtGui.QFont() font.setFamily("微软雅黑 Light") font.setPointSize(12) font.setBold(True) font.setWeight(75) return font def mySetsizePolicy(self,sizenum): if sizenum == 1: sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Preferred) sizePolicy.setHorizontalStretch(80) sizePolicy.setVerticalStretch(0) elif sizenum == 2: sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Preferred) sizePolicy.setHorizontalStretch(50) sizePolicy.setVerticalStretch(0) elif sizenum == 3: sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred) sizePolicy.setHorizontalStretch(100) sizePolicy.setVerticalStretch(100) sizePolicy.setHeightForWidth(self.groupBox.sizePolicy().hasHeightForWidth()) elif sizenum == 4: sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred) sizePolicy.setHorizontalStretch(100) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.groupBox_2.sizePolicy().hasHeightForWidth()) return sizePolicy def mySetlabel(self,label,fontmun,textformat,alignment,name): label.setFont(self.mysetfont(fontmun)) label.setTextFormat(textformat) label.setAlignment(alignment) label.setObjectName(name) def mySetLCD(self,LCD): LCD.setDigitCount(16) LCD.setObjectName("lcd") LCD.setSegmentStyle(QtWidgets.QLCDNumber.Flat) def mysetwidgets(self): '''''' #以下设置主面板 self.main_widget = QtWidgets.QWidget(self) self.main_widget.setSizePolicy(self.mySetsizePolicy(1)) self.main_widget.setGeometry(QtCore.QRect(10, 10, 600, 600)) self.main_widget.setObjectName("main_widget") #以下设置网格 self.gridLayout = QtWidgets.QGridLayout(self.main_widget) self.gridLayout.setObjectName("gridLayout") #以下设置标签 self.mylabel = [QtWidgets.QLabel('温度℃',self.main_widget), QtWidgets.QLabel('压力KPa', self.main_widget), QtWidgets.QLabel('瞬时流量kg/min', self.main_widget), QtWidgets.QLabel('累计流量kg', self.main_widget), QtWidgets.QLabel('第一路', self.main_widget), QtWidgets.QLabel('第二路', self.main_widget)] for i in range(6): self.mySetlabel(self.mylabel[i],1, QtCore.Qt.RichText,QtCore.Qt.AlignCenter, "mylabel_1") if i==4: self.mylabel[i].setSizePolicy(self.mySetsizePolicy(2)) self.gridLayout.addWidget(self.mylabel[i], 1, 0, 1, 1) elif i ==5: self.mylabel[i].setSizePolicy(self.mySetsizePolicy(2)) self.gridLayout.addWidget(self.mylabel[i], 2, 0, 1, 1) else: self.gridLayout.addWidget(self.mylabel[i], 0, i+1, 1, 1) #以下设置LCD #创建myLCD数组对应关系如下: # myLCD[0] ---------第一路温度单位℃ # myLCD[1] ---------第一路压力单位KPa # myLCD[2] ---------第一路瞬时流量单位kg/min # myLCD[3] ---------第一路累计流量单位kg # myLCD[4] ---------第二路温度单位℃ # myLCD[5] ---------第二路压力单位KPa # myLCD[6] ---------第二路瞬时流量单位kg/min # myLCD[7] ---------第二路累计流量单位kg self.myLCD = [QtWidgets.QLCDNumber(self.main_widget), QtWidgets.QLCDNumber(self.main_widget), QtWidgets.QLCDNumber(self.main_widget), QtWidgets.QLCDNumber(self.main_widget), QtWidgets.QLCDNumber(self.main_widget), QtWidgets.QLCDNumber(self.main_widget), QtWidgets.QLCDNumber(self.main_widget), QtWidgets.QLCDNumber(self.main_widget),] for i in range(8): self.mySetLCD(self.myLCD[i]) if i<=3: self.gridLayout.addWidget(self.myLCD[i], 1, i + 1, 1, 1) else: self.gridLayout.addWidget(self.myLCD[i], 2, i - 3, 1, 1) #添加动态图 _translate = QtCore.QCoreApplication.translate self.groupBox = QtWidgets.QGroupBox(self.main_widget) self.groupBox.setSizePolicy(self.mySetsizePolicy(3)) self.groupBox.setSizeIncrement(QtCore.QSize(35, 56)) self.groupBox.setBaseSize(QtCore.QSize(430, 500)) self.groupBox.setFont(self.mysetfont(2)) self.groupBox.setTitle(_translate("Dialog", "流量记录")) self.groupBox.setObjectName("groupBox") self.gridLayout.addWidget(self.groupBox, 3, 0, 1, 5) self.myFigure = MyDynamicMplCanvas(self.groupBox, width=5, height=3, dpi=100) self.myFigure.setGeometry(QtCore.QRect(10, 20, 700, 520)) #以下设置报警及按钮 #在hLayoutWidget上创建水平布局,安放报警提示和按钮 self.hLayoutWidget = QtWidgets.QWidget(self) self.hLayoutWidget.setGeometry(QtCore.QRect(10, 650, 700, 41)) self.hLayoutWidget.setObjectName("horizontalLayoutWidget") self.hLayout = QtWidgets.QHBoxLayout(self.hLayoutWidget) self.hLayout.setObjectName("horizontalLayout") self.groupBox_2 = QtWidgets.QGroupBox(self.hLayoutWidget) self.groupBox_2.setSizePolicy(self.mySetsizePolicy(4)) self.groupBox_2.setTitle("") self.groupBox_2.setObjectName("groupBox_2") self.hLayout.addWidget(self.groupBox_2) #添加按钮 spacerItem0 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) self.hLayout.addItem(spacerItem0) self.Btn_start = QtWidgets.QPushButton(self.hLayoutWidget) self.Btn_start.setObjectName("Btn_start") self.hLayout.addWidget(self.Btn_start) spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) self.hLayout.addItem(spacerItem) self.Btn_close = QtWidgets.QPushButton(self.hLayoutWidget) self.Btn_close.setObjectName("Btn_close") self.hLayout.addWidget(self.Btn_close) spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) self.hLayout.addItem(spacerItem1) self.Btn_start.setText(_translate("Dialog", "开始")) self.Btn_close.setText(_translate("Dialog", "结束")) self.main_widget.setFocus() self.setCentralWidget(self.main_widget) class WorkThread(QtCore.QThread): trigger = QtCore.pyqtSignal() ischange = False list_int_n = [] def __int__(self): super(WorkThread, self).__init__() def run(self): while True: #print(self.ischange) if self.ischange: try: self.instrument = minimalmodbus.Instrument('com2', 4) self.instrument.serial.baudrate = 9600 self.list_int_n = self.instrument.read_registers(0, 8, 4) except: self.trigger.emit() self.ischange = False time.sleep(1) #print(self.ischange) print(self.list_int_n) #self.trigger.emit() # 循环完毕后发出信号 class myWindow(ApplicationWindow): def __init__(self): super(myWindow,self).__init__() self.Btn_start.clicked.connect(self.mystart) self.int_n =[] self.x = [] self.y1 = [] self.y2 = [] self.time = time.time() self.timer = QtCore.QTimer() self.recordtimer = QtCore.QTimer() self.workThread = WorkThread() self.workThread1 = WorkThread() self.Btn_start.clicked.connect(self.mystart) self.timer.timeout.connect(self.myrun) self.recordtimer.timeout.connect(self.myrecord) self.ischange = False def mystart(self): self.timer.start(200) self.workThread.start() self.recordtimer.start(1000) self.time = time.time() #self.workThread1.start() #self.workThread.trigger.connect(self.timeStop) self.ischange = True def myrun(self): self.workThread.trigger.connect(self.showErrorDialog) self.workThread.ischange = self.ischange self.int_n = self.workThread.list_int_n #print(self.int_n) if len(self.int_n) >=8: for i in range(8): self.myLCD[i].display('{:.2f}'.format(self.int_n[i])) def showErrorDialog(self): self.workThread.ischange = False self.ischange = False self.timer.stop() self.recordtimer.stop() #time.sleep(1) self.errorwindow = errorDialog() self.errorwindow.show() def myrecord(self): if self.ischange and len(self.int_n)>=8 : if len(self.x)>=101: self.x = self.x[1:] self.x.append(time.time() - self.time) if len(self.y1) >= 101: self.y1 = self.y1[1:] self.y1.append(self.int_n[0]) if len(self.y2) >= 101: self.y2 = self.y2[1:] self.y2.append(self.int_n[1]) self.myFigure.x = self.x self.myFigure.y1 = self.y1 self.myFigure.y2 = self.y2 pass app = QtWidgets.QApplication(sys.argv) myWindow = myWindow() myWindow.show() sys.exit(app.exec_())