python 中的程序在使用 pyinstaller 打包后无法运行

Program in python doesn't work after packaging with pyinstaller

我的程序(.py) 在平台vscode 上运行完美。但是用pyinstaller打包成.exe文件后,好像不行。错误消息是“ModuleNotFoundError:没有名为 plotly.validators.scatter 的模块” 这是我的代码“气泡图”:

# Bubble Diagram Version3.0


import tkinter as tk
import tkinter.filedialog as fd
import plotly as py
import plotly.graph_objs as go
import openpyxl
import pandas as pd
import os


class App(tk.Tk):

    def __init__(self):
        super().__init__()
        self.path1 = fd.StringVar()
        self.path2 = fd.StringVar()
        self.name_input = fd.StringVar()
        group_1 = tk.LabelFrame(self, padx=15, pady=10,
                                text="Input and Output Settings")
        group_1.pack(padx=10, pady=5)
        tk.Label(group_1, text='Step1').grid(row=0, column=0)
        tk.Button(group_1, text="Import data from", bg='green',
                  width=20, command=self.choose_file).grid(row=0, column=1)
        tk.Label(group_1, textvariable=self.path1, width=40, bg='grey', fg='white').grid(row=0, column=2, pady=5)
        tk.Label(group_1, text='Step2').grid(row=1, column=0)
        tk.Button(group_1, text="Set output path", bg='orange',
                  width=20, command=self.choose_directory).grid(row=1, column=1)
        tk.Label(group_1, textvariable=self.path2, width=40, bg='grey', fg='white').grid(row=1, column=2, pady=5)
        tk.Label(group_1, text='Step3').grid(row=2, column=0)
        tk.Label(group_1, text='Input name WITHOUT suffix', bg='SteelBlue', width=20).grid(row=2, column=1)
        tk.Entry(group_1, textvariable=self.name_input, bg='grey', width=40).grid(row=2, column=2)

        group_2 = tk.LabelFrame(self, padx=15, pady=10, text="Implementation")
        group_2.pack(padx=10, pady=5)
        tk.Label(group_2, text='Step4').grid(row=0, column=0)
        tk.Button(group_2, text="Start to plot", bg='red',
                  width=10, command=self.start).grid(row=0, column=1)

    def choose_file(self):
        filetypes = (("Excel files", "*.xlsx"),
                     )
        self.filename = fd.askopenfilename(title="Open file",
                                           initialdir="/", filetypes=filetypes)
        self.path1.set(self.filename)

    def choose_directory(self):
        self.directory = fd.askdirectory(title="Open directory",
                                         initialdir="/")
        self.path2.set(self.directory)

    def start(self):
        self.draw(self.filename, self.directory)

    def draw(self, input_file, output_dir):
        self.input_file = input_file
        self.output_dir = output_dir
        wb = openpyxl.load_workbook(self.input_file)
        sheet = wb['Sheet1']
        row_max = sheet.max_row
        col_max = sheet.max_column
        first_row_list = []
        first_col_list = []
        for col_n in range(2, col_max + 1):
            first_row_list.append(sheet.cell(row=1, column=col_n).value)
        for row_n in range(2, row_max + 1):
            first_col_list.append(sheet.cell(row=row_n, column=1).value)

        data_all = pd.read_excel(self.input_file)
        data_selected = data_all.loc[:, first_row_list]

        df = pd.DataFrame(data_selected)
        df.index = first_col_list
        colors = ['rgb(150,204,90)', 'rgb(255, 130, 71)', 'rgb(255, 193, 37)', 'rgb(180,240,190)', 'rgb(255, 10, 1)',
                  'rgb(25, 190, 30)', 'rgb(100, 100, 100)', 'rgb(45,24,200)', 'rgb(33, 58, 108)', 'rgb(35, 208, 232)']

        data = [go.Scatter(
            x=df.columns,
            y=[country] * len(df.columns),
            mode='markers+text',
            marker=dict(
                color=colors[num],
                size=df.loc[country],
                showscale=False,
            ),
            text=list(map(str, df.loc[country])),
            textposition='middle center',
        )
            for num, country in enumerate(reversed(df.index))
        ]

        layout = go.Layout(plot_bgcolor='rgb(10, 10, 10)',
                           paper_bgcolor='rgb(20, 55, 100)',
                           font={
                               'size': 15,
                               'family': 'sans-serif',
                               'color': 'rgb(255, 255, 255)'
                           },
                           width=1000,
                           height=800,
                           xaxis=dict(
                               title='Output of grapes per year in different countries',
                               nticks=col_max + 1,
                               type='category',
                           ),
                           showlegend=False,
                           margin=dict(l=100, r=100, t=100, b=100),
                           hovermode=False,
                           )

        fig = go.Figure(data=data, layout=layout)
        self.name = self.name_input.get() + '.html'
        py.offline.plot(fig, filename=os.path.join(self.output_dir, self.name))


