用Pyside6 和sqlite3 重写了《电脑装配单》 加入切换主题 样式
图:
现在我们需要安装额外的依赖包:
pip install qt-material qdarkstyle
主要改动包括:
- 添加了菜单栏,包含主题切换菜单
- 提供了四种主题类型:
- 默认主题(系统原生样式)
- Fusion主题(亮色)
- Qt-Material主题(dark_teal、light_blue、dark_blue)
- QDarkStyle主题(暗色)
代码:
main.py
import sys
import os
from datetime import datetime
import tempfile
import webbrowser
from PySide6.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout,
QHBoxLayout, QLabel, QComboBox, QLineEdit,
QPushButton, QFrame, QScrollArea, QMessageBox,
QFileDialog, QDateEdit, QMenuBar, QMenu, QStyle,
QStyleFactory)
from PySide6.QtCore import Qt, Signal, QDate, QTimer
from PySide6.QtGui import QPalette, QColor
from database import Database
from dialogs import EditComponentDialog
from qt_material import apply_stylesheet, list_themes
import qdarkstyle
class ConfigRow(QWidget):
# 添加自定义信号
amount_changed = Signal()
def __init__(self, database, component_id, component_name, parent=None):
super().__init__(parent)
self.database = database
self.component_id = component_id
self.component_name = component_name
self.setup_ui()
def setup_ui(self):
layout = QHBoxLayout(self)
layout.setContentsMargins(5, 2, 5, 2)
# 配件名称
name_label = QLabel(self.component_name)
name_label.setFixedWidth(100)
layout.addWidget(name_label)
# 配置1
self.model1_combo = QComboBox()
self.model1_combo.setMinimumWidth(200)
self.model1_combo.currentIndexChanged.connect(self.update_price1)
layout.addWidget(self.model1_combo)
self.qty1_edit = QLineEdit('1')
self.qty1_edit.setFixedWidth(60)
self.qty1_edit.textChanged.connect(self.calculate_amount1)
layout.addWidget(self.qty1_edit)
self.price1_edit = QLineEdit('0')
self.price1_edit.setFixedWidth(80)
self.price1_edit.textChanged.connect(self.calculate_amount1)
layout.addWidget(self.price1_edit)
self.amount1_label = QLabel('0.00')
self.amount1_label.setFixedWidth(100)
layout.addWidget(self.amount1_label)
# 配置2
self.model2_combo = QComboBox()
self.model2_combo.setMinimumWidth(200)
self.model2_combo.currentIndexChanged.connect(self.update_price2)
layout.addWidget(self.model2_combo)
self.qty2_edit = QLineEdit('1')
self.qty2_edit.setFixedWidth(60)
self.qty2_edit.textChanged.connect(self.calculate_amount2)
layout.addWidget(self.qty2_edit)
self.price2_edit = QLineEdit('0')
self.price2_edit.setFixedWidth(80)
self.price2_edit.textChanged.connect(self.calculate_amount2)
layout.addWidget(self.price2_edit)
self.amount2_label = QLabel('0.00')
self.amount2_label.setFixedWidth(100)
layout.addWidget(self.amount2_label)
# 加载型号数据
self.load_models()
def load_models(self):
# 加载配置1的型号
self.model1_combo.clear()
self.model1_combo.addItem('', None) # 添加空选项
models1 = self.database.get_models(self.component_id, 'option1')
for model_id, model_name, price in models1:
self.model1_combo.addItem(model_name, (model_id, price))
# 加载配置2的型号
self.model2_combo.clear()
self.model2_combo.addItem('', None) # 添加空选项
models2 = self.database.get_models(self.component_id, 'option2')
for model_id, model_name, price in models2:
self.model2_combo.addItem(model_name, (model_id, price))
def update_price1(self, index):
data = self.model1_combo.currentData()
if data:
_, price = data
self.price1_edit.setText(str(price))
else:
self.price1_edit.setText('0')
def update_price2(self, index):
data = self.model2_combo.currentData()
if data:
_, price = data
self.price2_edit.setText(str(price))
else:
self.price2_edit.setText('0')
def calculate_amount1(self):
try:
qty = float(self.qty1_edit.text() or 0)
price = float(self.price1_edit.text() or 0)
amount = qty * price
self.amount1_label.setText(f'{amount:.2f}')
self.amount_changed.emit() # 发送信号
except ValueError:
self.amount1_label.setText('0.00')
self.amount_changed.emit() # 发送信号
def calculate_amount2(self):
try:
qty = float(self.qty2_edit.text() or 0)
price = float(self.price2_edit.text() or 0)
amount = qty * price
self.amount2_label.setText(f'{amount:.2f}')
self.amount_changed.emit() # 发送信号
except ValueError:
self.amount2_label.setText('0.00')
self.amount_changed.emit() # 发送信号
def get_data(self):
"""获取行数据"""
return {
'component_name': self.component_name,
'config1': {
'model': self.model1_combo.currentText(),
'qty': self.qty1_edit.text(),
'price': self.price1_edit.text(),
'amount': self.amount1_label.text()
},
'config2': {
'model': self.model2_combo.currentText(),
'qty': self.qty2_edit.text(),
'price': self.price2_edit.text(),
'amount': self.amount2_label.text()
}
}
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.database = Database()
self.current_theme = 'qt_material_dark_teal' # 默认主题
self.setup_menu() # 在setup_ui之前设置菜单
self.setup_ui()
def setup_menu(self):
"""设置菜单栏"""
menubar = self.menuBar()
# 主题菜单
theme_menu = menubar.addMenu('主题')
# 原始样式
default_action = theme_menu.addAction('默认主题')
default_action.triggered.connect(lambda: self.change_theme('default'))
# Fusion主题
fusion_light = theme_menu.addAction('Fusion主题')
fusion_light.triggered.connect(lambda: self.change_theme('fusion_light'))
# Qt-Material主题
qt_material_menu = theme_menu.addMenu('Qt-Material主题')
material_themes = ['dark_teal.xml', 'light_blue.xml', 'dark_blue.xml']
for theme in material_themes:
action = qt_material_menu.addAction(theme.replace('.xml', ''))
action.triggered.connect(lambda checked, t=theme: self.change_theme(f'qt_material_{t}'))
# QDarkStyle主题
dark_action = theme_menu.addAction('QDarkStyle暗色')
dark_action.triggered.connect(lambda: self.change_theme('qdarkstyle_dark'))
def change_theme(self, theme_name):
"""切换主题"""
self.current_theme = theme_name
app = QApplication.instance()
if theme_name == 'default':
# 恢复默认样式
app.setStyleSheet("")
app.setStyle('Windows')
elif theme_name.startswith('fusion'):
# 设置Fusion主题
app.setStyle('Fusion')
# 恢复亮色调色板
app.setPalette(app.style().standardPalette())
elif theme_name.startswith('qt_material'):
# 设置Qt-Material主题
theme_file = theme_name.replace('qt_material_', '')
apply_stylesheet(app, theme=theme_file)
elif theme_name.startswith('qdarkstyle'):
# 设置QDarkStyle主题
app.setStyleSheet(qdarkstyle.load_stylesheet(qt_api='pyside6'))
def setup_ui(self):
self.setWindowTitle('电脑配置单')
self.setMinimumSize(1200, 800)
# 创建中央部件
central_widget = QWidget()
self.setCentralWidget(central_widget)
layout = QVBoxLayout(central_widget)
# 标题
title_label = QLabel('电脑配置单')
title_label.setAlignment(Qt.AlignCenter)
layout.addWidget(title_label)
# 日期
date_layout = QHBoxLayout()
date_layout.addWidget(QLabel('日期:'))
# 创建日期选择器
self.date_edit = QDateEdit()
self.date_edit.setDisplayFormat('yyyy-MM-dd')
self.date_edit.setCalendarPopup(True) # 允许弹出日历选择
self.date_edit.setDate(QDate.currentDate()) # 设置当前日期
date_layout.addWidget(self.date_edit)
# 添加同步系统时间按钮
sync_button = QPushButton('同步系统时间')
sync_button.clicked.connect(self.sync_system_time)
date_layout.addWidget(sync_button)
date_layout.addStretch()
layout.addLayout(date_layout)
# 创建表头
header = QWidget()
header_layout = QHBoxLayout(header)
header_layout.setContentsMargins(5, 2, 5, 2)
labels = ['配件名称', '型号[1]', '数量[1]', '单价[1]', '金额[1]',
'型号[2]', '数量[2]', '单价[2]', '金额[2]']
widths = [100, 200, 60, 80, 100, 200, 60, 80, 100]
for label, width in zip(labels, widths):
lbl = QLabel(label)
lbl.setFixedWidth(width)
header_layout.addWidget(lbl)
layout.addWidget(header)
# 创建滚动区域
scroll = QScrollArea()
scroll.setWidgetResizable(True)
scroll.setFrameShape(QFrame.NoFrame)
layout.addWidget(scroll)
# 创建配置行容器
self.rows_widget = QWidget()
self.rows_layout = QVBoxLayout(self.rows_widget)
scroll.setWidget(self.rows_widget)
# 创建配置行
self.config_rows = []
components = self.database.get_components()
for component_id, component_name in components:
row = ConfigRow(self.database, component_id, component_name)
self.rows_layout.addWidget(row)
self.config_rows.append(row)
# 创建合计区域
total_frame = QFrame()
total_frame.setFrameShape(QFrame.Box)
total_layout = QHBoxLayout(total_frame)
# 配置1合计
total1_layout = QVBoxLayout()
total1_layout.addWidget(QLabel('配置1合计:'))
self.total1_label = QLabel('0.00')
total1_layout.addWidget(self.total1_label)
total_layout.addLayout(total1_layout)
# 配置2合计
total2_layout = QVBoxLayout()
total2_layout.addWidget(QLabel('配置2合计:'))
self.total2_label = QLabel('0.00')
total2_layout.addWidget(self.total2_label)
total_layout.addLayout(total2_layout)
layout.addWidget(total_frame)
# 创建按钮区域
button_layout = QHBoxLayout()
export_button = QPushButton('导出配置')
export_button.clicked.connect(self.export_config)
print_button = QPushButton('打印配置')
print_button.clicked.connect(self.print_config)
edit_button = QPushButton('编辑配件数据')
edit_button.clicked.connect(self.edit_components_data)
button_layout.addWidget(export_button)
button_layout.addWidget(print_button)
button_layout.addWidget(edit_button)
layout.addLayout(button_layout)
# 设置定时器更新合计
for row in self.config_rows:
row.amount_changed.connect(self.update_totals) # 连接新的信号
def update_totals(self):
"""更新合计金额"""
total1 = 0
total2 = 0
for row in self.config_rows:
try:
total1 += float(row.amount1_label.text())
total2 += float(row.amount2_label.text())
except ValueError:
continue
self.total1_label.setText(f'{total1:.2f}')
self.total2_label.setText(f'{total2:.2f}')
def sync_system_time(self):
"""同步系统时间"""
self.date_edit.setDate(QDate.currentDate())
def export_config(self):
"""导出配置到Excel文件"""
try:
import openpyxl
from openpyxl.styles import Alignment, Font, Border, Side
# 创建工作簿
wb = openpyxl.Workbook()
ws = wb.active
ws.title = "电脑配置单"
# 设置列宽
ws.column_dimensions['A'].width = 15
ws.column_dimensions['B'].width = 30
ws.column_dimensions['C'].width = 8
ws.column_dimensions['D'].width = 10
ws.column_dimensions['E'].width = 12
ws.column_dimensions['F'].width = 30
ws.column_dimensions['G'].width = 8
ws.column_dimensions['H'].width = 10
ws.column_dimensions['I'].width = 12
# 写入标题
ws['A1'] = "电脑配置单"
ws.merge_cells('A1:I1')
ws['A1'].font = Font(size=14, bold=True)
ws['A1'].alignment = Alignment(horizontal='center')
# 修改日期获取方式
ws['A2'] = f"日期:{self.date_edit.date().toString('yyyy-MM-dd')}"
ws.merge_cells('A2:I2')
# 写入表头
headers = ['配件名称', '型号[1]', '数量[1]', '单价[1]', '金额[1]',
'型号[2]', '数量[2]', '单价[2]', '金额[2]']
for col, header in enumerate(headers, 1):
cell = ws.cell(row=3, column=col)
cell.value = header
cell.font = Font(bold=True)
cell.alignment = Alignment(horizontal='center')
# 写入数据
for row_idx, config_row in enumerate(self.config_rows, 4):
data = config_row.get_data()
ws.cell(row=row_idx, column=1).value = data['component_name']
ws.cell(row=row_idx, column=2).value = data['config1']['model']
ws.cell(row=row_idx, column=3).value = data['config1']['qty']
ws.cell(row=row_idx, column=4).value = data['config1']['price']
ws.cell(row=row_idx, column=5).value = data['config1']['amount']
ws.cell(row=row_idx, column=6).value = data['config2']['model']
ws.cell(row=row_idx, column=7).value = data['config2']['qty']
ws.cell(row=row_idx, column=8).value = data['config2']['price']
ws.cell(row=row_idx, column=9).value = data['config2']['amount']
# 写入合计
total_row = len(self.config_rows) + 4
ws.cell(row=total_row, column=1).value = "合计"
ws.cell(row=total_row, column=5).value = f"配置1:{self.total1_label.text()}"
ws.cell(row=total_row, column=9).value = f"配置2:{self.total2_label.text()}"
# 设置边框
thin_border = Border(
left=Side(style='thin'),
right=Side(style='thin'),
top=Side(style='thin'),
bottom=Side(style='thin')
)
for row in ws.iter_rows(min_row=3, max_row=total_row, min_col=1, max_col=9):
for cell in row:
cell.border = thin_border
cell.alignment = Alignment(horizontal='center')
# 保存文件
file_path, _ = QFileDialog.getSaveFileName(
self,
"保存配置单",
f"电脑配置单_{datetime.now().strftime('%Y%m%d_%H%M%S')}.xlsx",
"Excel Files (*.xlsx)"
)
if file_path:
wb.save(file_path)
QMessageBox.information(self, "成功", "配置已成功导出到Excel文件!")
except Exception as e:
QMessageBox.critical(self, "错误", f"导出失败:{str(e)}")
def print_config(self):
"""生成打印预览HTML并在浏览器中打开"""
try:
html_content = f"""
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>电脑配置单</title>
<style>
body {{ font-family: Arial, sans-serif; margin: 20px; }}
table {{ width: 100%; border-collapse: collapse; margin-top: 20px; }}
th, td {{ border: 1px solid black; padding: 8px; text-align: center; }}
th {{ background-color: #f2f2f2; }}
.total-row {{ background-color: #f9f9f9; font-weight: bold; }}
@media print {{
button {{ display: none; }}
}}
</style>
</head>
<body>
<h2 style="text-align: center;">电脑配置单</h2>
<p>日期:{self.date_edit.date().toString('yyyy-MM-dd')}</p>
<table>
<tr>
<th>配件名称</th>
<th>型号[1]</th>
<th>数量[1]</th>
<th>单价[1]</th>
<th>金额[1]</th>
<th>型号[2]</th>
<th>数量[2]</th>
<th>单价[2]</th>
<th>金额[2]</th>
</tr>
"""
# 添加数据行
for row in self.config_rows:
data = row.get_data()
html_content += f"""
<tr>
<td>{data['component_name']}</td>
<td>{data['config1']['model']}</td>
<td>{data['config1']['qty']}</td>
<td>{data['config1']['price']}</td>
<td>{data['config1']['amount']}</td>
<td>{data['config2']['model']}</td>
<td>{data['config2']['qty']}</td>
<td>{data['config2']['price']}</td>
<td>{data['config2']['amount']}</td>
</tr>
"""
# 添加合计行
html_content += f"""
<tr class="total-row">
<td>合计</td>
<td colspan="3"></td>
<td>配置1:{self.total1_label.text()}</td>
<td colspan="3"></td>
<td>配置2:{self.total2_label.text()}</td>
</tr>
</table>
<button onclick="window.print()" style="margin-top: 20px;">打印</button>
</body>
</html>
"""
# 创建临时HTML文件
with tempfile.NamedTemporaryFile('w', delete=False, suffix='.html', encoding='utf-8') as f:
f.write(html_content)
temp_path = f.name
# 在浏览器中打开
webbrowser.open('file://' + temp_path)
except Exception as e:
QMessageBox.critical(self, "错误", f"打印预览失败:{str(e)}")
def edit_components_data(self):
"""打开编辑配件数据对话框"""
dialog = EditComponentDialog(self.database, self)
if dialog.exec_():
# 重新加载所有配置行的型号数据
for row in self.config_rows:
row.load_models()
if __name__ == '__main__':
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec())
dialogs.py
from datetime import datetime
import openpyxl
from openpyxl.styles import Font, Alignment, Border, Side
from PySide6.QtWidgets import *
from PySide6.QtCore import Qt
from PySide6.QtGui import QIntValidator, QDoubleValidator
class ComponentDialog(QDialog):
def __init__(self, db, component=None, parent=None):
super().__init__(parent)
self.db = db
self.component = component
self.setupUi()
if component:
self.load_data()
class CustomerDialog(QDialog):
def __init__(self, db, customer=None, parent=None):
super().__init__(parent)
self.db = db
self.customer = customer
self.setupUi()
if customer:
self.load_data()
class QuotationDialog(QDialog):
def __init__(self, db, quotation_id=None, parent=None):
super().__init__(parent)
self.db = db
self.quotation_id = quotation_id
self.details = [] # 初始化明细列表
self.setupUi()
if quotation_id:
self.load_quotation()
def export_to_excel(self):
"""导出报价单到Excel"""
try:
# 检查是否有报价单ID
if not hasattr(self, 'quotation_id') or not self.quotation_id:
# 如果是新建的报价单,先保存
self.save_quotation()
if not hasattr(self, 'quotation_id') or not self.quotation_id:
raise Exception("请先保存报价单")
# 获取报价单数据
quotation = self.db.get_quotation(self.quotation_id)
if not quotation:
raise Exception("报价单不存在")
print(f"导出报价单数据: {quotation}")
# 获取报价单明细
details = self.db.get_quotation_details(self.quotation_id)
if not details:
raise Exception("报价单没有明细数据")
print(f"导出报价单明细: {details}")
# 创建Excel工作簿
wb = openpyxl.Workbook()
ws = wb.active
ws.title = "报价单"
# 设置列宽
ws.column_dimensions['A'].width = 15
ws.column_dimensions['B'].width = 40
ws.column_dimensions['C'].width = 10
ws.column_dimensions['D'].width = 15
ws.column_dimensions['E'].width = 15
# 标题样式
title_font = Font(name='宋体', size=14, bold=True)
header_font = Font(name='宋体', size=11, bold=True)
normal_font = Font(name='宋体', size=11)
# 对齐方式
center_align = Alignment(horizontal='center', vertical='center')
right_align = Alignment(horizontal='right', vertical='center')
# 边框样式
thin_border = Border(
left=Side(style='thin'),
right=Side(style='thin'),
top=Side(style='thin'),
bottom=Side(style='thin')
)
# 写入标题
ws.merge_cells('A1:E1')
ws['A1'] = "报价单"
ws['A1'].font = title_font
ws['A1'].alignment = center_align
# 写入基本信息
customer_name = quotation[8] if len(quotation) > 8 and quotation[8] else "未知客户"
quotation_date = quotation[2] if len(quotation) > 2 and quotation[2] else datetime.now().strftime("%Y-%m-%d")
valid_days = quotation[4] if len(quotation) > 4 and quotation[4] else 7
status = quotation[6] if len(quotation) > 6 and quotation[6] else "待确认"
notes = quotation[5] if len(quotation) > 5 and quotation[5] else ""
ws['A2'] = "客户名称:"
ws['B2'] = customer_name
ws['D2'] = "报价日期:"
ws['E2'] = quotation_date
ws['A3'] = "有效期:"
ws['B3'] = f"{valid_days}天"
ws['D3'] = "状态:"
ws['E3'] = status
# 写入表头
headers = ['序号', '配件名称', '数量', '单价', '小计']
for col, header in enumerate(headers, 1):
cell = ws.cell(row=4, column=col)
cell.value = header
cell.font = header_font
cell.alignment = center_align
cell.border = thin_border
# 写入明细
row = 5
total_amount = 0
for i, detail in enumerate(details, 1):
try:
component_name = detail[7] if len(detail) > 7 and detail[7] else "未知配件"
quantity = int(detail[3]) if len(detail) > 3 and detail[3] is not None else 0
unit_price = float(detail[4]) if len(detail) > 4 and detail[4] is not None else 0.0
subtotal = float(detail[5]) if len(detail) > 5 and detail[5] is not None else 0.0
ws.cell(row=row, column=1, value=i).alignment = center_align
ws.cell(row=row, column=2, value=component_name)
ws.cell(row=row, column=3, value=quantity).alignment = center_align
ws.cell(row=row, column=4, value=f"¥{unit_price:.2f}").alignment = right_align
ws.cell(row=row, column=5, value=f"¥{subtotal:.2f}").alignment = right_align
# 添加边框
for col in range(1, 6):
ws.cell(row=row, column=col).border = thin_border
ws.cell(row=row, column=col).font = normal_font
total_amount += subtotal
row += 1
except Exception as e:
print(f"处理明细行时出错: {str(e)}, detail={detail}")
continue
# 写入合计
ws.merge_cells(f'A{row}:D{row}')
ws.cell(row=row, column=1, value="合计:").alignment = right_align
ws.cell(row=row, column=5, value=f"¥{total_amount:.2f}").alignment = right_align
# 写入备注
if notes:
row += 1
ws.merge_cells(f'A{row}:E{row}')
ws[f'A{row}'] = f"备注:{notes}"
# 保存文件
filename = f"报价单_{datetime.now().strftime('%Y%m%d%H%M%S')}.xlsx"
wb.save(filename)
QMessageBox.information(self, "成功", f"报价单已导出到:{filename}")
except Exception as e:
print(f"导出报价单失败: {str(e)}")
QMessageBox.warning(self, "错误", f"导出报价单失败: {str(e)}")
class SupplierDialog(QDialog):
def __init__(self, db, supplier=None, parent=None):
super().__init__(parent)
self.db = db
self.supplier = supplier
self.setupUi()
if supplier:
self.load_data()
class EditComponentDialog(QDialog):
def __init__(self, database, parent=None):
super().__init__(parent)
self.database = database
self.setup_ui()
self.load_data()
def setup_ui(self):
"""设置UI界面"""
self.setWindowTitle('编辑配件数据')
self.setMinimumSize(800, 600)
# 创建主布局
layout = QVBoxLayout(self)
# 创建树形视图
self.tree = QTreeWidget()
self.tree.setHeaderLabels(['型号', '价格', '配置方案'])
self.tree.itemSelectionChanged.connect(self.on_selection_changed)
layout.addWidget(self.tree)
# 创建编辑区域
edit_layout = QVBoxLayout()
# 配件选择
part_layout = QHBoxLayout()
part_layout.addWidget(QLabel('配件:'))
self.part_combo = QComboBox()
self.part_combo.currentIndexChanged.connect(self.on_part_changed)
part_layout.addWidget(self.part_combo)
edit_layout.addLayout(part_layout)
# 配置方案选择
option_layout = QHBoxLayout()
option_layout.addWidget(QLabel('配置方案:'))
self.option1_radio = QRadioButton('配置1')
self.option2_radio = QRadioButton('配置2')
self.option1_radio.setChecked(True)
option_layout.addWidget(self.option1_radio)
option_layout.addWidget(self.option2_radio)
edit_layout.addLayout(option_layout)
# 型号和价格输入
input_layout = QHBoxLayout()
input_layout.addWidget(QLabel('型号:'))
self.model_edit = QLineEdit()
input_layout.addWidget(self.model_edit)
input_layout.addWidget(QLabel('价格:'))
self.price_edit = QLineEdit()
input_layout.addWidget(self.price_edit)
edit_layout.addLayout(input_layout)
# 按钮
button_layout = QHBoxLayout()
add_button = QPushButton('添加/更新')
add_button.clicked.connect(self.add_item)
delete_button = QPushButton('删除')
delete_button.clicked.connect(self.delete_item)
button_layout.addWidget(add_button)
button_layout.addWidget(delete_button)
edit_layout.addLayout(button_layout)
layout.addLayout(edit_layout)
def load_data(self):
"""加载数据"""
# 加载配件列表
components = self.database.get_components()
self.part_combo.clear()
for component_id, name in components:
self.part_combo.addItem(name, component_id)
self.update_tree()
def update_tree(self):
"""更新树形视图"""
self.tree.clear()
component_id = self.part_combo.currentData()
if component_id is None:
return
# 获取配置1的型号
models1 = self.database.get_models(component_id, 'option1')
for model_id, model_name, price in models1:
item = QTreeWidgetItem([model_name, str(price), '配置1'])
item.setData(0, Qt.UserRole, model_id)
self.tree.addTopLevelItem(item)
# 获取配置2的型号
models2 = self.database.get_models(component_id, 'option2')
for model_id, model_name, price in models2:
item = QTreeWidgetItem([model_name, str(price), '配置2'])
item.setData(0, Qt.UserRole, model_id)
self.tree.addTopLevelItem(item)
def on_part_changed(self, index):
"""配件选择改变时的处理"""
self.update_tree()
def on_selection_changed(self):
"""选择项改变时的处理"""
items = self.tree.selectedItems()
if items:
item = items[0]
self.model_edit.setText(item.text(0))
self.price_edit.setText(item.text(1))
if item.text(2) == '配置1':
self.option1_radio.setChecked(True)
else:
self.option2_radio.setChecked(True)
def add_item(self):
"""添加或更新项目"""
try:
component_id = self.part_combo.currentData()
model_name = self.model_edit.text().strip()
price = float(self.price_edit.text())
option_type = 'option1' if self.option1_radio.isChecked() else 'option2'
if not model_name:
QMessageBox.warning(self, '警告', '请输入型号')
return
self.database.add_model(component_id, model_name, price, option_type)
self.update_tree()
self.model_edit.clear()
self.price_edit.clear()
except ValueError:
QMessageBox.warning(self, '错误', '价格必须是数字')
def delete_item(self):
"""删除项目"""
items = self.tree.selectedItems()
if not items:
QMessageBox.warning(self, '警告', '请选择要删除的项目')
return
if QMessageBox.question(self, '确认', '确定要删除选中的项目吗?') == QMessageBox.Yes:
for item in items:
model_id = item.data(0, Qt.UserRole)
self.database.delete_model(model_id)
self.update_tree()
database.py
import sqlite3
from datetime import datetime
import os
class Database:
def __init__(self):
self.db_file = os.path.join(os.path.dirname(__file__), 'computer_config.db')
self.init_database()
def init_database(self):
"""初始化数据库表"""
with sqlite3.connect(self.db_file) as conn:
cursor = conn.cursor()
# 创建配件类型表
cursor.execute('''
CREATE TABLE IF NOT EXISTS components (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL UNIQUE
)
''')
# 创建配件型号表
cursor.execute('''
CREATE TABLE IF NOT EXISTS models (
id INTEGER PRIMARY KEY AUTOINCREMENT,
component_id INTEGER,
model_name TEXT NOT NULL,
price REAL NOT NULL,
option_type TEXT NOT NULL,
FOREIGN KEY (component_id) REFERENCES components (id),
UNIQUE(component_id, model_name, option_type)
)
''')
# 初始化基础配件数据
base_components = [
'CPU', '主板', '内存', '硬盘', 'SSD固态盘', '显卡',
'机箱', '电源', '显示器', '键鼠套装', '键盘',
'鼠标', '散热器', '音箱', '光存储'
]
for component in base_components:
cursor.execute('INSERT OR IGNORE INTO components (name) VALUES (?)', (component,))
conn.commit()
def get_components(self):
"""获取所有配件类型"""
with sqlite3.connect(self.db_file) as conn:
cursor = conn.cursor()
cursor.execute('SELECT id, name FROM components')
return cursor.fetchall()
def get_models(self, component_id, option_type):
"""获取指定配件的型号列表"""
with sqlite3.connect(self.db_file) as conn:
cursor = conn.cursor()
cursor.execute('''
SELECT id, model_name, price
FROM models
WHERE component_id = ? AND option_type = ?
''', (component_id, option_type))
return cursor.fetchall()
def add_model(self, component_id, model_name, price, option_type):
"""添加或更新配件型号"""
with sqlite3.connect(self.db_file) as conn:
cursor = conn.cursor()
cursor.execute('''
INSERT OR REPLACE INTO models (component_id, model_name, price, option_type)
VALUES (?, ?, ?, ?)
''', (component_id, model_name, price, option_type))
conn.commit()
def delete_model(self, model_id):
"""删除配件型号"""
with sqlite3.connect(self.db_file) as conn:
cursor = conn.cursor()
cursor.execute('DELETE FROM models WHERE id = ?', (model_id,))
conn.commit()
def update_model_price(self, model_id, new_price):
"""更新配件型号价格"""
with sqlite3.connect(self.db_file) as conn:
cursor = conn.cursor()
cursor.execute('UPDATE models SET price = ? WHERE id = ?', (new_price, model_id))
conn.commit()