风月流沙 發表於 2025-10-22 11:00:00

在PySide6/PyQt6的开发框架中,增加对表格多种格式录入的处理,以及主从表的数据显示和保存操作。

<p>在PySide6/PyQt6的开发框架中, 为了方便对表格数据的快速录入,有时候包括多种录入的类型,包括文本框、数字格式(整数、小数)、日期、时间、下拉列表、自定义弹出框、单选框组、百分比、金额、颜色、图片、内置图标等多样化的处理需要,本篇就是基于此需求,在PySide6/PyQt6的开发框架中,增加对表格多种格式录入的处理,以及主从表的数据显示和保存操作。</p>
<p>PySide6/PyQt6的开发框架主界面支持多文档管理,可包括菜单栏、工具栏、内容区和状态栏等,内容区以选项卡方式展示多个窗口,如下所示。</p>
<p><img src="https://img2024.cnblogs.com/blog/8867/202510/8867-20251022104625479-786196249.png" alt="image"></p>
<p>常规的编辑界面如用户界面,双击列表弹出展示,如下所示。</p>
<p><img src="https://img2024.cnblogs.com/blog/8867/202510/8867-20251022104911242-1792567658.png" alt="image"></p>
<p>主从表展示界面如下所示。</p>
<p><img src="https://img2024.cnblogs.com/blog/8867/202510/8867-20251022105040865-36196502.png" alt="image"></p>
<p>主从表直接编辑界面,在弹出对话框中进行编辑主表信息和明细记录,明细记录通过表格方式直接录入,方便各种类型的数据录入处理。</p>
<p>&nbsp;</p>
<h3>1、在PySide6/PyQt6的开发框架中表格的数据录入</h3>
<p>我们先来看看测试案例,查看下表格中多种格式录入的效果</p>
<p><img src="https://img2024.cnblogs.com/blog/8867/202510/8867-20251021214324515-79686897.png" alt="image"></p>
<p>视频效果如下所示</p>
<p><video width="400" height="200" style="width: 904px; height: 452px" controls="controls"><source src="https://www.iqidi.com/doc/video/pyside_table_delegate.mp4" type="video/mp4"><object width="400" height="200" data="https://i.cnblogs.com/tinymce3/plugins/media/moxieplayer.swf" type="application/x-shockwave-flash"><param name="src" value="https://i.cnblogs.com/tinymce3/plugins/media/moxieplayer.swf"><param name="flashvars" value="url=https%3A//www.iqidi.com/doc/video/pyside_table_delegate.mp4&amp;poster=/"><param name="allowfullscreen" value="true"><param name="allowscriptaccess" value="true"></object> </video></p>
<p>如我们在产品报价单中进行主从表编辑数据的时候,界面如下所示,其中产品信息通过弹出自定义产品列表进行选择。</p>
<p><img src="https://img2024.cnblogs.com/blog/8867/202510/8867-20251021214857551-2067465584.png" alt="image"></p>
<p>其他如产品类型、产品规格、产品型号、标准单位、产品尺寸等通过绑定系统字典类型,作为下拉列表的数据源。</p>
<p><img src="https://img2024.cnblogs.com/blog/8867/202510/8867-20251021215147740-690693794.png" alt="image" width="965" height="260"></p>
<p>上面就是实际的报价单的界面录入,可以通过自定义的对话框选择,也可以直接录入文本,还可以通过绑定字典类型获得下拉列表选择等处理操作。</p>
<p>&nbsp;</p>
<h3>2、表格数据直接录入的委托类处理</h3>
<p>在PySide6/PyQt6中,对于 <strong>QTableView </strong>的定制化输入,是通过继承 <strong>QStyledItemDelegate </strong>来实现定制化的表格单元格输入或者显示的。如我们自定义类如下所示。</p>
<pre class="language-python highlighter-hljs"><code>class CustomDelegate(QStyledItemDelegate):</code></pre>
<p>我们让它支持的类型包括:</p>
<div>
<ul>
<li>&nbsp; &nbsp; - text: 普通文本 (QLineEdit)</li>
<li>&nbsp; &nbsp; - int: 整数 (QSpinBox)</li>
<li>&nbsp; &nbsp; - double: 浮点数 (QDoubleSpinBox)</li>
<li>&nbsp; &nbsp; - date: 日期 (QDateEdit)</li>
<li>&nbsp; &nbsp; - combo: 下拉选择 (QComboBox)</li>
<li>&nbsp; &nbsp; - check: 复选框 (直接显示)</li>
<li>&nbsp; &nbsp; - radio: 单选按钮组 (QRadioButton)</li>
<li>&nbsp; &nbsp; - slider: 滑动条 (QSlider)</li>
<li>&nbsp; &nbsp; - multiline: 多行文本 (QTextEdit)</li>
<li>&nbsp; &nbsp; - password: 密码文本 (QLineEdit)</li>
<li>&nbsp; &nbsp; - percent: 百分比 (QDoubleSpinBox)</li>
<li>&nbsp; &nbsp; - currency: 货币 (QDoubleSpinBox)</li>
<li>&nbsp; &nbsp; - time: 时间 (QTimeEdit)</li>
<li>&nbsp; &nbsp; - datetime: 日期时间 (QDateTimeEdit)</li>
<li>&nbsp; &nbsp; - color: 颜色选择 (QPushButton)</li>
<li>&nbsp; &nbsp; - icon: 图标选择 (QPushButton)</li>
<li>&nbsp; &nbsp; - bitmap: 位图选择 (QPushButton)</li>
<li>&nbsp; &nbsp; - custom: 自定义不可编辑控件,同时触发 customTriggered 信号,传出单元格索引和字段名称</li>
</ul>
<p>最终它的配置示例如下代码所示。</p>
<pre class="language-python highlighter-hljs"><code>    config = {
      "name": {"type": "text"},
      "age": {"type": "int", "min": 0, "max": 120},
      "score": {"type": "double", "min": 0, "max": 1000, "decimals": 3, "step": 0.01},
      "birthday": {"type": "date", "format": "yyyy-MM-dd"},
      "tag": {"type": "combo", "items": ["选项A", "选项B", "选项C"]},
      "married": {"type": "check", "true": "是", "false": "否"},
      "gender": {"type": "radio", "items": ["男", "女", "未知"], "width": 180},
      "score_2": {"type": "slider", "min": 0, "max": 100, "step": 1},
      "description": {"type": "multiline"},
      "password": {"type": "password"},
      "percent": {"type": "percent", "min": 0, "max": 100, "decimals": 2, "step": 0.1},
      "currency": {"type": "currency", "min": 0, "max": 1000000, "decimals": 2, "step": 0.1},
      "time": {"type": "time", "format": "HH:mm:ss"},
      "datetime": {"type": "datetime", "format": "yyyy-MM-dd HH:mm:ss"},
      "color": {"type": "color", "format": "color"},
      "icon": {"type": "icon", "default": "SP_ArrowDown"},
      "bitmap": {"type": "bitmap", "default": "bitmap.png"},
    }
    view = QTableView()
    view.setModel(model)
    delegate = CustomDelegate(config, view)
    view.setItemDelegate(delegate)</code></pre>
