Compare commits

..

6 Commits

Author SHA1 Message Date
2fe86ef332 打包文件 2025-12-30 17:50:43 +08:00
26397a83dd 1 2025-12-29 00:36:40 +08:00
d33216f9b9 添加库存不足检查功能
在记录消耗到库存功能中增加了库存充足性验证,确保只有在所有原料库存充足时才允许记录消耗。

主要改进:
- 在记录消耗前先检查所有原料的当前库存
- 如果任何原料库存不足,显示详细的库存不足信息并中止操作
- 提供清晰的提示信息,显示每种原料所需数量和实际库存数量
- 只有所有原料库存都充足时才执行记录操作
- 优化成功提示信息,显示记录的原料种类数量

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-29 00:33:51 +08:00
5cf27c8cfe 优化原料管理界面功能
1. 类目选项标准化
   - 新增和编辑界面的类目固定为"面料"、"辅料"、"其他"三个选项
   - 新增原料时默认选中"面料"

2. 必填字段验证
   - 新增必填验证:类目、类型、幅宽、克重
   - 保存时验证所有必填字段,提供明确的错误提示

3. 已删除原料恢复功能
   - 允许新增已被删除的型号,自动恢复并更新原料信息
   - 无需用户确认,直接恢复已删除原料

4. 供应商字段优化
   - 新增原料时供应商默认为空
   - 用户可选择输入新供应商或从列表选择

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-28 00:41:39 +08:00
803127062e 修复新建数据库缺少is_deleted列的问题
在CREATE TABLE语句中直接添加is_deleted字段,确保新建数据库时就包含该列。
修改了三个表的建表语句:
- fabrics表
- fabric_stock_in表
- fabric_consumption表

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-28 00:14:31 +08:00
83609aaa20 款号管理列表设置为只读模式
将衣服款号管理界面的表格所有列(款号、类目数量、款式图预览)设置为只读,防止用户直接在表格中编辑内容,保持与原料库列表的一致性。

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-28 00:10:43 +08:00
9 changed files with 415 additions and 60 deletions

74
build_exe.py Normal file
View File

@@ -0,0 +1,74 @@
"""
打包脚本 - 将项目打包为exe文件
使用方法: python build_exe.py
"""
import os
import sys
import subprocess
import shutil
def build_exe():
"""使用PyInstaller打包为exe"""
# 项目根目录
root_dir = os.path.dirname(os.path.abspath(__file__))
os.chdir(root_dir)
# 主程序文件
main_script = "main.py"
# 打包命令
cmd = [
"pyinstaller",
"--name=服装布料计算管理器",
"--onefile", # 打包为单个exe文件
"--windowed", # 不显示控制台窗口
"--hidden-import=PyQt5.QtCore",
"--hidden-import=PyQt5.QtGui",
"--hidden-import=PyQt5.QtWidgets",
"--hidden-import=sqlite3",
"--clean", # 清理临时文件
]
# 如果有图标文件,添加图标参数
if os.path.exists("icon.ico"):
cmd.append("--icon=icon.ico")
# 如果数据库文件存在,添加到打包数据中
if os.path.exists("fabric_library.db"):
# Windows使用分号Linux/Mac使用冒号
separator = ";" if sys.platform == "win32" else ":"
cmd.append(f"--add-data=fabric_library.db{separator}.")
cmd.append(main_script)
print("开始打包...")
print(f"执行命令: {' '.join(cmd)}")
try:
# 执行打包
result = subprocess.run(cmd, check=True, capture_output=True, text=True)
print("打包成功!")
print(f"\n输出文件位置: {os.path.join(root_dir, 'dist', '服装布料计算管理器.exe')}")
# 复制数据库文件到dist目录如果存在
if os.path.exists("fabric_library.db"):
dist_dir = os.path.join(root_dir, "dist")
if os.path.exists(dist_dir):
shutil.copy2("fabric_library.db", dist_dir)
print(f"数据库文件已复制到: {os.path.join(dist_dir, 'fabric_library.db')}")
print("\n打包完成!可以在 dist 目录中找到生成的exe文件。")
except subprocess.CalledProcessError as e:
print(f"打包失败: {e}")
print(f"错误输出: {e.stderr}")
sys.exit(1)
except FileNotFoundError:
print("错误: 未找到 pyinstaller请先安装: pip install pyinstaller")
sys.exit(1)
if __name__ == "__main__":
build_exe()

