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

Port设置功能开发实践: Pyside6 MVC架构与Model/View/Delegate模式的应用

Port设置功能开发实践: Pyside6 MVC架构与Model/View/Delegate模式的应用

前言

我们平时在进行GUI应用开发中,如何优雅地处理复杂的数据展示和交互逻辑是一个挑战。本文将通过一个实际的Port设置功能开发案例,深入探讨MVC架构模式在PySide6中的应用实践。

案例

示例效果1
在这里插入图片描述
示例效果2

在这里插入图片描述

我们需要开发一个Port设置功能,支持两种类型的Port生成:

  • Net-Based Port:基于网络的集总端口
  • Pin-Based Port:基于引脚的端口

功能需求包括:

  • 组件选择和管理
  • Port参数配置(参考类型、阻抗值、目标层等)
  • Port列表的增删改查
  • 数据验证和错误处理

架构设计思路

1. 为什么选择MVC架构?

传统的GUI开发往往将数据处理、界面展示和业务逻辑混杂在一起,导致代码难以维护和测试。MVC架构通过分离关注点,带来以下优势:

  • 可维护性:各层职责清晰,修改某一层不会影响其他层**(代码解耦,便于排查Bug)**
  • 可测试性:业务逻辑与UI分离,便于单元测试
  • 可扩展性:新增功能时只需修改对应层级
  • 代码复用:Model层可以被多个View复用

2. Model/View/Delegate模式的优势

Qt的Model/View/Delegate模式是MVC的变种,特别适合处理表格数据:

  • Model:管理数据和业务逻辑
  • View:负责数据展示
  • Delegate:处理数据编辑和自定义渲染

核心实现

Model层:数据管理的核心

from dataclasses import dataclass
import random@dataclass
class Port:id: inttype: strcomponent: strref_type: strref_z: floattarget_layer: str = ''@dataclass
class Component:name: strlayer: strnet: strpins: intclass PortModel:def __init__(self):self.ports = []self.components = self.load_components()self.layers = self.extract_layers()def load_components(self):# 从接口或数据库加载组件数据return [Component(f'Comp{random.randint(1,100)}', f'Layer{random.randint(1,5)}', f'Net{random.randint(1,10)}', random.randint(1,10))for _ in range(20)]def extract_layers(self):return sorted(set(comp.layer for comp in self.components))def generate_lumped_port(self, selected_components, ref_type, ref_z, target_layer):for comp in selected_components:port = Port(len(self.ports) + 1, 'Lumped', comp.name, ref_type, ref_z, target_layer)self.ports.append(port)def generate_pin_based_port(self, selected_components, ref_type, ref_z):for comp in selected_components:port = Port(len(self.ports) + 1, 'Pin-Based', comp.name, ref_type, ref_z)self.ports.append(port)

设计目的:

  • 使用@dataclass简化数据类定义
  • 将数据加载逻辑封装在Model中
  • 提供清晰的业务方法接口

View层:Model/View/Delegate的精妙应用

自定义TableModel
class PortTableModel(QAbstractTableModel):def __init__(self, ports=None):super().__init__()self.ports = ports or []self.headers = ['ID', 'Type', 'Component', 'Ref Type', 'Ref Z(ohm)', 'Target Layer']def rowCount(self, parent=QModelIndex()):return len(self.ports)def columnCount(self, parent=QModelIndex()):return len(self.headers)def data(self, index, role=Qt.DisplayRole):if role == Qt.DisplayRole:port = self.ports[index.row()]if index.column() == 0: return port.idelif index.column() == 1: return port.typeelif index.column() == 2: return port.componentelif index.column() == 3: return port.ref_typeelif index.column() == 4: return f'{port.ref_z:.2f}'elif index.column() == 5: return port.target_layerreturn Nonedef setData(self, index, value, role=Qt.EditRole):if role == Qt.EditRole and index.column() == 4:self.ports[index.row()].ref_z = float(value)self.dataChanged.emit(index, index)return Truereturn Falsedef flags(self, index):flags = super().flags(index)if index.column() == 4:  # Ref Z列可编辑flags |= Qt.ItemIsEditablereturn flags
自定义Delegate
class RefZDelegate(QStyledItemDelegate):def createEditor(self, parent, option, index):editor = QDoubleSpinBox(parent)editor.setMinimum(0.0)editor.setMaximum(1000.0)editor.setDecimals(2)return editordef setEditorData(self, editor, index):value = index.model().data(index, Qt.DisplayRole)editor.setValue(float(value))def setModelData(self, editor, model, index):value = editor.value()model.setData(index, value, Qt.EditRole)

