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绘制简易表盘的更多相关文章

  1. QT绘制饼图

    QT版本:QT5.6.1 QT绘制饼图,出问题的代码如下 void DrawPieDialog::paintEvent(QPaintEvent *event) { float startAngle=0 ...

  2. Qt 学习之路 2(24):Qt 绘制系统简介

    Qt 学习之路 2(24):Qt 绘制系统简介 豆子 2012年10月30日 Qt 学习之路 2 77条评论 Qt 的绘图系统允许使用相同的 API 在屏幕和其它打印设备上进行绘制.整个绘图系统基于Q ...

  3. Qt项目简易开发原理及常见问题解决

    一.资源下载地址 https://www.aliyundrive.com/s/jBU2wBS8poH 本项目路径:项目->免费->QtDev 注释:为了方便qt全功能开发,QtDev中包含 ...

  4. Qt QBarSeries简易柱状图教程

    博客园最强Qt QBarSeries简易柱状图教程 前情提要 每个人的绘图需求不同,此篇教程也是根据需求来改的.我的需求大概如下所示. 通过信号槽的方式接收signals来刷新柱状图,所以每次触发信号 ...

  5. canvas绘制简易时钟

    时钟绘制的非常简易,但该有的都有了. 效果图如下, <!DOCTYPE html> <html> <head lang="en"> <me ...

  6. Qt绘制异形窗体

    异形窗体即不规则窗体,一般采用png图片,一般绘制异形窗体分两步: 1.设置遮罩区 2.绘制图片   使用png图片的透明部分作为遮罩区,然后绘制图片,这样我们就看到一个只绘制了非透明部分的图形,废话 ...

  7. Qt 学习之路 :Qt 绘制系统简介

    Qt 的绘图系统允许使用相同的 API 在屏幕和其它打印设备上进行绘制.整个绘图系统基于QPainter,QPainterDevice和QPaintEngine三个类. QPainter用来执行绘制的 ...

  8. qt绘制设备

    # -*- coding: utf-8 -*- # python:2.x __author__ = 'Administrator' from PyQt4.QtGui import  * from Py ...

  9. QT绘制系统简介

    #3个类:QPainter,QPainterDevice 和 QPaintEngine 三个类 #qpainter用于执行绘制操作 #QPainterDevice是一个二维空间抽象,允许qpainte ...

随机推荐

  1. 『动善时』JMeter基础 — 3、JMeter插件管理

    JMeter是一个Java开发的开源软件,开源的软件有一个好处,就是会有很多第三方开发出来的插件,使得JMeter在处理某一些功能的时候更加的方便.并且这些插件拿过来就可以使用,完全免费的. 我们安装 ...

  2. ZOJ 3736 模拟魔方

    题意:       2*2*2的魔方,给你一个初始状态,和一个限定步数,问你在这么多步数条件下最多能有多少面拼好,(不是累加关系,是某一个状态的最多,最多是6); 思路:      最多是7步,所以直 ...

  3. hdu1828 线段树扫描线求矩形面积的周长

    题意:       给你n个矩形,问你这n个矩形所围成的图形的周长是多少. 思路:       线段树的扫描线简单应用,这个题目我用的方法比较笨,就是扫描两次,上下扫描,求出多边形的上下边长和,然后同 ...

  4. Windows域的管理

    目录 域的管理 默认容器 组织单位的管理 添加额外域控制器 卸载域控服务器 组策略应用 域的管理 域用户账户的管理 创建域用户账户 配置域用户账户属性 验证用户的身份 授权或拒绝对域资源的访问 组的管 ...

  5. Day007 计算器

    计算器 public static void main(String[] args) { double[] num={0,1}; String oprater="a"; doubl ...

  6. Day002 Java特性和优势

    Java特性和优势 简单性(摒弃了c++的指针和内存分配释放) 面向对象(万物皆对象) 可移植性(write once run anywhere) 高性能 分布式 动态性(反射机制) 多线程 安全性 ...

  7. Nacos使用 MySQL 8.0 提示Public Key Retrieval is not allowed

    原因如下(参考官网给出的连接选项): 如果用户使用了 sha256_password 认证,密码在传输过程中必须使用 TLS 协议保护,但是如果 RSA 公钥不可用,可以使用服务器提供的公钥:可以在连 ...

  8. 逆向工程初步160个crackme-------3

    这个Crackme3 涉及到浮点指令以及浮点数的存储与运算,我没学习过浮点指令,不得不从网上恶补了1个小时,一边看汇编指令一边百度其指令含义. 回头得好好补补这方面的知识了,太菜了! 我大致了解了一下 ...

  9. react+antd 使用脚手架动态修改主题色

    最近做了一个需求,后台管理系统添加一个可以动态修改ant-design主题色.查询了大多数的文章,发现基本都是抄来抄去,而且文章记录的也一点也不详细.刚刚把这个功能做完了,顺便记录一下如何去修改主题色 ...

  10. git取消更改 恢复版本命令

      #删除远程的xxx分支 git push origin :xxx #取消对文件的修改.还原到最近的版本,废弃本地做的修改. git checkout -- <file>   #取消已经 ...