QT+Python程序转exe/安装包后数据丢失问题求助
解决方案
核心思路:分离只读资源与可写数据目录
程序运行时,只读资源(如初始JSON模板、图片、字体) 放在PyInstaller打包的资源目录(_MEIPASS),而可写数据需要存到用户有写入权限的系统标准目录,比如:
- Windows:
C:\Users\<用户名>\AppData\Roaming\SmartTank - macOS:
~/Library/Application Support/SmartTank - Linux:
~/.config/SmartTank
步骤1:修改路径处理逻辑
新增两个方法,分别处理只读资源路径和可写数据路径:
import sys import os import json import shutil from pathlib import Path class YourMainWindow(QMainWindow): def __init__(self): super().__init__() # 初始化应用数据目录并确保目录存在 self.app_data_dir = self.get_app_data_dir() os.makedirs(self.app_data_dir, exist_ok=True) def resource_path(self, relative_path): """获取只读资源的绝对路径(兼容开发环境与PyInstaller打包)""" try: base_path = sys._MEIPASS except Exception: base_path = os.path.abspath(".") return os.path.join(base_path, relative_path) def get_app_data_dir(self): """获取用户可写的应用数据目录,跨平台兼容""" if sys.platform == 'win32': return os.path.join(os.environ['APPDATA'], 'SmartTank') elif sys.platform == 'darwin': return os.path.join(os.path.expanduser('~'), 'Library', 'Application Support', 'SmartTank') else: return os.path.join(os.path.expanduser('~'), '.config', 'SmartTank') def get_data_file_path(self, filename): """获取可写数据文件路径,自动复制初始模板(若不存在)""" data_file = os.path.join(self.app_data_dir, filename) # 若数据文件不存在,从只读资源目录复制初始模板 if not os.path.exists(data_file): template_file = self.resource_path(f'database/{filename}') if os.path.exists(template_file): shutil.copy(template_file, data_file) else: # 无初始模板时创建空数据结构 if filename == 'data_nomor.json': with open(data_file, 'w') as f: json.dump({"nomor": []}, f) return data_file
步骤2:修改数据读写逻辑
以save_data_nomor为例,替换原路径调用为新的可写路径方法:
def save_data_nomor(self): new_number = self.ui.inputNomor.text().strip() if new_number == "": print("Number cannot be empty.") return # 获取可写数据文件路径 file_path = self.get_data_file_path('data_nomor.json') try: with open(file_path, 'r') as file: data_nomor = json.load(file) except Exception as e: print(f"Error reading JSON file: {e}") data_nomor = {"nomor": []} if new_number not in data_nomor["nomor"]: data_nomor["nomor"].append(new_number) try: with open(file_path, 'w') as file: json.dump(data_nomor, file, indent=4) print(f"Number '{new_number}' successfully saved.") except Exception as e: print(f"Error saving JSON file: {e}") self.ui.inputNomor.clear()
步骤3:调整PyInstaller打包命令
单文件/非单文件打包命令保持不变,确保初始资源被正确打包:
# 单文件打包 pyinstaller --name SmartTank --onefile --windowed --icon=icon.ico --add-data "database;database" --add-data "images;images" --add-data "font;font" main.py # 非单文件打包 pyinstaller --name SmartTank --windowed --icon=icon.ico --add-data "database;database" --add-data "images;images" --add-data "font;font" main.py
步骤4:调整Inno Setup配置
无需给Program Files目录添加写入权限,数据已存储在用户专属目录。典型脚本片段:
[Setup] AppName=SmartTank AppVersion=1.0 DefaultDirName={pf}\SmartTank DefaultGroupName=SmartTank OutputDir=output OutputBaseFilename=SmartTankInstaller SetupIconFile=icon.ico [Files] Source: "dist\SmartTank\*"; DestDir: "{app}"; Flags: recursesubdirs createallsubdirs [Icons] Name: "{group}\SmartTank"; Filename: "{app}\SmartTank.exe" Name: "{commondesktop}\SmartTank"; Filename: "{app}\SmartTank.exe"; Tasks: desktopicon
关键说明
- 原方法失效原因
- 单文件打包时,
_MEIPASS是临时目录,程序退出后自动删除,数据无法持久化。 - 安装到Program Files后,普通用户无写入权限,无法修改目录内文件。
- 单文件打包时,
- 跨平台兼容性
get_app_data_dir自动适配各系统标准应用数据目录,符合系统规范。
- 初始数据复制逻辑
- 首次运行时自动将打包的初始模板复制到用户目录,后续读写操作副本,避免权限与丢失问题。
内容的提问来源于stack exchange,提问作者SKevinAR18
相关产品推荐
相关产品推荐

