ARTICLE · 1020917
QT自定义控件之化工生产安全管理(源码开源)
QT自定义控件之化工生产安全管理(源码开源)一、前言 二、作品展示 首先来进行作品展示下: 


储罐外壳:借助AI来帮助更好的实现炫酷的效果,具体的细节调控可能有些毛刺,但是目前在仿真这块完全是够用了,大家也可以自行优化的更好点。 内部液体:根据实时数据(液位百分比),绘制对应高度的填充区域,液体使用径向渐变。这个接口可实现外部暴露出来,可对接实时数据来进行模拟量的分析及后期增加图表等更加形象化,这个后期博客也可考虑增加。 动态更新:外部采集到液位数据更新对象成员变量,定时器触发 区分不同介质管线,使用不同颜色、虚线样式做区分;利用QPainter结合定时器偏移算法,实现管道内部介质流动动画,直观模拟管路介质输送、启停状态,让静态工艺画面具备动态可视化能力。 3、告警渲染 三、实现流程 2、核心代码 本期的核心代码主要在 3、注意中间罐体警告变色 当低于警告线时会变成红色罐体,正常情况下会呈现蓝色,如下: 
四、源码开源 本期源码开放,有需要的朋友可评论区回复“需要源码”,如果觉得这个对你有帮助 麻烦大家关注点赞收藏~博客更新的动力,感谢大家~如果有定制化需要,也可直接私信~视频后期会放在这里或某站,同名“Q态王”可搜索。大家如果有什么新的控件灵感,也可以评论区交流 五、答疑环节 这次新增答疑环节: 1、关于背景图如何绘制? 答:一般是通过AI来先画个原型,然后自行再来修改 2、关于技术栈的运用? 答:其实我文中也有写的,可能大家还是有点疑问,这可能来自于后端的一些朋友的疑问,这些创作是基于C++、QT、QPainter。 3、能不能用于pyqt,有没有qml接口? 答:这些其实都还没有时间调研,评论区如果有大神知道也可以在评论区分享下经验,交流下~ 4、关于这个技术如何与现有的SCADA系统中运用? 答:这位应该是来自于plc同学们的疑问,其实这种上位机的画面嵌入的话可能涉及到协议及接口的问题,需要实操起来。后期也可考虑往此方向深入研究点,还是评论区如果有大佬知道,麻烦指导一二! 后期大家疑问多的时候,还是会考虑使用这个答疑区,如果不多的情况下,这个区就暂且退下啦~ 六、往期精选文章推荐 1、QT自定义控件之电气接线图(源码开源) 2、QT自定义控件之葫芦兄弟(致敬童年动画片) 3、QT自定义控件之暖通系统(作品展示) 4、QT自定义控件之智慧农田操作系统(作品展示) 5、QT自定义控件之多发酵罐并联工艺(源码开源) 6、QT自定义控件之热换站系统(源码开源) 7、QT自定义控件之压缩机监控系统(源码开源) 8、QT自定义控件之化工配置流程示意(源码开源) 9、QT自定义控件之双向运行地铁(源码开源) 10、QT自定义控件之绘制清洗管道工艺流程图(源码开源)
做工业组态、工厂数字孪生面板的同学应该都见过这类效果图:带储罐、泵体、管道流向、测点数据、告警面板的 2.5D 工艺监控大屏。这次设计的要素也是来自于这种,然后这次的技术方案依然是:Qt + QPainter+C++。
选用 QPainter 复刻了一套化工生产安全管理组态界面,不用重型 3D 引擎,结合AI+纯 C+++Qt 来进行绘图,就能实现储罐、泵、阀门、管线流动动画、数据面板、状态色标。关于这次的设计,最近也有很多人来咨询我一些问题,后面答疑环节来给大家一一说下。
适用场景:工业上位机、嵌入式工控屏,不需要浏览器环境,性能可控,可对接后端采集到的实时测点数据。



画面核心元素:
2.5D 等轴测视角:储罐、泵体、压力表、阀门等设备
带颜色区分的工艺管线:红绿紫不同介质管道,带流动箭头动画 仿真实时数据面板:安全等级、异常预警、巡检完成度等指标 设备状态渲染:储罐液位动态变化、告警高亮 网格背景 + 科技感外发光 UI,整体深色工业监控风格
开发的核心亮点:
1、储罐液位动态绘制
储罐是本图最显眼的组件。 实现方式:
update(),触发重绘。2、管道流体流动动画
左上角信息面板也是 QPainter 绘制。 好处:面板和工艺画面是一体的,缩放、平移时不会错位。目前暂做仿真效果,大家如果有需要可再稍加修改下。
矩形背景 + 半透明填充 + 描边发光; 异常预警数据低于警告线时,中间罐体自动变红闪烁(定时器切换颜色标记)。当高于警告线时,会自动切换成蓝色统一罐体。
1、主窗口结构
主窗口MainWindow,右上角放置普通 QPushButton【开始】按钮;中心放置我们自定义的绘图控件CustomWidget(继承自QWidget)
static constexpr int kWarningLine = 50; // 警戒线static constexpr int kMaxValue = 100; // 最大值static constexpr int kMinValue = 0; // 最小值static constexpr int kStep = 5; // 步长static constexpr int kJarWidgetIndex = 8; // 控件索引int m_direction = 1; // 1 表示递增,-1 表示递减static const QString kBlueJar = ":/img/blue_jar.png";static const QString kRedJar = ":/img/red_jar.png";MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow){ui->setupUi(this);m_Timer = new QTimer(this);connect(m_Timer,SIGNAL(timeout()),this,SLOT(SlotTimer()));m_WarnTimer = new QTimer(this);connect(m_WarnTimer,SIGNAL(timeout()),this,SLOT(SlotWarnTimer()));//初始值m_CurrentValue = 30;}
CustomWidget.cpp中部分代码展示:#if defined(_MSC_VER) && (_MSC_VER >= 1600)# pragma execution_character_set("utf-8")#endifvoidrotateAndPaintRect(QPainter *painter, const QRectF &rect, int angle,bool isRound = false,int rx = 0,int ry = 0){qreal cx = 0;qreal cy = 0;cx = rect.center().x();cy = rect.center().y();painter->save();painter->translate(cx ,cy);painter->rotate(angle);QPointF drawOffset = rect.topLeft() - QPointF(cx,cy);painter->drawRect(drawOffset.x(), drawOffset.y(), rect.width(), rect.height());painter->restore();}voidDrawText(QPainter &painter,const QRectF &rect,QString str,double angle,int size,QString fontFamily,int textAlignment){QRectF rotatedRect(-rect.width()/2, -rect.height()/2, rect.width(), rect.height());qreal cx = rect.x() + rect.width() / 2;qreal cy = rect.y() + rect.height() / 2;QFont font = QFont(fontFamily, size);painter.setFont(font);painter.save();painter.translate(cx, cy);painter.scale(1, -1);painter.rotate(-angle);//旋转角度painter.drawText(rotatedRect,textAlignment,str);//恢复上面保存的状态painter.restore();}voidDrawImage(QPainter &painter, QImage &image, const QRectF &rect,double angle){QRectF rotatedRect(-rect.width()/2, -rect.height()/2, rect.width(), rect.height());painter.drawImage(rotatedRect, image);}