62
build_exe.spec Normal file
View File

@@ -0,0 +1,62 @@
# -*- mode: python ; coding: utf-8 -*-
"""
PyInstaller 配置文件
可以直接使用: pyinstaller build_exe.spec
"""
import os
block_cipher = None
# 收集所有需要打包的Python文件
a = Analysis(
['main.py'],
pathex=[],
binaries=[],
datas=[
('fabric_library.db', '.') if os.path.exists('fabric_library.db') else None,
],
hiddenimports=[
'PyQt5.QtCore',
'PyQt5.QtGui',
'PyQt5.QtWidgets',
'sqlite3',
],
hookspath=[],
hooksconfig={},
runtime_hooks=[],
excludes=[],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher,
noarchive=False,
)
# 过滤掉None值
a.datas = [d for d in a.datas if d is not None]
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
exe = EXE(
pyz,
a.scripts,
a.binaries,
a.zipfiles,
a.datas,
[],
name='服装布料计算管理器',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
upx_exclude=[],
runtime_tmpdir=None,
console=False, # 不显示控制台窗口
disable_windowed_traceback=False,
argv_emulation=False,
target_arch=None,
codesign_identity=None,
entitlements_file=None,
icon=None, # 可以指定图标文件路径,例如: 'icon.ico'
)

View File

@@ -42,7 +42,8 @@ class DatabaseManager:
unit TEXT DEFAULT '', unit TEXT DEFAULT '',
timestamp TEXT, timestamp TEXT,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP, created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
is_deleted INTEGER DEFAULT 0
) )
''') ''')
@@ -194,6 +195,7 @@ class DatabaseManager:
note TEXT, note TEXT,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP, created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
is_deleted INTEGER DEFAULT 0,
FOREIGN KEY (model) REFERENCES fabrics(model) FOREIGN KEY (model) REFERENCES fabrics(model)
) )
''') ''')
@@ -216,6 +218,7 @@ class DatabaseManager:
unit TEXT, unit TEXT,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP, created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
is_deleted INTEGER DEFAULT 0,
FOREIGN KEY (style_number) REFERENCES garments(style_number), FOREIGN KEY (style_number) REFERENCES garments(style_number),
FOREIGN KEY (model) REFERENCES fabrics(model) FOREIGN KEY (model) REFERENCES fabrics(model)
) )

View File

@@ -142,17 +142,24 @@ class GarmentLibraryDialog(QDialog):
self.garment_table.setRowHeight(i, 140) self.garment_table.setRowHeight(i, 140)
for row_idx, (style_number, image_path) in enumerate(rows): for row_idx, (style_number, image_path) in enumerate(rows):
self.garment_table.setItem(row_idx, 0, QTableWidgetItem(style_number)) # 款号列 - 设置为只读
style_item = QTableWidgetItem(style_number)
style_item.setFlags(style_item.flags() & ~Qt.ItemIsEditable)
self.garment_table.setItem(row_idx, 0, style_item)
# 查询材料数量 # 查询材料数量
with self.get_conn() as conn: with self.get_conn() as conn:
cursor2 = conn.execute("SELECT COUNT(*) FROM garment_materials WHERE style_number = ?", (style_number,)) cursor2 = conn.execute("SELECT COUNT(*) FROM garment_materials WHERE style_number = ?", (style_number,))
count = cursor2.fetchone()[0] count = cursor2.fetchone()[0]
self.garment_table.setItem(row_idx, 1, QTableWidgetItem(str(count))) # 类目数量列 - 设置为只读
count_item = QTableWidgetItem(str(count))
count_item.setFlags(count_item.flags() & ~Qt.ItemIsEditable)
self.garment_table.setItem(row_idx, 1, count_item)
# 显示图片预览 # 显示图片预览
image_item = QTableWidgetItem() image_item = QTableWidgetItem()
image_item.setTextAlignment(Qt.AlignCenter) image_item.setTextAlignment(Qt.AlignCenter)
image_item.setFlags(image_item.flags() & ~Qt.ItemIsEditable)
if image_path and os.path.exists(image_path): if image_path and os.path.exists(image_path):
try: try:

