恒美微站 Logo 恒美微站
  • 首页
  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心
  • 联系我们

Qt QPainter手绘Smith圆图:从坐标变换到数据点映射的完整实现

  • 首页
  • 资讯中心
  • /
  • Qt QPainter手绘Smith圆图:从坐标变换到数据点映射的完整实现

相关资讯

深度迁移学习实战:预训练权重微调与可迁移性边界 2026/10/21 21:29:29
风云四号L1数据读取与真彩色合成:从DN值到出图全流程解析 2026/10/21 21:29:29
Spark调优实战:代码、参数与数据倾斜三线协同优化 2026/10/21 21:24:29

最新资讯

PJ85718DM与PIC32构建HVAC多点单总线温度监测系统
STM32+PJ85718DM单总线温度传感器:HVAC分布式测温软硬件实战
STM32L073RZ与PJ85718DM实现本地远程温度采集:I2C与Modbus RTU设计详解
基于STM32与PJ85718DM的HVAC本地及远程温度监测系统设计
一颗芯片测两路温度:STM32+PJ85718DM实现本地与远程二极管测温
HVAC温度采集实战:本地I2C与远程RS-485组网架构详解

今日推荐

电商智能运营体系从0到1:架构设计、落地步骤与避坑指南
压缩空气储能与热网热惯性联合优化调度:热电联产系统建模与求解
若依框架AI代码生成不稳定?用Agent Skill三层结构+五步法搞定

本周热门

基于GMM的语音识别系统原理与MATLAB实现详解
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

本月精选

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证
2026 大模型集体涨价:用 Python 做企业 Token 成本测算与选型避坑(附配置)

Qt QPainter手绘Smith圆图:从坐标变换到数据点映射的完整实现

