【问题标题】:I'm not sure how to execute this code correctly [using PyInstaller] into a .exe file我不确定如何将这段代码正确地[使用 PyInstaller] 执行到 .exe 文件中
【发布时间】:2020-02-16 09:32:52
【问题描述】:

我正在尝试使用 PyInstaller 将我的 .py 文件转换为 .exe 文件。这个过程很好。但是,当我尝试运行 .exe 文件时,程序提到它找不到“client_secret.json”。

我不确定如何在文件包中正确包含 JSON 文件,以便正确执行转换后的 .exe 文件。

我是 PyInstaller 的新手,所以请不要对我评价太苛刻:/

我已尝试将“client_secret.json”添加到 PALSheet.spec 的 .spec 文件中,但 .exe 文件(已更新)无法运行 [也是由于无法找到“client_secret.json”]。

这里是“PALSheet.py”的代码:

import tkinter
import gspread
from oauth2client.service_account import ServiceAccountCredentials


# use creds to create a client to interact with the Google Drive API and Google Sheets API
scope = 'https://spreadsheets.google.com/feeds','https://www.googleapis.com/auth/drive'

creds = ServiceAccountCredentials.from_json_keyfile_name('client_secret.json', scope)
client = gspread.authorize(creds)

# Find a workbook by name and open the first sheet
# Make sure you use the right name here.
sheet = client.open("PAL Tutoring Data SpreadSheet").sheet1

def ResetData():
    student_entry.delete('0', 'end')
    grade_entry.delete('0', 'end')
    class_entry.delete('0', 'end')
    topic_entry.delete('0', 'end')

def FindNextRow():
    global next_row
    next_row = ((len(sheet.col_values(int(1)))) + 1)

def InputData():
    FindNextRow()

    global next_row
    sheet.update_cell(next_row, 1, student_entry.get())
    sheet.update_cell(next_row, 2, grade_entry.get())
    sheet.update_cell(next_row, 3, class_entry.get())
    sheet.update_cell(next_row, 4, topic_entry.get())

    student_entry.delete('0', 'end')
    grade_entry.delete('0', 'end')
    class_entry.delete('0', 'end')
    topic_entry.delete('0', 'end')

window = tkinter.Tk()
window.title("PAL Data Input")

student_label = tkinter.Label(window, text = " Student Name: ")
student_label.grid(row = "2", column = "1")
student_entry = tkinter.Entry(window)
student_entry.grid(row = "2", column = "2")

grade_label = tkinter.Label(window, text = "Grade Level: ")
grade_label.grid(row = "3", column = "1")
grade_entry = tkinter.Entry(window)
grade_entry.grid(row = "3", column = "2")

class_label = tkinter.Label(window, text = "Math Class: ")
class_label.grid(row = "4", column = "1")
class_entry = tkinter.Entry(window)
class_entry.grid(row = "4", column = "2")

topic_label = tkinter.Label(window, text = " Problem Topic: ")
topic_label.grid(row = "5", column = "1")
topic_entry = tkinter.Entry(window)
topic_entry.grid(row = "5", column = "2")

process_button = tkinter.Button(window, text = "Update Sheet", command = InputData)
process_button.grid(row = "6", column = "2")

reset_button = tkinter.Button(window, text = "Reset Fields", command = ResetData)
reset_button.grid(row = "6", column = "1")

window.mainloop()

我将“PALSheet.py”转换为“PALSheet.exe”后,文件显示如下错误:

Traceback(最近一次调用最后一次): 文件“palsheet.py”,第 9 行,在 文件“site-packages\oauth2client\service_account.py”,第 219 行,在 from_json_keyfile_name FileNotFoundError:[Errno 2] 没有这样的文件或目录:'client_secret.json' [280] 执行脚本 PALSheet 失败