51
main.py
View File

@@ -243,7 +243,10 @@ class FabricManager(QMainWindow):
WHERE style_number = ? WHERE style_number = ?
''', (style_number,)) ''', (style_number,))
rows = cursor.fetchall() rows = cursor.fetchall()
inserted = 0
# 第一步:检查所有原料库存是否充足
insufficient_materials = []
materials_to_record = []
for category, fabric_type, usage_per_piece, unit in rows: for category, fabric_type, usage_per_piece, unit in rows:
if usage_per_piece == 0: if usage_per_piece == 0:
@@ -273,16 +276,58 @@ class FabricManager(QMainWindow):
consume_qty = usage_per_piece * quantity * (1 + loss_rate) consume_qty = usage_per_piece * quantity * (1 + loss_rate)
final_unit = unit final_unit = unit
# 检查当前库存
stock_info = conn.execute('''
SELECT
COALESCE(SUM(CASE WHEN si.is_deleted = 0 THEN si.quantity ELSE 0 END), 0) AS total_in,
COALESCE(SUM(CASE WHEN c.is_deleted = 0 THEN c.consume_quantity ELSE 0 END), 0) AS total_out
FROM fabrics f
LEFT JOIN fabric_stock_in si ON f.model = si.model
LEFT JOIN fabric_consumption c ON f.model = c.model
WHERE f.model = ?
GROUP BY f.model
''', (category,)).fetchone()
if stock_info:
total_in, total_out = stock_info
current_stock = total_in - total_out
else:
current_stock = 0
# 如果库存不足,记录下来
if current_stock < consume_qty:
material_name = category or "未命名材料"
insufficient_materials.append(
f"{material_name}: 需要 {consume_qty:.3f} {final_unit},库存仅剩 {current_stock:.3f} {final_unit}"
)
else:
# 库存充足,加入待记录列表
materials_to_record.append((
style_number, category, usage_per_piece, quantity,
loss_rate, consume_qty, final_unit
))
# 如果有库存不足的材料,提示用户并中止操作
if insufficient_materials:
message = "以下原料库存不足,无法记录消耗:\n\n" + "\n".join(insufficient_materials)
QMessageBox.warning(self, "库存不足", message)
return
# 第二步:所有库存都充足,执行记录
inserted = 0
for material_data in materials_to_record:
style_num, model, single_usage, qty, loss, consume_qty, final_unit = material_data
conn.execute(''' conn.execute('''
INSERT INTO fabric_consumption INSERT INTO fabric_consumption
(style_number, model, single_usage, quantity_made, loss_rate, consume_quantity, consume_date, unit) (style_number, model, single_usage, quantity_made, loss_rate, consume_quantity, consume_date, unit)
VALUES (?, ?, ?, ?, ?, ?, ?, ?) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
''', (style_number, category, usage_per_piece, quantity, loss_rate, consume_qty, datetime.now().strftime('%Y-%m-%d'), final_unit)) ''', (style_num, model, single_usage, qty, loss, consume_qty, datetime.now().strftime('%Y-%m-%d'), final_unit))
inserted += 1 inserted += 1
conn.commit() conn.commit()
if inserted > 0: if inserted > 0:
QMessageBox.information(self, "成功", "本次生产消耗已记录到库存!") QMessageBox.information(self, "成功", f"本次生产消耗已记录到库存!共记录 {inserted} 种原料。")
else: else:
QMessageBox.information(self, "提示", "本次没有可记录的原料消耗") QMessageBox.information(self, "提示", "本次没有可记录的原料消耗")
except Exception as e: except Exception as e:

View File

