QT绘制简易表盘
1、简介
最近学习了一下QT,熟悉了一段时间后发现它的功能还是挺强大的,同时也比较方便用户上手去使用。现在就基于最近学习的内容,实现一个简易的带指针旋转功能的表盘。文中表盘的实现是基于QT的QPainter类中的绘图方法,同时外加QT的定时器设计完成的。效果上肯定没有贴图片那么美观,不过两者的设计思想是基本一样的,这里的设计方法可以提供给你一个不错的参考。
2、设计思路
在讲解设计思路前还是先来看下绘制的表盘所实现的效果吧!后面对实现效果进行一步步拆解来讲解,这样或许会更加直观,更便于理解。

我们先不去考虑指针旋转这一动态过程实现,可以将表盘分解析为3个组成部分,表盘的外形轮廓、指针和显示的当前速度的数值。外形轮廓由一个圆弧和一些指示刻度组成,它的绘制肯定要使用QT中的画圆弧的函数、画线函数还有显示文本函数。指针是一个不规则的多边形,它的绘制会用到QT中的绘制多边形的函数。显示当前速度值比较简单些,直接使用显示文本函数绘制。
有了静态部分的基础,现在开始考虑指针的动态旋转过程和旋转过程中的渐变效果是如何实现的。指针旋转的角度应该和当前的转速相互对应,当前转速改变时,会根据新的转速计算出当前指针位于什么角度的位置,然后可以调用QT的旋转角度函数让多边形指针旋转到这个位置。旋转的渐变效果其实是通过绘制扇形实现的,要绘制扇形的角度和指针旋转的角度是一样,由于绘制的扇形的内部的着色采用了颜色的线性内插,所以不同的角度显示的颜色程度不同,因此给人以渐变的效果。
转速的周期改变是在定时器中完成的,构造函数中初始化一个周期定时器,当定时器时间超时,根据当前转速的状态(上行还是下行),确定让转速自增1还是自减1。转速改变时,调用函数让界面进行重新绘制。
3、核心函数
所用到的绘制函数都是QPainter类中的方法, QPainter可以理解成是个画家,这个画家有很多种绘制图形的方法,它可以在所以继承QPaintDevice的类上进行绘制。
绘制多边形:
drawPolygon
QT中drawPolygon有很多重载版本,要传入的参数一般就是要指定绘制多边形的所有顶点位置。
绘制圆弧:
drawArc
同理,QT中也有很多drawArc的重载版本。它传入的参数比较特殊,QT中的绘制圆弧是在一个正方形中操作的,要传入的是正方形区域的大小,绘制圆弧的圆心默认是在传入正方形区域中心。除此之外还需要指定要绘制圆弧的起始角度和跨越的角度,它传入是值是实际角度*16。 当传入的角度值为正,表示逆时针绘制圆弧,角度值为负,表示顺时钟绘制圆弧。
绘制扇形:
drawPie
绘制扇形和绘制圆弧的参数类似,可参考绘制圆弧。
QPainter坐标转换:
translate
如执行painter.translate(100, 100),后续绘制的参考坐标变成了相对100, 100位置而言的。执行painter.drawLine(0, 0, 0, 20),实际绘制直线的两端点是(100, 100)和(100, 120)
旋转:
ratate
坐标转换和旋转过程可参考下图:

保存和恢复QPainter状态:
save
restore
save保存当前QPainter状态(即当前对QPainter的设置)到堆栈,restore恢复之前保存的状态。这两个方法在QPainter绘图中很有用,详细可参考后面代码。
4、代码实现
先从定时器函数函数来看,当构造函数中设置的定时器超时,会产生定时器事件,timerEvent被自动调用
void Widget::timerEvent(QTimerEvent *e)
{
int timerId = e->timerId(); if(this->time_id == timerId) {
if(this->status == 0) {
this->speed += 1;
if(this->speed >= 180)
this->status = 1;
}else {
this->speed -= 1;
if(this->speed <= 0)
this->status = 0; } this->update();
}
}
判断当前转速是处于上行还是下行状态,根据转速所处的状态修改转速。然后调用this->update(), 该函数会产生Widget类中的重绘事件,paintEvent函数被执行。
painteEvent函数内容如下:
void Widget::paintEvent(QPaintEvent *event)
{
qreal angle; angle = (qreal)270 / (180-1); QPainter painter(this); painter.translate(width()/2, height()/2); drawFrame(&painter, angle); //① drawPointer(&painter, angle); //② drawSpeed(&painter); //③
}
① 绘制仪表盘圆弧形状的外框
② 根据转速大小,计算当前指针所在的角度,然后绘制指针和产生渐变效果的扇形
③ 显示当前转速值
drawFrame函数
void Widget::drawFrame(QPainter *painter, qreal angle)
{
painter->save(); painter->setBrush(QBrush(QColor(0, 255, 0, 255), Qt::SolidPattern));
painter->drawArc(-200, -200, 400, 400, -135*16, -270*16); //① painter->restore(); for(int i = 0; i < 180; i++) { //②
painter->save(); painter->rotate(-225 + i * angle); if(i % 10 == 0) {
painter->drawLine(180, 0, 200, 0);
}else {
painter->drawLine(190, 0, 200, 0);
} painter->restore();
} painter->save(); for(int i = 0; i < 9; i++) { //③ int xTextPos = 180 * qCos((225 - i * 15)*3.14/180);
int yTextPos = -180 * qSin((225 - i * 15)*3.14/180);
painter->drawText(xTextPos+5, yTextPos+10, QString::number(i * 10));
painter->drawText(-xTextPos-25, yTextPos+10, QString::number((18 - i) * 10));
}
painter->drawText(-10, -165, "90"); painter->restore();
}
① 绘制表盘的外形圆弧
② 绘制圆弧上的刻度信息
③ 绘制表盘外形的刻度旁边的数值,这里利用了圆的对称性
drawPointer函数
void Widget::drawPointer(QPainter *painter, qreal angle)
{
QPoint point[4] = {
QPoint(0, 10),
QPoint(-10, 0),
QPoint(0, -170),
QPoint(10, 0),
}; painter->save(); QLinearGradient linear; //①
linear.setStart(-200, -200);
linear.setFinalStop(200, 200);
linear.setColorAt(0, QColor(0, 255, 255, 0));
linear.setColorAt(1, QColor(0, 255, 255, 255));
painter->setPen(Qt::NoPen);
painter->setBrush(linear);
painter->drawPie(-200, -200, 400, 400, 225 * 16, -(angle * this->speed) * 16); painter->restore(); painter->save(); painter->setBrush(QBrush(QColor(0, 0, 0, 255), Qt::SolidPattern));
painter->rotate(-135 + this->speed * angle);
painter->drawPolygon(point, 4); //② painter->restore();
}
① 根据当前速度计算扇形区域的大小,绘制渐变扇形
② 根据当前速度计算指针所要在的位置,旋转指针到该位置
drawSpeed函数
void Widget::drawSpeed(QPainter *painter)
{
painter->save(); painter->setPen(QColor("#0"));
// 绘制速度
QFont font("Times", 10, QFont::Bold);
font.setBold(true);
font.setPixelSize(66);
painter->setFont(font);
painter->drawText(-60, 100, 120, 92, Qt::AlignCenter, QString::number(speed));
painter->restore();
}
drawSpeed函数在指定位置,使用指定颜色字体,显示当前速度
5、小结
到此,模拟仪表盘的实现讲解完毕了,这也算对这几天QT的学习做了一个小小的总结。从使用过程中可以感受到使用QT进行图形界面的设计,软件编写上还是比较灵活的。QT的GUI功能非常很强大,它提供的接口函数很多,但想要都用好的确很难,平时还需要多看看QT官方文档中的解释和提供的demo。
完整代码