发布时间:2026/10/21 21:29:29
Qt QPainter手绘Smith圆图:从坐标变换到数据点映射的完整实现 简介这是一份面向射频微波工程师与Qt图形开发学习者的Smith圆图绘制源码基于Qt5.11.3与QPainter实现可读取txt文件中的复数阻抗数据并映射到圆图上完成可视化帮助解决阻抗匹配分析中手工绘图繁琐、计算不直观的问题。压缩包共5个文件约6KB包含2个cpp源文件、1个h头文件、1个pro工程文件与1个ui界面文件分别承担绘图逻辑、类声明、工程配置与界面布局结构紧凑便于直接编译运行。资源已有821人学习下载适合作为Qt绘图与文件操作的实战参考。读者可从中掌握QFile与QTextStream读取解析数据、复数到圆图坐标的几何变换、drawLine与drawArc等绘制圆环弧线、drawText添加标注以及重写paintEvent刷新视图的完整思路对理解Smith圆图构成与Qt自定义控件开发均有实用价值。1. 用 QPainter 手搓一张 Smith 圆图这个源码包到底能干什么如果你做过射频功放匹配、天线阻抗调试或者被 ADS、AWR 里那张密密麻麻的圆图折磨过那你大概率动过“自己画一张”的念头。这个源码包干的就是这件事用 Qt 的 QPainter 从零绘制一张 Smith 圆图不依赖任何第三方图表库纯手写坐标变换和曲线绘制。它适合两类人一类是想在自研测试仪器、矢网上位机里嵌入圆图显示的嵌入式/上位机开发者另一类是正在学 Qt 绘图、想找一个“有真实数学背景、又不至于太庞大”的练手项目的同学。整个项目体量不大但麻雀虽小五脏俱全——等电阻圆、等电抗弧、阻抗/导纳变换、数据点映射该有的都有。下面我就按“它是什么 → 怎么跑起来 → 核心怎么实现 → 怎么改”的顺序把这份源码拆开讲。2. 先跑起来环境、目录结构与最小可运行路径2.1 技术栈与依赖确认这份源码是标准的 Qt Widgets 工程没有用到 QML也没有引入 Qt Charts 模块——这一点很关键因为一旦用了 Qt ChartsSmith 圆图反而不好画它的极坐标和自定义网格支持并不适合这种非线性的复平面映射。常见做法是用QWidget子类重写paintEvent所有绘制逻辑都塞进QPainter里。依赖清单大致如下组件版本建议说明Qt5.12 及以上 / 6.x5.12 是 LTS6.x 需要把QRegExp换成QRegularExpression编译器MSVC 2017 / GCC 7无特殊要求qmake 或 CMake任选源码包一般给的是.pro文件平台Windows / Linux / macOSQPainter 跨平台无需改代码如果你用的是 Qt 6注意QPainter的HighQualityAntialiasing枚举已经废弃要改成QPainter::Antialiasing否则编译会报错。这是从 Qt 5 迁移到 Qt 6 时最常见的翻车点之一。2.2 目录结构长什么样一个典型的 Qt Smith chart 工程目录大致是这样组织的SmithChart/ ├── SmithChart.pro # qmake 工程文件 ├── main.cpp # 程序入口 ├── mainwindow.h/.cpp # 主窗口放控件和布局 ├── smithchartwidget.h/.cpp # 核心圆图绘制控件 ├── smithchartdata.h/.cpp # 数据模型阻抗点、频率扫描 └── resources.qrc # 图标等资源可选核心文件只有一个smithchartwidget.cpp。其他都是壳。你把这个文件看懂整个项目就通了。mainwindow负责把SmithChartWidget放进布局可能再加几个QLineEdit让用户输入阻抗值或者一个QPushButton触发重绘。2.3 编译与运行步骤假设你拿到的是 qmake 工程操作步骤如下# 1. 进入工程目录 cd SmithChart # 2. 用 qmake 生成 MakefileLinux/macOS qmake SmithChart.pro make -j4 # 3. Windows MSVC 环境下 qmake SmithChart.pro nmake # 4. 运行 ./SmithChart # Linux/macOS Release\SmithChart.exe # Windows如果你用 Qt Creator直接打开.pro文件选好 Kit点绿色三角就行。第一次编译如果报 “cannot find -lQt5Charts”说明.pro里多写了QT charts把这一行删掉即可——这个项目不需要 Qt Charts。提示编译前确认 Qt 的 bin 目录在系统 PATH 里否则 qmake 可能找不到。Windows 下用 “Qt 5.x (MinGW/MSVC) 命令行” 打开终端最省事。跑起来之后你应该能看到一个窗口里面有一张完整的 Smith 圆图最外圈是单位圆内部是等电阻圆和等电抗弧可能还有几个标记点。如果窗口一片空白八成是paintEvent没被触发检查一下控件是否设置了最小尺寸或者resizeEvent里有没有调用update()。3. 核心绘制逻辑从复平面到屏幕坐标的完整链路3.1 Smith 圆图的数学基础Smith 圆图的本质是把阻抗平面Z 平面通过反射系数公式映射到单位圆内。反射系数 Γ 和归一化阻抗 z r jx 的关系是Γ (z - 1) / (z 1)其中 z 是归一化阻抗除以特性阻抗 Z0通常是 50Ω。Γ 是一个复数模值 ≤ 1所以所有阻抗点都落在单位圆内。等电阻圆和等电抗弧就是在这个单位圆里画出来的。等电阻圆圆心在实轴上坐标为 (r/(1r), 0)半径为 1/(1r)。 等电抗弧圆心在 (1, 1/x)半径为 1/|x|只画单位圆内的部分。这些公式是绘制的理论基础源码里一定有一个函数负责把 (r, x) 转成屏幕上的 (px, py)。我一般会把这个转换写成两个函数一个负责阻抗到反射系数一个负责反射系数到屏幕坐标。3.2 坐标变换阻抗 → 反射系数 → 像素先看阻抗到反射系数的转换。这段代码通常长这样// 将归一化阻抗 (r, x) 转换为反射系数 (gammaReal, gammaImag) void SmithChartWidget::impedanceToGamma(double r, double x, double gammaReal, double gammaImag) { // z r jx // Gamma (z - 1) / (z 1) double denomReal r 1.0; double denomImag x; double denomMagSq denomReal * denomReal denomImag * denomImag; // 分子(r - 1) jx double numReal r - 1.0; double numImag x; // 复数除法 gammaReal (numReal * denomReal numImag * denomImag) / denomMagSq; gammaImag (numImag * denomReal - numReal * denomImag) / denomMagSq; }逻辑说明这里直接套用复数除法公式。分母是 (z1)分子是 (z-1)。注意denomMagSq是分母的模平方不能省。参数r和x是归一化后的值如果你输入的是实际阻抗记得先除以 50。接下来是反射系数到屏幕坐标// 将反射系数映射到屏幕像素坐标 QPointF SmithChartWidget::gammaToScreen(double gammaReal, double gammaImag) { // 圆心在控件中心半径取控件宽高较小值的一半再留边距 double cx width() / 2.0; double cy height() / 2.0; double radius qMin(width(), height()) / 2.0 - margin_; // 注意屏幕 y 轴向下而虚轴向上所以要取负 double px cx gammaReal * radius; double py cy - gammaImag * radius; return QPointF(px, py); }逻辑说明margin_是预留的边距防止圆图贴边。gammaReal对应屏幕 x 轴gammaImag对应屏幕 y 轴但方向相反——这是新手最容易搞错的地方画出来的图上下颠倒通常就是这里符号写反了。3.3 绘制等电阻圆和等电抗弧有了坐标变换绘制网格就是循环调用。等电阻圆的绘制void SmithChartWidget::drawResistanceCircles(QPainter painter) { painter.setPen(QPen(Qt::gray, 1)); // 常见的电阻取值0.2, 0.5, 1, 2, 5 等 QListdouble rValues {0.2, 0.5, 1.0, 2.0, 5.0}; for (double r : rValues) { // 等电阻圆在 Gamma 平面的圆心和半径 double centerGamma r / (1.0 r); double radiusGamma 1.0 / (1.0 r); // 转成屏幕坐标 QPointF centerScreen gammaToScreen(centerGamma, 0.0); double radiusScreen radiusGamma * (qMin(width(), height()) / 2.0 - margin_); painter.drawEllipse(centerScreen, radiusScreen, radiusScreen); } }逻辑说明等电阻圆在 Γ 平面上的圆心始终在实轴上半径随 r 增大而减小。r0时是单位圆本身r→∞时退化成点 (1,0)。这里只取了几个典型值实际项目中可以根据缩放级别动态生成。等电抗弧稍微麻烦一点因为它是圆弧不是整圆void SmithChartWidget::drawReactanceArcs(QPainter painter) { painter.setPen(QPen(Qt::lightGray, 1)); QListdouble xValues {0.2, 0.5, 1.0, 2.0, 5.0, -0.2, -0.5, -1.0, -2.0, -5.0}; for (double x : xValues) { // 等电抗弧在 Gamma 平面的圆心和半径 double centerReal 1.0; double centerImag 1.0 / x; double radiusGamma qAbs(1.0 / x); QPointF centerScreen gammaToScreen(centerReal, centerImag); double radiusScreen radiusGamma * (qMin(width(), height()) / 2.0 - margin_); // 只画单位圆内的部分用 clipPath 裁剪 QPainterPath clipPath; QPointF unitCenter gammaToScreen(0, 0); double unitRadius qMin(width(), height()) / 2.0 - margin_; clipPath.addEllipse(unitCenter, unitRadius, unitRadius); painter.setClipPath(clipPath); painter.drawEllipse(centerScreen, radiusScreen, radiusScreen); painter.setClipping(false); } }逻辑说明等电抗弧的圆心在 Γ 平面的 (1, 1/x)半径是 1/|x|。正 x 对应上半圆负 x 对应下半圆。关键点是必须用setClipPath把绘制限制在单位圆内否则弧线会跑到圆外看起来像一堆乱线。setClipping(false)记得恢复不然后面画数据点也会被裁掉。3.4 数据点映射与标记有了网格接下来就是把实际的阻抗测量点画上去。假设你有一组频率扫描数据每个点是一个 (r, x)void SmithChartWidget::drawDataPoints(QPainter painter, const QVectorQPointF impedancePoints) { painter.setPen(Qt::NoPen); painter.setBrush(Qt::red); for (const QPointF z : impedancePoints) { double gr, gi; impedanceToGamma(z.x(), z.y(), gr, gi); QPointF screenPt gammaToScreen(gr, gi); // 画一个半径 3 像素的小圆点 painter.drawEllipse(screenPt, 3.0, 3.0); } }逻辑说明impedancePoints里存的是归一化阻抗x 分量是电阻 ry 分量是电抗 x。每个点先转 Γ再转屏幕坐标最后画圆点。如果要画连线比如频率扫描轨迹把drawEllipse换成drawPolyline或QPainterPath即可。注意数据点的顺序很重要。如果是频率扫描点必须按频率递增排列否则连线会乱成一团。我一般会在数据模型里就排好序绘制时不再处理。4. 避坑与常见问题那些让我调了半天的细节4.1 圆图上下颠倒或左右镜像现象画出来的圆图跟标准 Smith 圆图比电抗弧方向反了或者整体镜像。 原因屏幕坐标系 y 轴向下而 Γ 平面的虚轴向上。gammaToScreen里如果写成cy gammaImag * radius图就上下颠倒。 解决确保虚部映射时取负号即py cy - gammaImag * radius。实部一般不需要取负除非你要做镜像显示。4.2 等电抗弧画到圆外现象单位圆外面出现一堆弧线看起来像蜘蛛网。 原因drawEllipse画的是整圆没有裁剪。 解决在画弧之前设置QPainterPath裁剪区域为单位圆画完再setClipping(false)。注意裁剪路径也要用屏幕坐标构建。4.3 窗口缩放时图形变形现象拉伸窗口圆图变成椭圆。 原因gammaToScreen里 x 和 y 方向用了不同的缩放系数或者半径计算时只取了宽度。 解决半径统一用qMin(width(), height()) / 2.0 - margin_保证 x 和 y 方向缩放一致。同时重写resizeEvent在里面调用update()触发重绘。4.4 高 DPI 屏幕上线条模糊现象在 4K 屏或缩放 150% 的屏幕上网格线发虚。 原因Qt 默认没有开启高 DPI 缩放或者QPainter没开抗锯齿。 解决在main.cpp里设置QApplication::setAttribute(Qt::AA_EnableHighDpiScaling)Qt 5.6并在paintEvent里加painter.setRenderHint(QPainter::Antialiasing)。如果还是模糊检查是否用了固定像素坐标而没有按devicePixelRatio缩放。4.5 数据点密集时卡顿现象几千个频率点画上去界面拖动明显掉帧。 原因每个点单独drawEllipse调用次数太多。 解决改用QPainterPath把所有点加进去一次性drawPath。或者用QPolygonF加drawPoints。如果点实在太多考虑做抽稀或者用 OpenGL 加速的QOpenGLWidget。5. 二次开发把它改成你自己的圆图控件5.1 封装成独立控件这份源码最大的价值是SmithChartWidget可以独立出来。你只需要保留smithchartwidget.h/.cpp把mainwindow里的调用改成你自己的逻辑。常见做法是给它加几个 public 接口class SmithChartWidget : public QWidget { Q_OBJECT public: explicit SmithChartWidget(QWidget *parent nullptr); // 设置归一化阻抗数据 void setImpedanceData(const QVectorQPointF points); // 设置特性阻抗默认 50 欧 void setCharacteristicImpedance(double z0); // 设置是否显示导纳圆图 void setShowAdmittance(bool show); protected: void paintEvent(QPaintEvent *event) override; void resizeEvent(QResizeEvent *event) override; private: QVectorQPointF dataPoints_; double z0_ 50.0; bool showAdmittance_ false; double margin_ 20.0; };这样你就可以把它嵌到任何 Qt 界面里比如矢网的上位机、天线调谐器的控制面板。setImpedanceData里记得调用update()否则数据变了界面不刷新。5.2 加导纳圆图导纳圆图其实就是把阻抗圆图旋转 180 度。因为导纳 y 1/z反射系数 Γ_y -Γ_z。实现上很简单在impedanceToGamma之后把结果取负即可。或者更直接一点画网格的时候同时画两套用不同颜色区分。我一般会在控件上加一个复选框让用户切换显示阻抗还是导纳。5.3 加交互鼠标拾取阻抗值想让用户用鼠标在圆图上点一下就能读出阻抗值需要重写mousePressEvent把屏幕坐标反变换回 Γ再反变换回 z。反变换公式// 从屏幕坐标反算归一化阻抗 QPointF SmithChartWidget::screenToImpedance(const QPointF screenPt) { double cx width() / 2.0; double cy height() / 2.0; double radius qMin(width(), height()) / 2.0 - margin_; double gammaReal (screenPt.x() - cx) / radius; double gammaImag (cy - screenPt.y()) / radius; // Gamma - z: z (1 Gamma) / (1 - Gamma) double denomReal 1.0 - gammaReal; double denomImag -gammaImag; double denomMagSq denomReal * denomReal denomImag * denomImag; double numReal 1.0 gammaReal; double numImag gammaImag; double r (numReal * denomReal numImag * denomImag) / denomMagSq; double x (numImag * denomReal - numReal * denomImag) / denomMagSq; return QPointF(r, x); }这个反变换在调试匹配网络时特别有用——用户点一下你就能算出当前点的驻波比和阻抗实时显示在旁边的标签上。5.4 性能优化与渲染验证改完之后怎么验证我一般会做三件事第一用一组已知的阻抗点比如纯电阻 50Ω、开路、短路测试看落点是否在正确位置第二拖动窗口缩放看网格是否保持正圆第三灌入一万个随机点看帧率是否还能接受。如果卡顿优先检查是不是每个点都单独创建了QPen或QBrush——这些对象要提到循环外面。还有一个血泪经验paintEvent里不要做任何耗时计算。所有坐标变换能缓存的就缓存能预计算的就预计算。我曾经在一个项目里把 Γ 转换放在paintEvent里实时算结果数据量一上来界面直接卡死。后来改成数据更新时算好存起来绘制时只做映射流畅度立刻上来了。从那以后我每次写自定义绘图控件都强制把“数据准备”和“绘制”分成两个阶段绝不在paintEvent里做数学运算。希望这个习惯也能帮到你。本文还有配套的精品资源点击获取

关于恒美微站

恒美微站专注于为个体商户、工作室提供极简自助建站服务,让每个人都能轻松拥有专业网站。

快速链接

  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心

服务项目

  • 可视化建站
  • 拖拽编辑
  • 主题定制
  • SEO 优化
  • 网站托管

联系方式

  • 📍 地址:北京市朝阳区建国路 88 号
  • 📞 电话:400-888-8888
  • ✉️ 邮箱:info@hmyw.cn
  • 🕐 时间:周一至周日 9:00-18:00

© 2024 恒美微站 hmyw.cn 版权所有 | 京 ICP 备 12345678 号