@@ -111,11 +111,8 @@ class RawMaterialEditDialog(QDialog):
"""加载类目和供应商列表""" """加载类目和供应商列表"""
try: try:
with self.get_conn() as conn: with self.get_conn() as conn:
# 加载类目 # 加载类目 - 设置默认选项为:面料、辅料、其他
cursor = conn.execute("SELECT DISTINCT category FROM fabrics WHERE category IS NOT NULL AND category != '' AND (is_deleted IS NULL OR is_deleted = 0)") self.edit_major_category.addItems(["面料", "辅料", "其他"])
majors = set(row[0] for row in cursor.fetchall() if row[0])
majors.update({"布料", "辅料", "其他"})
self.edit_major_category.addItems(sorted(majors))
# 加载供应商 # 加载供应商
cursor = conn.execute("SELECT DISTINCT supplier FROM fabrics WHERE supplier IS NOT NULL AND supplier != '' AND (is_deleted IS NULL OR is_deleted = 0) ORDER BY supplier") cursor = conn.execute("SELECT DISTINCT supplier FROM fabrics WHERE supplier IS NOT NULL AND supplier != '' AND (is_deleted IS NULL OR is_deleted = 0) ORDER BY supplier")
@@ -172,6 +169,26 @@ class RawMaterialEditDialog(QDialog):
major = self.edit_major_category.currentText().strip() major = self.edit_major_category.currentText().strip()
sub = self.edit_sub_category.text().strip() sub = self.edit_sub_category.text().strip()
# 验证必填字段:类目
if not major:
QMessageBox.warning(self, "错误", "请选择类目")
return
# 验证必填字段:类型
if not sub:
QMessageBox.warning(self, "错误", "请输入类型")
return
# 验证必填字段:幅宽
if self.edit_width.value() <= 0:
QMessageBox.warning(self, "错误", "请输入幅宽")
return
# 验证必填字段:克重
if self.edit_gsm.value() <= 0:
QMessageBox.warning(self, "错误", "请输入克重")
return
# 特殊处理胸杯类型 # 特殊处理胸杯类型
if "胸杯" in sub: if "胸杯" in sub:
major = "辅料" major = "辅料"
@@ -453,16 +470,16 @@ class RawMaterialLibraryDialog(QDialog):
self.load_table() self.load_table()
def load_major_categories(self): def load_major_categories(self):
"""加载主类目""" """加载主类目 - 只显示数据库中实际存在的类目"""
try: try:
with self.get_conn() as conn: with self.get_conn() as conn:
cursor = conn.execute("SELECT DISTINCT category FROM fabrics WHERE category IS NOT NULL AND category != '' AND (is_deleted IS NULL OR is_deleted = 0)") cursor = conn.execute("SELECT DISTINCT category FROM fabrics WHERE category IS NOT NULL AND category != '' AND (is_deleted IS NULL OR is_deleted = 0)")
majors = set(row[0] for row in cursor.fetchall() if row[0]) majors = set(row[0] for row in cursor.fetchall() if row[0])
majors.update({"布料", "辅料", "其他"})
self.major_combo.blockSignals(True) self.major_combo.blockSignals(True)
self.major_combo.clear() self.major_combo.clear()
self.major_combo.addItem("全部类目") self.major_combo.addItem("全部类目")
if majors:
self.major_combo.addItems(sorted(majors)) self.major_combo.addItems(sorted(majors))
self.major_combo.blockSignals(False) self.major_combo.blockSignals(False)
except: except:
@@ -471,26 +488,12 @@ class RawMaterialLibraryDialog(QDialog):
def load_add_major_categories(self): def load_add_major_categories(self):
"""加载添加界面的主类目""" """加载添加界面的主类目"""
try: # 设置默认类目选项为:面料、辅料、其他
with self.get_conn() as conn:
cursor = conn.execute("SELECT DISTINCT category FROM fabrics WHERE category IS NOT NULL AND category != '' AND (is_deleted IS NULL OR is_deleted = 0)")
majors = set(row[0] for row in cursor.fetchall() if row[0])
majors.update({"布料", "辅料", "其他"})
self.add_major_category.blockSignals(True) self.add_major_category.blockSignals(True)
current_text = self.add_major_category.currentText()
self.add_major_category.clear() self.add_major_category.clear()
self.add_major_category.addItems(sorted(majors)) self.add_major_category.addItems(["面料", "辅料", "其他"])
self.add_major_category.setCurrentText("面料")
if current_text in majors:
self.add_major_category.setCurrentText(current_text)
else:
self.add_major_category.setCurrentText("布料")
self.add_major_category.blockSignals(False) self.add_major_category.blockSignals(False)
except:
self.add_major_category.clear()
self.add_major_category.addItems(["布料", "辅料", "其他"])
self.add_major_category.setCurrentText("布料")
def load_sub_categories(self): def load_sub_categories(self):
"""加载子类型""" """加载子类型"""
@@ -528,9 +531,12 @@ class RawMaterialLibraryDialog(QDialog):
self.supplier_combo.addItems(suppliers) self.supplier_combo.addItems(suppliers)
self.supplier_combo.blockSignals(False) self.supplier_combo.blockSignals(False)
# 新增原料界面的供应商下拉框,添加空选项作为默认值
self.add_supplier.blockSignals(True) self.add_supplier.blockSignals(True)
self.add_supplier.clear() self.add_supplier.clear()
self.add_supplier.addItem("") # 添加空选项
self.add_supplier.addItems(suppliers) self.add_supplier.addItems(suppliers)
self.add_supplier.setCurrentIndex(0) # 默认选中空选项
self.add_supplier.blockSignals(False) self.add_supplier.blockSignals(False)
except: except:
pass pass
@@ -647,7 +653,7 @@ class RawMaterialLibraryDialog(QDialog):
def delete_raw(self, model): def delete_raw(self, model):
"""逻辑删除原料""" """逻辑删除原料"""
reply = QMessageBox.question(self, "确认", f"确定要删除 '{model}' 吗?\n\n此操作将标记删除该原料,不会影响历史数据。") reply = QMessageBox.question(self, "确认", f"确定要删除 '{model}' 吗?")
if reply == QMessageBox.Yes: if reply == QMessageBox.Yes:
try: try:
with self.get_conn() as conn: with self.get_conn() as conn:
@@ -668,6 +674,27 @@ class RawMaterialLibraryDialog(QDialog):
major = self.add_major_category.currentText().strip() major = self.add_major_category.currentText().strip()
sub = self.add_sub_category.text().strip() sub = self.add_sub_category.text().strip()
# 验证必填字段:类目
if not major:
QMessageBox.warning(self, "错误", "请选择类目")
return
# 验证必填字段:类型
if not sub:
QMessageBox.warning(self, "错误", "请输入类型")
return
# 验证必填字段:幅宽
if self.add_width.value() <= 0:
QMessageBox.warning(self, "错误", "请输入幅宽")
return
# 验证必填字段:克重
if self.add_gsm.value() <= 0:
QMessageBox.warning(self, "错误", "请输入克重")
return
if "胸杯" in sub: if "胸杯" in sub:
major = "辅料" major = "辅料"
@@ -680,12 +707,32 @@ class RawMaterialLibraryDialog(QDialog):
try: try:
with self.get_conn() as conn: with self.get_conn() as conn:
# 检查是否已存在 # 检查是否已存在该型号
cursor = conn.execute("SELECT model FROM fabrics WHERE model = ?", (model,)) cursor = conn.execute("SELECT model, is_deleted FROM fabrics WHERE model = ?", (model,))
if cursor.fetchone(): existing = cursor.fetchone()
if existing:
_, is_deleted = existing
# 如果型号存在且未被删除,提示使用编辑功能
if not is_deleted or is_deleted == 0:
QMessageBox.warning(self, "错误", f"型号 '{model}' 已存在,请使用编辑功能修改") QMessageBox.warning(self, "错误", f"型号 '{model}' 已存在,请使用编辑功能修改")
return return
# 如果型号已被删除,直接恢复并更新
conn.execute('''
UPDATE fabrics
SET category=?, fabric_type=?, supplier=?, color=?, width=?, gsm=?,
retail_price=?, bulk_price=?, unit=?, timestamp=?, is_deleted=0, updated_at=CURRENT_TIMESTAMP
WHERE model=?
''', (category, fabric_type, supplier, color,
self.add_width.value() or None, self.add_gsm.value() or None,
self.add_retail.value() or None, self.add_bulk.value() or None,
unit, datetime.now().strftime('%Y-%m-%d %H:%M:%S'), model))
conn.commit()
QMessageBox.information(self, "成功", f"已保存 '{model}'")
else:
# 型号不存在,创建新记录
conn.execute(''' conn.execute('''
INSERT INTO fabrics INSERT INTO fabrics
(model, category, fabric_type, supplier, color, width, gsm, retail_price, bulk_price, unit, timestamp) (model, category, fabric_type, supplier, color, width, gsm, retail_price, bulk_price, unit, timestamp)
@@ -695,7 +742,6 @@ class RawMaterialLibraryDialog(QDialog):
self.add_retail.value() or None, self.add_bulk.value() or None, self.add_retail.value() or None, self.add_bulk.value() or None,
unit, datetime.now().strftime('%Y-%m-%d %H:%M:%S'))) unit, datetime.now().strftime('%Y-%m-%d %H:%M:%S')))
conn.commit() conn.commit()
QMessageBox.information(self, "成功", f"已保存 '{model}'") QMessageBox.information(self, "成功", f"已保存 '{model}'")
# 清空表单 # 清空表单