#include "widget.h" #include <QPainter>
#include <QBrush>
#include <QLabel>
#include <QTimerEvent>
#include <QLinearGradient>
#include <QFont> #include <QtMath> Widget::Widget(QWidget *parent)
: QWidget(parent)
{
resize(800, 480);
setWindowTitle("模拟仪表盘"); this->speed = 0;
this->status = 0; this->time_id = this->startTimer(50);
} Widget::~Widget()
{
} void Widget::paintEvent(QPaintEvent *event)
{
qreal angle; angle = (qreal)270 / (180-1); QPainter painter(this); painter.translate(width()/2, height()/2); drawFrame(&painter, angle); drawPointer(&painter, angle); drawSpeed(&painter);
} void Widget::drawFrame(QPainter *painter, qreal angle)
{
painter->save(); painter->setBrush(QBrush(QColor(0, 255, 0, 255), Qt::SolidPattern));
painter->drawArc(-200, -200, 400, 400, -135*16, -270*16); painter->restore(); for(int i = 0; i < 180; i++) {
painter->save(); painter->rotate(-225 + i * angle); if(i % 10 == 0) {
painter->drawLine(180, 0, 200, 0);
}else {
painter->drawLine(190, 0, 200, 0);
} painter->restore();
} painter->save(); for(int i = 0; i < 9; i++) { int xTextPos = 180 * qCos((225 - i * 15)*3.14/180);
int yTextPos = -180 * qSin((225 - i * 15)*3.14/180);
painter->drawText(xTextPos+5, yTextPos+10, QString::number(i * 10));
painter->drawText(-xTextPos-25, yTextPos+10, QString::number((18 - i) * 10));
}
painter->drawText(-10, -165, "90"); painter->restore();
} void Widget::drawPointer(QPainter *painter, qreal angle)
{
QPoint point[4] = {
QPoint(0, 10),
QPoint(-10, 0),
QPoint(0, -170),
QPoint(10, 0),
}; painter->save(); QLinearGradient linear;
linear.setStart(-200, -200);
linear.setFinalStop(200, 200);
linear.setColorAt(0, QColor(0, 255, 255, 0));
linear.setColorAt(1, QColor(0, 255, 255, 255));
painter->setPen(Qt::NoPen);
painter->setBrush(linear);
painter->drawPie(-200, -200, 400, 400, 225 * 16, -(angle * this->speed) * 16); painter->restore(); painter->save(); painter->setBrush(QBrush(QColor(0, 0, 0, 255), Qt::SolidPattern));
painter->rotate(-135 + this->speed * angle);
painter->drawPolygon(point, 4); painter->restore();
} void Widget::drawSpeed(QPainter *painter)
{
painter->save(); painter->setPen(QColor("#0"));
// 绘制速度
QFont font("Times", 10, QFont::Bold);
font.setBold(true);
font.setPixelSize(66);
painter->setFont(font);
painter->drawText(-60, 100, 120, 92, Qt::AlignCenter, QString::number(speed));
painter->restore();
} void Widget::timerEvent(QTimerEvent *e)
{
int timerId = e->timerId(); if(this->time_id == timerId) {
if(this->status == 0) {
this->speed += 1;
if(this->speed >= 180)
this->status = 1;
}else {
this->speed -= 1;
if(this->speed <= 0)
this->status = 0; } this->update();
}
}
widget.cpp

#ifndef WIDGET_H
#define WIDGET_H #include <QWidget> class Widget : public QWidget
{
Q_OBJECT public:
Widget(QWidget *parent = nullptr);
~Widget(); void paintEvent(QPaintEvent *event);
void timerEvent(QTimerEvent *e); private:
void drawFrame(QPainter *painter, qreal angle);
void drawPointer(QPainter *painter, qreal angle);
void drawSpeed(QPainter *painter); int speed;
int time_id;
int status; };
#endif // WIDGET_H
widegt.h