【问题讨论】:

    标签: python json python-3.x pyinstaller


    【解决方案1】:

    每当您使用构建 .exe 时

    pyinstaller -F foobar.py

    pyinstaller 将在您的 appdata 文件夹中创建一个包含 .exe 依赖项的临时目录。如果您在没有 -F 标志的情况下构建它,theese 将与可执行文件本身位于同一目录中。要访问这个临时文件夹,您需要使用如下函数:

    import sys,os
    def resource_path(relative_path: str) -> str:
        """ Get absolute path to resource"""
        try:
            # PyInstaller creates a temp folder and stores path in _MEIPASS
            base_path = sys._MEIPASS
        except Exception:
            base_path = os.path.abspath(".")
    
        return os.path.join(base_path, relative_path)
    

    然后,您可以将 "client_secret.json" 替换为 ressource_path("client_secret.json"),即使使用 pyinstaller 的 -F 标志(当然没有 -F 标志)也能正常工作。 您需要将数据附加到 pyinstaller,因此新的构建命令是:

    pyinstaller -F --add-data "client_secret.json;client_secret.json" foobar.py

    【讨论】:

    • 我需要在我的 .py 文件中包含上述函数吗?我不明白 100%
    • 是的,因为您调用的是ressource_path("client_secret.json") 而不是"ressource_path",所以您需要在.py 中定义它
    • 不客气。如果可行,请留下一个赞成票并将其标记为已解决。其他:请随时再次提问:)
    • foobar.py 只是您文件的占位符...由于您的文件名为“PALSheet.py”,只需将其替换为“PALSheet.py”...
    • 只需将函数复制粘贴到您的脚本中,然后将所有"ressource_path" 替换为ressource_path("client_secret.json")
    【解决方案2】:

    这是你的新“PALSheet.py”:

    import tkinter
    import gspread
    from oauth2client.service_account import ServiceAccountCredentials
    import sys,os
    
    def resource_path(relative_path: str) -> str:
        """ Get absolute path to resource"""
        try:
            # PyInstaller creates a temp folder and stores path in _MEIPASS
            base_path = sys._MEIPASS
        except Exception:
            base_path = os.path.abspath(".")
    
        return os.path.join(base_path, relative_path)
    
    # use creds to create a client to interact with the Google Drive API and Google Sheets API
    scope = 'https://spreadsheets.google.com/feeds','https://www.googleapis.com/auth/drive'
    
    creds = ServiceAccountCredentials.from_json_keyfile_name(ressource_path("client_secret.json"), scope)
    client = gspread.authorize(creds)
    
    # Find a workbook by name and open the first sheet
    # Make sure you use the right name here.
    sheet = client.open("PAL Tutoring Data SpreadSheet").sheet1
    
    def ResetData():
        student_entry.delete('0', 'end')
        grade_entry.delete('0', 'end')
        class_entry.delete('0', 'end')
        topic_entry.delete('0', 'end')
    
    def FindNextRow():
        global next_row
        next_row = ((len(sheet.col_values(int(1)))) + 1)
    
    def InputData():
        FindNextRow()
    
        global next_row
        sheet.update_cell(next_row, 1, student_entry.get())
        sheet.update_cell(next_row, 2, grade_entry.get())
        sheet.update_cell(next_row, 3, class_entry.get())
        sheet.update_cell(next_row, 4, topic_entry.get())
    
        student_entry.delete('0', 'end')
        grade_entry.delete('0', 'end')
        class_entry.delete('0', 'end')
        topic_entry.delete('0', 'end')
    
    window = tkinter.Tk()
    window.title("PAL Data Input")
    
    student_label = tkinter.Label(window, text = " Student Name: ")
    student_label.grid(row = "2", column = "1")
    student_entry = tkinter.Entry(window)
    student_entry.grid(row = "2", column = "2")
    
    grade_label = tkinter.Label(window, text = "Grade Level: ")
    grade_label.grid(row = "3", column = "1")
    grade_entry = tkinter.Entry(window)
    grade_entry.grid(row = "3", column = "2")
    
    class_label = tkinter.Label(window, text = "Math Class: ")
    class_label.grid(row = "4", column = "1")
    class_entry = tkinter.Entry(window)
    class_entry.grid(row = "4", column = "2")
    
    topic_label = tkinter.Label(window, text = " Problem Topic: ")
    topic_label.grid(row = "5", column = "1")
    topic_entry = tkinter.Entry(window)
    topic_entry.grid(row = "5", column = "2")
    
    process_button = tkinter.Button(window, text = "Update Sheet", command = InputData)
    process_button.grid(row = "6", column = "2")
    
    reset_button = tkinter.Button(window, text = "Reset Fields", command = ResetData)
    reset_button.grid(row = "6", column = "1")
    
    window.mainloop()
    

    现在你需要运行:

    pyinstaller -F --add-data "client_secret.json;client_secret.json" "PALSheet.py"

    【讨论】:

    • 我还需要添加你建议的功能吗?还是那是 pyinstaller 的一部分?
    • 是的,忘记实现了。现在它应该运行
    • 好的,但现在我认为我的可访问性有问题。我正在使用 gspread(链接到 Google 表格),现在它说: PermissionError: [Errno 13] Permission denied: 'C:Users\\Bee_Boy\\AppData\\Local\\Temp\\_MEI19522\\client_secret.json
    • 以管理员身份运行?
    猜你喜欢
    • 2022-08-15
    • 1970-01-01
    • 1970-01-01
    • 2022-11-13
    • 2019-01-16
    • 2016-04-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多