3
requirements.txt Normal file
View File

@@ -0,0 +1,3 @@
PyQt5>=5.15.0
pyinstaller>=5.0.0

30
打包.bat Normal file
View File

@@ -0,0 +1,30 @@
@echo off
chcp 65001 >nul
echo ========================================
echo 服装布料计算管理器 - 打包脚本
echo ========================================
echo.
echo 正在检查依赖...
python -c "import PyQt5" 2>nul
if errorlevel 1 (
echo 错误: 未安装PyQt5正在安装...
pip install -r requirements.txt
if errorlevel 1 (
echo 安装失败,请手动执行: pip install -r requirements.txt
pause
exit /b 1
)
)
echo.
echo 开始打包...
python build_exe.py
echo.
echo ========================================
echo 打包完成!
echo exe文件位置: dist\服装布料计算管理器.exe
echo ========================================
pause

85
打包说明.md Normal file
View File

@@ -0,0 +1,85 @@
# 打包为EXE文件说明
## 前置要求
1. 确保已安装Python 3.7或更高版本
2. 安装项目依赖:
```bash
pip install -r requirements.txt
```
## 打包方法
### 方法一:使用打包脚本(推荐)
直接运行打包脚本:
```bash
python build_exe.py
```
### 方法二使用PyInstaller命令
使用spec文件打包
```bash
pyinstaller build_exe.spec
```
或者直接使用命令行:
```bash
pyinstaller --name=服装布料计算管理器 --onefile --windowed --add-data=fabric_library.db;. main.py
```
### 方法三使用spec文件推荐用于自定义配置
```bash
pyinstaller build_exe.spec
```
## 打包输出
打包完成后生成的exe文件位于 `dist` 目录中:
- `dist/服装布料计算管理器.exe`
## 注意事项
1. **数据库文件**:如果项目中有 `fabric_library.db` 文件打包脚本会自动将其包含在exe同目录下。首次运行exe时如果数据库不存在程序会自动创建。
2. **文件大小**打包后的exe文件可能较大通常50-100MB这是因为包含了Python解释器和所有依赖库。
3. **杀毒软件**某些杀毒软件可能会误报这是正常现象。PyInstaller打包的exe文件需要添加白名单。
4. **依赖库**确保所有依赖都已正确安装特别是PyQt5。
5. **测试**打包完成后建议在干净的Windows系统上测试exe文件是否能正常运行。
## 常见问题
### 问题1打包失败提示找不到模块
**解决方案**:在 `build_exe.spec` 的 `hiddenimports` 中添加缺失的模块。
### 问题2exe运行时缺少DLL文件
**解决方案**确保PyQt5已正确安装可以尝试重新安装
```bash
pip uninstall PyQt5
pip install PyQt5
```
### 问题3exe文件太大
**解决方案**
- 使用 `--exclude-module` 排除不需要的模块
- 使用 `--onedir` 模式代替 `--onefile`会生成一个文件夹而不是单个exe
### 问题4数据库路径问题
**解决方案**程序已自动处理数据库路径exe运行时会在exe同目录下查找或创建数据库文件。
## 优化建议
1. **添加图标**:在 `build_exe.spec` 中设置 `icon='icon.ico'`需要准备一个ico格式的图标文件。
2. **减小体积**:如果不需要某些功能,可以排除相关模块:
```python
excludes=['matplotlib', 'numpy', 'pandas'] # 示例
```
3. **版本信息**:可以创建版本信息文件(.rc文件并添加到spec配置中。