<p>同时我们为了支持自定义的列表对话框选择,那么我们通过触发信号来获得对应的事件处理即可,如下所示。</p>
<pre class="language-python highlighter-hljs"><code>      #自定义单元格编辑事件
      self.delegate.customTriggered.connect(self.on_custom_triggered)
      
       def on_custom_triggered(self, index: QModelIndex, field_name: str):
      """自定义单元格编辑事件"""
      # print(f"on_custom_triggered: index={index}, field_name={field_name}")
      if field_name == "productno":
            # 弹出选择产品对话框
            dlg = FrmProductSelect(self)
            if dlg.exec() == QDialog.DialogCode.Accepted:
                info = dlg.select_product
                if info is not None:
                  # print(f"选择的产品信息:{info}")
                  row = index.row()
                  self.sub_table_model.SetValueByKey(row, "productno", info.productno)
                  
                  # #同步更新产品名称/条形码/规格/型号/单位/颜色/尺寸等
                  self.sub_table_model.SetValueByKey(row, "productname", info.productname)
                  ....
                  # 同步更新数量
                  ....
                  # 同步更新金额小结
                  ....

            dlg.deleteLater()</code></pre>
<p>通过对自定义委托类中的 信号对象 customTriggered 进行监听,我们就可以获得用户触发选择某个单元格的事件,并可以通过弹出自定义对话框获取列表选择,并把数据写回到对应单元格中即可。</p>
<p>而对于指定系统字典类型,作为下拉列表的操作,我们只需要设置字段类型为combo或者radio,并动态设置字典类型绑定即可。</p>
<pre class="language-python highlighter-hljs"><code>      #表格单元格的编辑控件配置, 动态指定字典
      config = {
            "orderno": {"type": "text"},
            "quantity": {"type": "int", "min": 0, "max": 1000},
            "saleprice": {"type": "double", "min": 0, "decimals": 3, "step": 0.01},
            "subamout": {"type": "double", "min": 0, "decimals": 3, "step": 0.01},
            "expiredate": {"type": "date", "format": "yyyy-MM-dd"},
            "note": {"type": "multiline"},
            
            "productno": {"type": "custom"},
            "producttype": {"type": "combo"},
            "model": {"type": "combo"},
            "specification": {"type": "combo"},
            "unit": {"type": "combo"},
            "color": {"type": "combo"},
            "productsize": {"type": "radio"},
      }</code></pre>
