【问题标题】:Exe with tkinter GUI generated by Pyinstaller exe does not work由 Pyinstaller exe 生成的带有 tkinter GUI 的 Exe 不起作用
【发布时间】:2019-12-04 17:32:32
【问题描述】:

我使用 Pyinstaller 将我的 Python 脚本变成了一个独立的可执行程序。

当我在 spyder 中测试时,我的脚本可以运行,但作为 exe 却不能,我不明白为什么。

节目详情

我的程序由:

  • 由一系列函数组成的处理。它将两个 csv 文件作为输入并返回一个单行 pandas DataFrame。最后一个函数叫做add_data()

  • 调用 add_data() 的 test() 函数,将其结果保存在 csv 文件中,并在执行结束时通知用户

  • 一个带有 try-except 条件的 execute_test() 函数; try 使用来自 GUI 的用户输入调用 test(),except 显示错误消息框。

  • 一个 tkinter GUI 要求用户选择两个 csv 文件 + 一个 test() 将保存 csv 结果的目录。

行为

在 spyder 上,执行 python 脚本时,我选择文件和目录,它返回“处理完成”消息框,所以问题不应该来自脚本本身。

Pyinstaller 成功构建 exe 文件。我可以打开它,出现 GUI 和命令提示符;条目和按钮工作正常。

选择相同的文件和目录后启动处理时,只返回错误信息框,表示处理由于某种原因没有执行。 我从提示中得到的唯一消息是这条消息:

C:\Users...\Continuum\miniconda3\envs\myenv\lib\site-packages\PyInstaller\loader\pyimod03_importers.py:627: MatplotlibDeprecationWarning:MATPLOTLIBDATA 环境变量 在 Matplotlib 3.1 中已弃用,将在 3.3 中删除。

我没有在我的程序中显式导入ma​​tplotlib,而且我之前也没有在我的环境中安装它。我在这条消息之后做了,但它没有任何改变。

包含我处理的整个代码会太长,但我在其中明确导入的是:pandasgeopandasshapely.geometrytkinter(参见下面的代码)。由于我之前遇到过问题(已解决),因此在使用 geopandas 时似乎也需要 pyproj

Pyinstaller 信息

如果需要,我可以提供将脚本转换为 .exe 时得到的全部提示输出。 值得一提的是我得到了:

  • 一些不包括进口关于PySide、PyQt5、gtk、matplotlib、PyQt4、tkinter

  • 警告71826 WARNING: Hidden import "PyQt5.sip" not found!

  • 并且警告说它没有找到某些 DLL

81853 INFO: Looking for dynamic libraries
81931 WARNING: lib not found: tbb.dll dependency of ...\mkl_tbb_thread.dll
81978 WARNING: lib not found: msmpi.dll dependency of C:\Users\...\bin\mkl_blacs_msmpi_lp64.dll
82509 WARNING: lib not found: pgf90rtl.dll dependency of C:\Users\...\bin\mkl_pgi_thread.dll
82525 WARNING: lib not found: pgc14.dll dependency of C:\Users\...\bin\mkl_pgi_thread.dll
82556 WARNING: lib not found: pgf90.dll dependency of C:\Users\...\bin\mkl_pgi_thread.dll
82587 WARNING: lib not found: msmpi.dll dependency of C:\Users\...\bin\mkl_blacs_msmpi_ilp64.dll
82634 WARNING: lib not found: mpich2mpi.dll dependency of C:\Users\...\bin\mkl_blacs_mpich2_lp64.dll
82712 WARNING: lib not found: mpich2mpi.dll dependency of C:\Users\...\bin\mkl_blacs_mpich2_ilp64.dll
82869 WARNING: lib not found: impi.dll dependency of C:\Users\...\bin\mkl_blacs_intelmpi_lp64.dll
83025 WARNING: lib not found: impi.dll dependency of C:\Users\...\bin\mkl_blacs_intelmpi_ilp64.dll

配置

我使用 Miniconda 并尽量避免使用 pip。我的环境配置是:

  • Windows 10 / conda 4.7.10 / Python 3.7.3 / spyder 3.3.6
  • 熊猫 0.25.0 / geopandas 0.5.1 / pyproj 2.2.1 / tk 8.6.9
  • numpy 1.16.4 / ma​​tplotlib 3.1.1
  • pyinstaller 3.5 / setuptools 41.0.1 / pywin32 224

还有很多我不知道的其他包和模块(虽然不是 anaconda)。

我什至不知道确切的标题是什么,因为我不知道错误来自 哪里(我的猜测是 tkinter、matplotlib 或者可能是 numpy)。

我还想说我是一个初学者程序员,我在处理整个包/模块/导入/依赖项/DLL/兼容性方面的问题。仍在研究它,我大致了解大多数概念,但我可能需要详细解释此级别发生的事情以便调试它。 有人可以帮忙吗?

我的代码

from tkinter import Tk, Frame, Label, Button, Entry,\
filedialog as fd, messagebox

## Functions that will be called by user interaction with the GUI    
def test(file_L, file_T, directory):

    df_test = add_data(file_L, file_T) # calls the previous treatment

    path=str(directory)+'/'+'test_result.csv'
    df_test.to_csv(path, encoding='utf-8', index=False)

    messagebox.showinfo("End", "The treatment is done")

