粒子箱概率密度计算器Tkinter脚本转EXE启动失败求助
粒子箱概率密度计算器打包EXE无法启动问题排查
问题描述
我开发了一款基于Tkinter的粒子箱(Particle in a Box)概率密度计算器,尝试用PyInstaller执行命令:
pyinstaller --name PartyProb --onefile --windowed --icon=psi.ico PartyProb.py
同时也试过auto-py-to-exe工具,但生成的EXE文件点击后无任何反应,需要解决打包后无法启动的问题。
完整代码
import tkinter as tk from tkinter import ttk from scipy.integrate import quad import numpy as np from PIL import Image, ImageTk from sympy import symbols, pi, evalf import matplotlib.pyplot as plt from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg def parse_position_range(position_str): positions = [p.strip() for p in position_str.split(',')] parsed_positions = [] for pos in positions: try: parsed_positions.append(float(pos)) except ValueError: parsed_positions.append(eval(pos.replace('pi', str(pi))).evalf()) return parsed_positions def probability_density_function(x, n, L): psi_squared = (2/L) * np.sin((n * np.pi * x) / L)**2 return psi_squared def calculate_probability_in_range(x_range, n, L): integrand = lambda x: probability_density_function(x, n, L) probability, _ = quad(integrand, x_range[0], x_range[1]) return probability def plot_probability_density(n_values, L, x_range): fig, ax = plt.subplots(figsize=(7, 4)) for n in n_values: x_values = np.linspace(0, L, 1000) psi_squared = probability_density_function(x_values, n, L) ax.plot(x_values, psi_squared, label=f'n={n}') ax.set_xlabel('Position (x)') ax.set_ylabel('|ψ(x)|^2') ax.legend() ax.grid(True) return fig def on_calculate_probability(): n_values = [int(n) for n in entry_n.get().split(',')] L = float(entry_L.get()) x_range_str = entry_range.get() x_range = parse_position_range(x_range_str) probability_results = [] for n in n_values: probability_in_range = calculate_probability_in_range(x_range, n, L) probability_results.append(f'P(n={n}): {probability_in_range:.4e}') probability_label.config(text='\n'.join(probability_results), font=('Arial', 12)) # Plot the probability density function fig = plot_probability_density(n_values, L, x_range) canvas = FigureCanvasTkAgg(fig, master=window) canvas_widget = canvas.get_tk_widget() canvas_widget.grid(row=6, column=0, columnspan=2, pady=10) # Create the Tkinter root window window = tk.Tk() window.title("Particle in a Box Probability Calculator") # Load and resize the image image_path = r"C:\Users\jandi\Downloads\normalised_rob_density_function.png" original_image = Image.open(image_path) resized_image = original_image.resize((625, 100)) # Adjust the size as needed image = ImageTk.PhotoImage(resized_image) # Introductory message label intro_label = ttk.Label(window, text="Welcome to the Particle in a Box Probability Calculator!\n" "This programme calculates the probability of finding a particle in a given range in one dimension.\n" "Enter quantum numbers [1, 2, 3, ...], box width (L) [1], and position range [0 ≤ P(L) ≤ 1] to get started.\n" "Values in the position range can include pi (this format) but not fractions [1/2] (idk why). For example, [0.5*pi/2].\n" "A graph of the probability density at each position will also be generated. It is similar to the text and are a combination\n" "of sine and cosine wavefunction. Interestingly, as the quantum number increases, |ψ|^2 becomes an approximation of some constant probability.\n" "Enjoy P.Chem II!\n" "jm\n") intro_label.grid(row=0, column=0, columnspan=2, pady=10) # Image label image_label = ttk.Label(window, image=image) image_label.grid(row=1, column=0, columnspan=2, pady=10) ttk.Label(window, text="Quantum Numbers (comma-separated):").grid(row=2, column=0, sticky='e', padx=5, pady=5) ttk.Label(window, text="Box Width (L):").grid(row=3, column=0, sticky='e', padx=5, pady=5) ttk.Label(window, text="Position Range (comma-separated):").grid(row=4, column=0, sticky='e', padx=5, pady=5) entry_n = ttk.Entry(window) entry_n.grid(row=2, column=1, pady=5) entry_L = ttk.Entry(window) entry_L.grid(row=3, column=1, pady=5) entry_range = ttk.Entry(window) entry_range.grid(row=4, column=1, pady=5) calculate_button = ttk.Button(window, text="Calculate Probability", command=on_calculate_probability) calculate_button.grid(row=5, column=0, columnspan=2, pady=10) probability_label = ttk.Label(window, text="", font=('Times New Roman', 12)) probability_label.grid(row=5, column=1, columnspan=2, pady=10) window.mainloop()
排查与解决步骤
1. 修复硬编码的图片路径问题
代码中使用了绝对路径加载图片,打包后EXE无法找到该路径,这是最常见的启动失败原因:
- 把图片和脚本放在同一目录,修改代码中的图片路径为相对路径,并添加资源打包逻辑:
import sys import os def resource_path(relative_path): try: base_path = sys._MEIPASS # 打包后的临时资源路径 except Exception: base_path = os.path.abspath(".") # 开发时的当前路径 return os.path.join(base_path, relative_path) # 替换原有的image_path赋值 image_path = resource_path("normalised_rob_density_function.png") - 打包时用
--add-data参数将图片打包进EXE:
(Windows用分号分隔路径,macOS/Linux用冒号)pyinstaller --name PartyProb --onefile --windowed --icon=psi.ico --add-data "normalised_rob_density_function.png;." PartyProb.py
2. 查看控制台错误信息
--windowed参数会隐藏控制台,导致无法看到启动时的报错。先去掉该参数打包,运行EXE时控制台会显示具体错误:
pyinstaller --name PartyProb --onefile --icon=psi.ico PartyProb.py
根据控制台输出的错误(如缺失依赖、路径错误)针对性修复。
3. 解决Matplotlib后端兼容问题
Tkinter与Matplotlib结合打包时,需显式指定后端:
在代码开头添加:
import matplotlib matplotlib.use('TkAgg')
4. 补充缺失的依赖导入
PyInstaller可能无法自动识别scipy、sympy的子模块,创建hook-PartyProb.py文件,内容如下:
from PyInstaller.utils.hooks import collect_submodules hiddenimports = collect_submodules('scipy.integrate') + collect_submodules('sympy') + collect_submodules('numpy')
打包时添加钩子参数:
pyinstaller --name PartyProb --onefile --windowed --icon=psi.ico --additional-hooks-dir=. PartyProb.py
5. 修复重复绘图导致的界面卡死
每次点击计算都会创建新画布,但未销毁旧画布,可能导致界面无响应:
修改on_calculate_probability函数,添加旧画布销毁逻辑:
# 在函数顶部添加全局变量声明 global canvas_widget # 销毁旧画布(首次运行时忽略错误) try: canvas_widget.destroy() except NameError: pass # 后续绘图代码不变 fig = plot_probability_density(n_values, L, x_range) canvas = FigureCanvasTkAgg(fig, master=window) canvas_widget = canvas.get_tk_widget() canvas_widget.grid(row=6, column=0, columnspan=2, pady=10)
内容的提问来源于stack exchange,提问作者EurphoricPhantom
相关产品推荐
相关产品推荐

