PyInstaller打包exe仅在虚拟环境正常,双击无法生成结果文件
Let's tackle this frustrating problem you're dealing with: your Tkinter-based script works flawlessly when run from a Conda terminal, but when you double-click the PyInstaller-built exe or run it via Windows CMD, it can create the Results folder but won't write any of the 5 output text files. Even after ruling out permissions, the discrepancy between execution environments is puzzling. Here’s the breakdown of likely causes and fixes:
Key Observations & Likely Root Causes
- Silent Errors in GUI Mode: When running via double-click or CMD, your GUI script won’t display terminal error messages. If an exception occurs during file-writing steps (like missing dependencies or path issues), the code stops executing without alerting you—making it look like files aren’t being written, when in reality an error halted the process.
- PyInstaller Hidden Dependencies: Your current
--hidden-importonly coverssklearn.utils._cython_blas, but libraries likescipy,sklearn, andpandasoften have additional hidden dependencies that PyInstaller misses when building the exe. These missing dependencies can cause runtime failures in non-Conda environments. - Path Resolution Edge Cases: While your path logic seems solid, there might be subtle differences in how
os.path.realpathoros.chdirbehaves when the exe is launched outside a Conda terminal, leading to unexpected path issues.
Step-by-Step Fixes
1. Add Error Handling to Catch Silent Failures
Wrap your file-writing code blocks in try-except and use Tkinter’s messagebox to display errors. This will let you see exactly what’s going wrong when double-clicking the exe.
For example, modify one of your file-writing sections like this:
try: with open(pca_name, 'w') as file_pca: if tgt: principalDf = pd.concat([principalDf, target], axis = 1) principalDf.to_csv(file_pca, header = False, index = False, sep = '\t', line_terminator = '\n', encoding = 'UTF-8') except Exception as e: messagebox.showerror("File Write Error", f"Failed to create PCA file:\n{str(e)}")
Repeat this pattern for all 5 file-writing blocks in your code.
2. Expand PyInstaller Hidden Dependencies
Update your PyInstaller command to include more hidden imports and use --collect-all to ensure all required library files are bundled:
pyinstaller -F --exclude-packages PyQt5 --hidden-import="sklearn.utils._cython_blas" --hidden-import="scipy.spatial.distance" --collect-all sklearn --collect-all scipy --collect-all pandas your_script.py
The --collect-all flag tells PyInstaller to include all files from the specified packages, avoiding missing dependencies that cause runtime crashes.
3. Test with a Console Window
Add the --console flag to your PyInstaller command. This will open a terminal window when you double-click the exe, showing all print statements and error logs:
pyinstaller -F --exclude-packages PyQt5 --hidden-import="sklearn.utils._cython_blas" --console your_script.py
This is invaluable for debugging—you’ll see exactly where the code fails if it hits an exception.
4. Simplify Path Handling
Since you already os.chdir(dir_) into the Results folder, you can remove the dir_ prefix from your file paths to avoid redundant path concatenation. For example:
# Instead of this: pca_name = f'{dir_}[PCA][{scaling}]{name}.nna' # Use this (after os.chdir(dir_)): pca_name = f'[PCA][{scaling}]{name}.nna'
This eliminates any chance of path formatting errors.
5. Verify Runtime Paths
Add a quick check to confirm your working directory and Results folder path are correct. Insert this after creating the dir_ variable:
messagebox.showinfo("Debug Paths", f"Working Directory: {os.getcwd()}\nResults Folder: {dir_}")
This will confirm that the script is pointing to the correct directory when launched outside Conda.
Example Modified Code Snippet
Here’s a snippet of your code with error handling added (for the first file write):
# ... (your existing code up to this point) os.chdir(dir_) name = os.path.splitext(os.path.basename(file_nna))[0] name_no_dim = name.split('(')[0] #remove NaN nna = nna.dropna(axis = 0, how = 'all') nna = nna.dropna(axis = 1, how = 'all') nna_all = nna if tgt: target = nna.iloc[:,-num_tgt:] nna = nna.iloc[:,:-num_tgt] #num var and records M,N = shape(nna) #data scaling app = tk.Tk() minmax = messagebox.askyesno('scaling', 'Do you want a linear scaling?\n(If NO, a standard scaling will be computed)') if minmax: scaler = MinMaxScaler() scaling ='MinMax' else: scaler = StandardScaler() scaling ='StSc' mode_eu = messagebox.askyesno('mode', 'Do you want EU?\n(If NO, CORR will be computed)') if mode_eu: mode = 'EU' else: mode = 'CORR' app.destroy() # Add error handling for PCA processing and file writes try: nna = scaler.fit_transform(nna) pca_name = f'[PCA][{scaling}]{name}.nna' pca_name_T = f'[PCA][{scaling}][{mode}]{name_no_dim}({N}x{N}).wtx' pca_name_T_tgt = f'[PCA][{scaling}][{mode}]{name_no_dim}({N+num_tgt}x{N+num_tgt}).wtx' pca_name_r = f'[PCA][{scaling}]{name_no_dim}({N+num_tgt}x{M})_record.nna' pca_name_r_wtx = f'[PCA][{scaling}][{mode}]{name_no_dim}({M}x{M})_record.wtx' pca = PCA(n_components = N) principalComponents = pca.fit_transform(nna) principalDf = pd.DataFrame(data = principalComponents) if mode == 'EU': distances = pdist(principalDf.values, metric='euclidean') dist_matrix = squareform(distances) elif mode== 'CORR': distances = pdist(principalDf.values, metric='correlation') dist_matrix = squareform(distances) wtx = 1-dist_matrix with open(pca_name, 'w') as file_pca: if tgt: principalDf = pd.concat([principalDf, target], axis = 1) principalDf.to_csv(file_pca, header = False, index = False, sep = '\t', line_terminator = '\n', encoding = 'UTF-8') messagebox.showinfo("Success", f"Created file: {pca_name}") except Exception as e: messagebox.showerror("Processing Error", f"Error during PCA processing:\n{str(e)}") # Repeat similar try-except blocks for the remaining file writes
Final Notes
Most likely, the issue is missing dependencies that only manifest when running outside the Conda environment. Using --collect-all for sklearn, scipy, and pandas should resolve this. Adding error handling will help you confirm the exact cause if the problem persists.
内容的提问来源于stack exchange,提问作者FrancisSem

