Python3.10用PyInstaller/auto-py-to-exe打包报IndexError: tuple index out of range
我尝试将livrable.py脚本转换为exe可执行文件,使用auto-py-to-exe和PyInstaller打包时均出现相同报错IndexError: tuple index out of range。我在.py文件所在目录打开PowerShell执行打包操作,已确认该目录下安装了脚本调用的全部依赖包,也尝试使用PyInstaller开发版本打包,仍出现相同错误。
PowerShell端报错截图:
auto-py-to-exe端报错截图:
对应脚本代码
import numpy as np import cv2 import statistics import matplotlib.pyplot as plt from skimage.morphology import binary_closing, binary_dilation, binary_erosion, binary_opening from skimage.color import rgb2gray import skimage.morphology as morpho from PIL import Image from scipy import ndimage from skimage import data from skimage.morphology import disk from skimage.filters import threshold_otsu, rank from skimage.filters import threshold_multiotsu from skimage.util import img_as_ubyte from numpy import array import matplotlib import matplotlib.pyplot as plt import tkinter as tk from tkinter import messagebox from tkinter.filedialog import * from skimage import morphology from skimage.segmentation import watershed from skimage.feature import peak_local_max from skimage import filters def comptage(n,f,i) : if n < 10 : n = "000" + str(n) else : if n >=10 : if n < 100 : n = "00" +str(n) else : n = "0" + str(n) folder = f imname = i + n im = Image.open(folder + '\\' + imname + "_TRANS.tif") imDAPI = Image.open(folder + '\\' + imname + "_DAPI.tif") imGFP = Image.open(folder + '\\' + imname + "_GFP.tif") imtrans = array(im) imdapi = array(imDAPI) imgfp = array(imGFP) sobel = filters.sobel(imtrans) sobel1 = sobel>0.05 sobel1 = binary_dilation(sobel1) imtrans1 = sobel1 * imtrans thresh = cv2.threshold(imtrans1, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)[1] cnts = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) cnts = cnts[0] if len(cnts) == 2 else cnts[1] cv2.fillPoly(imtrans1, cnts, [255,255,255]) kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (4,4)) mask = cv2.morphologyEx(imtrans1, cv2.MORPH_OPEN, kernel, iterations=5) imdapi1 = filters.sobel(imdapi) imdapi2 = imdapi1 >0.1 imdapi3 = binary_opening(imdapi2) imdapi4 = imdapi * binary_closing(imdapi3) thresh1 = cv2.threshold(imdapi4, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)[1] cnts1 = cv2.findContours(thresh1, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) cnts1 = cnts1[0] if len(cnts1) == 2 else cnts1[1] cv2.fillPoly(imdapi4, cnts1, [255,255,255]) kernel1 = cv2.getStructuringElement(cv2.MORPH_RECT, (4,4)) imdapi4 = cv2.morphologyEx(imdapi4, cv2.MORPH_OPEN, kernel, iterations=1) imdapi4 = cv2.bitwise_and(imdapi4, mask) imgfp1 = cv2.threshold(imgfp, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)[1] imgfp2 = cv2.bitwise_and(imgfp1, mask) imdapigfp = imdapi4 * imgfp2 CoefBeads = 1.2083459023 stdrd = 0.0059145257 NbBeads = np.mean(mask)/CoefBeads NbLiveCells = np.mean(imdapi4)/stdrd NbDeadCells = ndimage.label(imdapigfp)[1] NbTotalCells = NbLiveCells +NbDeadCells resimage = [imname , NbBeads , NbLiveCells , NbDeadCells , NbTotalCells] return resimage #f = r"C:\Users\sleve\EVerZom\EVerZom EVZ - Documents\A_R&D interne\Axe 2_Production d'EVs\2021\20211102-BioBLu10c1bis-hASC\09 11 2021 Production 20211102-BioBLu10c1bis-hASC\t4" # Récupère le root du fichier contenant les images gui = tk.Tk() gui.title('fichier') gui.geometry("300x100") def getEntry(): global f f = myEntry.get() gui.destroy() myEntry = tk.Entry(gui, width=40) myEntry.pack(pady=20) btn = tk.Button(gui, height=1, width=30, text="Chemin d'accès au fichier", command=getEntry) btn.pack() gui.mainloop() #i = "13" # Récupère le nom de l'image en le demandant dans une fenêtre à l'utilisateur gui = tk.Tk() gui.title('image') gui.geometry("300x100") def getEntry(): global i i = myEntry.get() gui.destroy() myEntry = tk.Entry(gui, width=40) myEntry.pack(pady=20) btn = tk.Button(gui, height=1, width=20, text="Nom de l'image", command=getEntry) btn.pack() gui.mainloop() #debut = 1 #Récupère le rang de la première photo gui = tk.Tk() gui.title('Rang de la première photo') gui.geometry("300x100") def getEntry(): global debut debut = myEntry.get() gui.destroy() myEntry = tk.Entry(gui, width=40) myEntry.pack(pady=20) btn = tk.Button(gui, height=1, width=20, text="Num photo début", command=getEntry) btn.pack() gui.mainloop() #fin = 13 #Récupère le rang de la dernière photo gui = tk.Tk() gui.title('Rang de la dernière photo') gui.geometry("300x100") def getEntry(): global fin fin = myEntry.get() gui.destroy() myEntry = tk.Entry(gui, width=40) myEntry.pack(pady=20) btn = tk.Button(gui, height=1, width=20, text="Num photo fin", command=getEntry) btn.pack() gui.mainloop() #Récupère le nom que l'utilisateur veut donner au fichier résultat gui = tk.Tk() gui.title('Nom à donner au fichier résultat') gui.geometry("300x100") def getEntry(): global nomfichier nomfichier = myEntry.get() gui.destroy() myEntry = tk.Entry(gui, width=40) myEntry.pack(pady=20) btn = tk.Button(gui, height=1, width=20, text="Nom fichier résultat", command=getEntry) btn.pack() gui.mainloop() debut = int(debut) fin = int(fin) resultat = [] for n in range(debut , fin+1) : resultat.append(comptage(n,f,i)) resultat = array(resultat) print(resultat) np.savetxt(nomfichier + '.csv', resultat, fmt='%s')
该脚本在本地Python环境中可正常运行。
版本适配调整
该报错绝大多数由Python和PyInstaller版本不兼容导致,优先卸载现有PyInstaller,安装适配性稳定的版本:pip uninstall pyinstaller -y pip install pyinstaller==5.13.2若你使用的是Python 3.12版本,建议降级到Python 3.9/3.10,这两个版本和常用的科学计算库、PyInstaller的兼容性最优。
补充隐藏导入参数
你用到的skimage、scipy、matplotlib等库存在大量PyInstaller无法自动识别的子依赖,打包时需要手动指定隐藏导入:
直接用PyInstaller命令打包参考:pyinstaller --onefile --windowed --hidden-import="skimage" --hidden-import="skimage.filters.rank" --hidden-import="scipy" --hidden-import="cv2" --hidden-import="matplotlib.backends.backend_tkagg" --hidden-import="PIL" livrable.py用auto-py-to-exe的话,在「高级」-「隐藏导入」栏依次添加上述包名即可。
修正路径拼接逻辑
原代码中硬编码的反斜杠路径在打包后容易出现路径识别错误,建议导入os模块,用os.path.join拼接路径,示例如下:
把folder + '\\' + imname + "_TRANS.tif"改为os.path.join(folder, f"{imname}_TRANS.tif")
按上述步骤操作即可正常打包,如果仍有报错,可根据打包日志中提示的缺失模块,补充对应的隐藏导入参数即可。
内容的提问来源于stack exchange,提问作者stanislas loiseau

