用Pyside6 和sqlite3 重写了《电脑装配单》 加入切换主题 样式

图:

现在我们需要安装额外的依赖包:

pip install qt-material qdarkstyle

主要改动包括:

  • 添加了菜单栏,包含主题切换菜单
  • 提供了四种主题类型:
  • 默认主题(系统原生样式)
  • Fusion主题(亮色)
  • Qt-Material主题(dark_teal、light_blue、dark_blue)
  • QDarkStyle主题(暗色)

代码:

main.py

python 复制代码
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

python 复制代码
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

python 复制代码
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() 

End

相关推荐
西岭千秋雪_4 分钟前
MySQL数据结构选择
数据结构·数据库·mysql
Code成立19 分钟前
《Java核心技术II》简单约简
java·开发语言·python
辣椒酱.25 分钟前
neo4j学习笔记
python·neo4j
V+zmm101341 小时前
基于微信小程序投票评选系统的设计与实现ssm+论文源码调试讲解
java·数据库·微信小程序·小程序·毕业设计·ssm
lennon_jlu1 小时前
1.4 java反射机制 简单的java反射机制实践
java·开发语言·python
熟透的蜗牛1 小时前
大数据技术(八)—— HBase数据读写流程和Api的使用
大数据·数据库·hbase
PersistJiao1 小时前
Couchbase 的 OLAP 能力现状以及提升 OLAP 能力的方法
数据库·couchbase
对,就是哥1 小时前
ABAP弹出对对话框错误信息设计
java·数据库·算法
Minxinbb1 小时前
MySQL DBA需要掌握的 7 个问题
数据库·mysql·dba
hakesashou1 小时前
在vscode中编写Python的详细步骤
ide·vscode·python