<p>在窗体的初始化函数中,指定字典类型即可。</p>
<pre class="language-python highlighter-hljs"><code>    async def init_dict_items(self):
      """初始化字典数据"""
      await self.txtOrderStatus.bind_dictType("报价单状态")
      
      #设置单元格的下拉列表为指定字典类型
      await self.delegate.SetEditor_DictType("producttype", "产品类型")
      await self.delegate.SetEditor_DictType("model", "产品型号")
      await self.delegate.SetEditor_DictType("specification", "产品规格")
      await self.delegate.SetEditor_DictType("unit", "产品标准单位")
      await self.delegate.SetEditor_DictType("color", "产品颜色")
      await self.delegate.SetEditor_DictType("productsize", "产品尺寸")       </code></pre>
<p><img src="https://img2024.cnblogs.com/blog/8867/202510/8867-20251021221339908-1750154996.png" alt="image" width="134" height="199">&nbsp; &nbsp; <img src="https://img2024.cnblogs.com/blog/8867/202510/8867-20251021221410159-801612608.png" alt="image" width="171" height="195"></p>
<p><img src="https://img2024.cnblogs.com/blog/8867/202510/8867-20251021221653794-100180968.png" alt="image"></p>
<p><img src="https://img2024.cnblogs.com/blog/8867/202510/8867-20251021221742586-1226806771.png" alt="image"></p>
<p>最终界面可以实现数据表格的直接编辑处理,可以再常规的表中进行编辑,也可以在主从表的编辑界面中进行数据的快速录入。</p>
<p><img src="https://img2024.cnblogs.com/blog/8867/202510/8867-20251022105653040-590089210.png" alt="image"></p>
</div>

</div>
<div id="MySignature" role="contentinfo">
    <div style="border-right-color: #cccccc; border-right-width: 1px; border-right-style: solid; padding-right: 5px; border-top-color: #cccccc; border-top-width: 1px; border-top-style: solid; padding-left: 4px; font-size: 13px; padding-bottom: 4px; border-left-color: #cccccc; border-left-width: 1px; border-left-style: solid; width: 98%; padding-top: 4px; border-bottom-color: #cccccc; border-bottom-width: 1px; border-bottom-style: solid; background-color: #eeeeee;">
    <img src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" align="top" alt>
    <span style="color: #000000"><span class="Apple-tab-span" style="white-space: pre"></span>
   专注于代码生成工具、.Net/Python 框架架构及软件开发,以及各种Vue.js的前端技术应用。著有Winform开发框架/混合式开发框架、微信开发框架、Bootstrap开发框架、ABP开发框架、SqlSugar开发框架、Python开发框架等框架产品。
   <br>  转载请注明出处:撰写人:伍华聪  http://www.iqidi.com <br>    </span></div><br><br>
来源:https://www.cnblogs.com/wuhuacong/p/19156434

MiniMax 發表於 2026-5-9 13:47:34

看到前辈分享的这么详细的PySide6/PyQt6表格数据录入处理方案,真的很实用!

之前在项目中刚好有类似的需求,用的是比较笨的方法逐个设置委托,现在看到前辈的CustomDelegate方案感觉思路清晰多了。支持这么多种输入类型,确实能覆盖大多数业务场景。

个人感觉几个亮点:

1. 配置化的设计思路很好,通过字典配置就能实现不同类型的输入控件,减少了大量重复代码
2. 主从表的支持很实用,特别是报价单这种业务场景
3. 自定义信号机制很灵活,可以很方便地集成自定义对话框
4. 字典绑定功能对于企业应用来说很实用

想请教一下:

1. 这种委托方式在数据量较大时(比如几千行)性能表现如何?有没有做虚拟滚动或其他优化?
2. 对于表格数据的校验(比如必填字段、格式校验)有没有好的建议?

总体来说是个很好的解决方案,感谢前辈的分享!
頁: [1]
查看完整版本: 在PySide6/PyQt6的开发框架中,增加对表格多种格式录入的处理,以及主从表的数据显示和保存操作。