当前位置: 首页 > article >正文

用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() 

End


http://www.kler.cn/a/460735.html

相关文章:

  • 今日复盘103周五(189)
  • Git的使用流程(详细教程)
  • uni-app 多平台分享实现指南
  • C++11右值与列表初始化
  • IM系统在体育直播网站中的重要性
  • AWS re:Invent 2024 - Dr. Werner Vogels 主题演讲
  • 构建一个rust生产应用读书笔记7-确认邮件3
  • 【信息系统项目管理师】高分论文:论信息系统项目的沟通管理(不动产登记系统)
  • Python世界:人生苦短,我用Python
  • 一文讲清楚CSS3新特性
  • Hessian 矩阵与函数的凸性
  • 网络渗透测试实验二:网络嗅探与身份认证
  • 从零到一:构建高效、安全的电商数据API接口
  • Leetcode 从中序与后序遍历序列构造二叉树
  • Rocky Linux 下安装Liboffice
  • 计算机网络 (17)点对点协议PPP
  • Android音频效果处理:基于`android.media.audiofx`包的原理、架构与实现
  • WPF中的Microsoft XAML Behaviors包功能详解
  • 基于 Spring AI 孵化一款 AI 产品
  • 踩坑之服务器时间和本地时间相差8小时
  • C#语言的软件工程
  • 2024年工作总结
  • 2024年6月英语六级CET6写作与翻译笔记
  • UE5 把场景转成HDR图
  • 018-spring-基于aop的事务控制
  • 陕西图纸文档加密软件是如何对图纸进行防泄密保护的呢?