if __name__ == "__main__":
    app = App()
    app.title("Bubble Diagram")
    app.mainloop()

这是示例数据文件 (.xlsx):

       1991  1992  1993  1994  1995  1996  1997
US       10    14    16    18    20    42    64
JAPAN   100    30    70    85    30    42    64
CN       50    22    30    65    70    66    60

我应该怎么做才能解决这个问题?谢谢

所以你还在为这个苦恼吗?

您需要更新规范文件的“数据”和“隐藏导入”部分,以确保导入库。我在下面向您展示了我的(它工作得很好)。您需要修改它以包含 openpyxl、tkinter 和 pandas.

a = Analysis(['main.py'],
             pathex=['C:\Users\user\PycharmProjects\PlotlyExample'],
             binaries=[],
             datas=[('C:\Users\user\PycharmProjects\PlotlyExample\venv\Lib\site-packages\plotly\', 'plotly'),
             ('C:\Users\user\PycharmProjects\PlotlyExample\venv\Lib\site-packages\kaleido\', 'kaleido'),
             ('C:\Users\user\PycharmProjects\PlotlyExample\venv\Lib\site-packages\pptx\', 'pptx'),],
             hiddenimports=['pandas','numpy','plotly','pptx'],
             hookspath=[],
             runtime_hooks=[],
             excludes=[],
             win_no_prefer_redirects=False,
             win_private_assemblies=False,
             cipher=block_cipher,
             noarchive=False)

关于 plotly.json,我实际上遇到了与您相同的错误,所以我知道上述解决方案有效。 :)

同样重要的是,如果您要导出静态图像,请在规范文件中包含 Kaleido 或 Orcas。我正在使用 Kaleido,因此您可以在我的规范文件设置中看到它。

然后 运行 PyInstaller 通过: pyinstaller main.spec --clean

其中 main.spec 是您的规范文件(更改名称)。

编辑: 为了让事情更简单,这是我的整个规范文件:

# -*- mode: python ; coding: utf-8 -*-
# Command to compile the spec and python files
# pyinstaller main.spec --clean --onefile

block_cipher = None


a = Analysis(['main.py'],
             pathex=['C:\Users\user\PycharmProjects\PlotlyExample'],
             binaries=[],
             datas=[('C:\Users\user\PycharmProjects\PlotlyExample\venv\Lib\site-packages\plotly\', 'plotly'),
             ('C:\Users\user\PycharmProjects\PlotlyExample\venv\Lib\site-packages\kaleido\', 'kaleido'),
             ('C:\Users\user\PycharmProjects\PlotlyExample\venv\Lib\site-packages\pptx\', 'pptx'),],
             hiddenimports=['pandas','numpy','plotly','pptx'],
             hookspath=[],
             runtime_hooks=[],
             excludes=[],
             win_no_prefer_redirects=False,
             win_private_assemblies=False,
             cipher=block_cipher,
             noarchive=False)
pyz = PYZ(a.pure, a.zipped_data,
             cipher=block_cipher)
exe = EXE(pyz,
          a.scripts,
          [],
          exclude_binaries=True,
          name='main',
          debug=False,
          bootloader_ignore_signals=False,
          strip=False,
          upx=True,
          console=True )
coll = COLLECT(exe,
               a.binaries,
               a.zipfiles,
               a.datas,
               strip=False,
               upx=True,
               upx_exclude=[],
               name='main')