#include "widget.h" #include <QApplication> int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}
main.cpp
QT绘制简易表盘的更多相关文章
- QT绘制饼图
QT版本:QT5.6.1 QT绘制饼图,出问题的代码如下 void DrawPieDialog::paintEvent(QPaintEvent *event) { float startAngle=0 ...
- Qt 学习之路 2(24):Qt 绘制系统简介
Qt 学习之路 2(24):Qt 绘制系统简介 豆子 2012年10月30日 Qt 学习之路 2 77条评论 Qt 的绘图系统允许使用相同的 API 在屏幕和其它打印设备上进行绘制.整个绘图系统基于Q ...
- Qt项目简易开发原理及常见问题解决
一.资源下载地址 https://www.aliyundrive.com/s/jBU2wBS8poH 本项目路径:项目->免费->QtDev 注释:为了方便qt全功能开发,QtDev中包含 ...
- Qt QBarSeries简易柱状图教程
博客园最强Qt QBarSeries简易柱状图教程 前情提要 每个人的绘图需求不同,此篇教程也是根据需求来改的.我的需求大概如下所示. 通过信号槽的方式接收signals来刷新柱状图,所以每次触发信号 ...
- canvas绘制简易时钟
时钟绘制的非常简易,但该有的都有了. 效果图如下, <!DOCTYPE html> <html> <head lang="en"> <me ...
- Qt绘制异形窗体
异形窗体即不规则窗体,一般采用png图片,一般绘制异形窗体分两步: 1.设置遮罩区 2.绘制图片 使用png图片的透明部分作为遮罩区,然后绘制图片,这样我们就看到一个只绘制了非透明部分的图形,废话 ...
- Qt 学习之路 :Qt 绘制系统简介
Qt 的绘图系统允许使用相同的 API 在屏幕和其它打印设备上进行绘制.整个绘图系统基于QPainter,QPainterDevice和QPaintEngine三个类. QPainter用来执行绘制的 ...
- qt绘制设备
# -*- coding: utf-8 -*- # python:2.x __author__ = 'Administrator' from PyQt4.QtGui import * from Py ...
- QT绘制系统简介
#3个类:QPainter,QPainterDevice 和 QPaintEngine 三个类 #qpainter用于执行绘制操作 #QPainterDevice是一个二维空间抽象,允许qpainte ...
随机推荐
- 12- Jmeter逻辑控制器
正则表达式 -1代表所有 1代表第一个 0表示随机 逻辑控制器 仅一次控制器 转换控制器(广义并发,构造混合场景) 引入函数助手 并发: 广义并发的并发: 还可以添加两个线程组: 一个线程组访问 首页 ...
- 【并发编程】ThreadLocal
ThreadLocal Thread类中 具有一个ThreadLocal.ThreadLocalMap ,这个变量是由ThreadLocal去维护的,各个线程之间相互隔离
- 批处理打造MySQLCleaner
#批处理打造MySQLCleaner ###1. 简介 在我们卸载MySQL数据库的时候,往往除了需要卸载软件,还需要删除各种注册表信息,隐藏文件,卸载服务,否则当我们再次安装MySQL时就会出现一些 ...
- UVA11388GCD LCM
题意: 输入两个整数G,L,找出两个正整数a,b使得gcd(a ,b)=G,lcm(a ,b)=L,如果有多组解,输出最小的a的那组,如果没解,输出-1. 思路: 比较简单,如 ...
- 在进程空间使用虚拟内存(Windows 核心编程)
虚拟内存空间 如今的 Windows 操作系统不仅可以运行多个应用程序,还可以让每一个应用程序享受到约 4 GB 的虚拟内存空间(包括系统占用),假如内存为 4 GB 的话.那为什么 Window 可 ...
- Linux-鸟菜-5-目录配置-FHS
Linux-鸟菜-5-目录配置-FHS 这节内容比较休闲,主要就是介绍Linux的目录配置,也就是那些目录通常是干啥的,这个比较重要,需要我们去了解.但是我觉得通常看一遍记不住啥,也就记个大概,主要还 ...
- 【vue-05】vue-cli
Vue-router官网 安装 vue-router是一个插件包,所以我们还是需要用npm 来进行安装.打开命令行工具,进入你的项目目录,输入下面命令. npm install vue-router ...
- 内存回收导致关键业务抖动案例分析-论云原生OS内存QoS保障
蒋彪,腾讯云高级工程师,10+年专注于操作系统相关技术,Linux内核资深发烧友.目前负责腾讯云原生OS的研发,以及OS/虚拟化的性能优化工作. 导语 云原生场景,相比于传统的IDC场景,业务更加复杂 ...
- 谁动了我的 Linux?原来 history 可以这么强大!
当我们频繁使用 Linux 命令行时,有效地使用历史记录,可以大大提高工作效率. 在平时 Linux 操作过程中,很多命令是重复的,你一定不希望大量输入重复的命令.如果你是系统管理员,你可能需要对用户 ...
- SwiftUI 简明教程之自定义 Modifier
本文为 Eul 样章,如果您喜欢,请移步 AppStore/Eul 查看更多内容. Eul 是一款 SwiftUI & Combine 教程 App(iOS.macOS),以文章(文字.图片. ...