def execute_test(inputL, inputT, inputD):
    try:
        return test(inputL.filedir.get(), 
                inputT.filedir.get(), 
                inputD.selecdir.get())
    except:
        messagebox.showerror("Error", "The program failed to launch.\n"\
                             "Either the inputs are not correct, or an "\
                             "intern error occured.") 

# Class Button + Entry to select a csv file
class Selection:
    def __init__(self, master):
        self.filedir = Entry(master, bd=2)
        self.load_button = Button(master, text="...", bg='yellow',
                                  command=self.loadFile)     

    def loadFile(self):
        self.filename = fd.askopenfilename(
                filetypes = (("csv files","*.csv"),("all files","*.*"))
                        ) 

        self.filedir.delete(0,"end")
        self.filedir.insert(0, self.filename)

# Class Button + Entry to select a directory
class Directory:
    def __init__(self, master):
        self.selecdir = Entry(master, bd=2)
        self.load_button = Button(master, text="...", bg='yellow',
                                  command=self.loadDir)

    def loadDir(self):
        self.dirname = fd.askdirectory()
        self.selecdir.delete(0,"end")
        self.selecdir.insert(0, self.dirname)

# GUI itself
if __name__=='__main__': 

    from functools import partial

    #-----Defining the root
    root = Tk()
    root.geometry("+800+400")

    #-----Defining the Frames                   
    f2 = Frame(root)
    f2.grid_columnconfigure(0, weight=2) 
    f2.grid_columnconfigure(1, weight=1)            
    f2.grid_rowconfigure(0, weight=1) 
    f2.grid_rowconfigure(1, weight=1)
    f2.grid_rowconfigure(2, weight=1)              
    f2.grid_rowconfigure(3, weight=1) 
    f2.grid_rowconfigure(4, weight=1) 
    f2.grid_rowconfigure(5, weight=1)      

    f3 = Frame(root)

    #-----Defining the widgets
    TextL = Label(f2, text="Please select file L :")
    L = Selection(f2) 

    TextT = Label(f2, text="Please select file T :")
    T = Selection(f2) 

    TextD = Label(f2, text="Please select the directory in which the result "\
                   "will be saved as a csv :") 
    D = Directory(f2)


    b_validate = Button(f3, text="Execute", bg='cyan',
                        command = partial(execute_test, L, T, D))
    b_exit = Button(f3, text="Exit", bg='red', command = root.destroy)

    #-----Geometry managers    
    f2.pack(expand=True)
    f3.pack(side='right')

    TextL.grid(row=0)
    L.filedir.grid(row=1, column=0, sticky='ew')
    L.load_button.grid(row=1, column=1, sticky='w') 

    TextT.grid(row=2)
    T.filedir.grid(row=3, column=0, sticky='ew')
    T.load_button.grid(row=3, column=1, sticky='w')    

    TextD.grid(row=4)  
    D.selecdir.grid(row=5, column=0, sticky='ew')
    D.load_button.grid(row=5, column=1, sticky='w')

    b_validate.pack(side='left')
    b_exit.pack(side='left')


    root.mainloop()

【问题讨论】:

  • 更新:我做了一个模拟的“add_data()”函数来提供一个功能性的GUI来指出错误;它只是连接两个输入 csv 文件的列并返回连接的数据帧。它有效......所以问题似乎来自“处理”部分和pyinstaller之间的不兼容。我将按功能探索这个主要功能,如果我取得一些进展,我会更新这个问题。
  • 我在你的问题中看不到错误,所有这些都是警告,可以忽略。此外,您的代码不会导入 pandas。使用 CMD 运行您的可执行文件并放置完整的错误回溯。
  • 好的,关于警告。当我需要它时,我在函数中导入了熊猫。我通过 CMD 运行它,但没有得到错误回溯;我得到的只是我最初发布的相同 Matplotlib 警告。错误消息只是我编程的 showerror 消息框,所以它“只是”我的程序没有运行(我现在正在逐个函数调查)

标签: python tkinter exe pyinstaller


【解决方案1】:

关注@M.R.的建议,回答这个问题的有用陈述是:

  • 可以忽略警告
  • 由于我在命令提示符中没有错误回溯,似乎阻止的不是我发布的代码,而是 之前

我逐个检查了我的治疗功能的细节。似乎一个功能(至少)使整个事情不起作用。

它是一个调用 geopandas 和 shapely 的,并且之前遇到过关于 pyproj 的错误;如果我自己没有成功调试它,我可能会专门针对这个问题提出一个新问题(这与我在这里提出的问题相同:现在我的 python 脚本可以工作,我不明白为什么作为 exe突然就没有了)。在这种情况下,我将在此处链接下一个问题。

如果它可以帮助其他绝望的程序员,那么在调试过程中对我也有帮助的是:

  • 事实上我的函数已经被分离了(这使得生成单独的 exe 更容易)

  • 有一个最小的 GUI 来将每个函数都变成一个 exe 是很有用的。如果有人遇到暗示用户输入的类似问题,请随意使用我发布的 GUI 代码作为布局,如果它可以节省您的时间。您只需在开头添加您的函数,然后更改您在 test() 部分中调用的内容(我在其中调用了 add_data()

  • 我也听从了How to debug small programs 的建议。这需要时间,但这是值得的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-10-27
    • 2019-04-06
    • 1970-01-01
    • 2020-11-17
    • 2013-08-22
    • 2020-12-17
    相关资源
    最近更新 更多