技术目的:

  • 继承QAbstractTableModel实现自定义数据模型
  • 通过flags()方法控制单元格的可编辑性
  • 使用QStyledItemDelegate提供专业的数值编辑体验
  • 信号机制确保数据变更的实时响应

ViewModel层:优雅的事件处理

class PortViewModel:def __init__(self, model, view):self.model = modelself.view = viewself.connect_signals()def connect_signals(self):self.view.generate_lumped_btn.clicked.connect(self.generate_lumped)self.view.generate_pin_based_btn.clicked.connect(self.generate_pin_based)self.view.delete_btn.clicked.connect(self.delete_selected_port)self.view.check_ports_btn.clicked.connect(self.check_ports)def generate_lumped(self):# 从视图获取选中的组件selected = self.view.net_comp_table.selectionModel().selectedRows()if not selected:QMessageBox.warning(self.view, 'Warning', 'Please select at least one component.')returnselected_components = [self.view.net_comp_table.model().components[idx.row()] for idx in selected]# 获取参考类型ref_type = 'Ground' if self.view.ref_ground.isChecked() else 'Plane' if self.view.ref_plane.isChecked() else Noneif not ref_type:QMessageBox.warning(self.view, 'Warning', 'Please select a reference type.')return# 获取目标层target_layer = self.view.target_layer_combo.currentText()if not target_layer:QMessageBox.warning(self.view, 'Warning', 'Please select a target layer.')return# 调用模型方法生成Portref_z = 50.0  # 默认阻抗值self.model.generate_lumped_port(selected_components, ref_type, ref_z, target_layer)self.view.update_port_table()

设计目的:

  • ViewModel不直接操作数据,而是协调Model和View
  • 完整的输入验证和用户友好的错误提示
  • 通过信号槽机制实现松耦合

架构优势的体现

1. 数据与UI的彻底分离

# 数据更新时,UI自动响应
def update_port_table(self):self.port_table.model().layoutChanged.emit()

通过Qt的信号机制,数据变更会自动触发UI更新,无需手动同步。

2. 高度的可扩展性

想要添加新的Port类型?只需:

  • 在Model中添加新的生成方法
  • 在View中添加对应的UI元素
  • 在ViewModel中连接新的信号
http://www.dtcms.com/a/524768.html

相关文章:

  • 白之家低成本做网站深圳比较好的建网站公司
  • 深度学习一些知识点(指标+正则化)
  • 企业官方网站建设的作用仿牌 镜像网站
  • java实现多线程分片下载超大文件,支持HTTPS。
  • 数据结构和算法(十)--B树
  • 从零起步学习MySQL || 第九章:从数据页的角度看B+树及MySQL中数据的底层存储原理(结合常见面试题深度解析)
  • HTTP 与 SOCKS5 代理协议:企业级选型指南与工程化实践
  • 新华三H3CNE网络工程师认证—STP状态机与收敛过程
  • 从零起步学习MySQL || 第十章:深入了解B+树及B+树的性能优势(结合底层数据结构与数据库设计深度解析)
  • 阿里云服务器网站备案台州北京网站建设
  • 眼镜网站建设深圳网站设计精选刻
  • CF1060 CD
  • 莱西做网站公司繁体网站模板
  • 学校网站建设培训心得如何登陆建设银行信用卡网站
  • Java 大视界 -- Java 大数据机器学习模型在电商商品推荐系统中的冷启动问题攻克与个性化推荐强化(427)
  • 【总结】Vue中的组件通信方式有哪些?React中的组件通信方式有哪些?
  • 外贸网站优化推广手工制作大全折纸
  • 华为一站式服务建站视觉设计案例
  • 前端大文件分片上传
  • webrtc代码走读(四)-QOS-NACK实现-发送端
  • 主成分分析(PCA)在计算机图形学中的深入解析与应用
  • Kubernetes:实战Pod共享存储
  • 合肥市建设工程造价管理站网站ps网站背景图片怎么做
  • 5118网站是免费的吗网站如何防止重登录
  • 网络编程实战02·从零搭建Epoll服务器
  • IP数据报分片 题
  • 杭州设计 公司 网站建设适合小企业的erp软件
  • 全面掌握PostgreSQL关系型数据库,创建用户创建数据库操作,笔记09
  • 西安市网站制作公司购物商城排名
  • 思维大反转——往内走如实觉察