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. 829. Consecutive Numbers Sum

    Given a positive integer N, how many ways can we write it as a sum of consecutive positive integers? ...

  2. SpringCloud(六)Bus消息总线

    Bus 消息总线 概述 分布式自动刷新配置功能 Spring Cloud Bus 配合 Spring Cloud Config使用可以实现配置的动态刷新 Bus支持两种消息代理:RabbitMQ和Ka ...

  3. [源码分析] 并行分布式任务队列 Celery 之 Timer & Heartbeat

    [源码分析] 并行分布式任务队列 Celery 之 Timer & Heartbeat 目录 [源码分析] 并行分布式任务队列 Celery 之 Timer & Heartbeat 0 ...

  4. 路由器逆向分析------binwalk工具的安装

    本文博客链接:http://blog.csdn.net/qq1084283172/article/details/65441110 一.binwalk工具运行支持的平台 binwalk工具安装支持的平 ...

  5. hdu3746 KMP的next数组应用,求项链首尾项链循环

    题意:       给你一个项链,问你最少加多少个珠子能满足整个项链是一个循环的项链(首尾相连) 思路:      KMP的简单应用只要了解next数组的意义就好说了,下面总结下  next在循环方面 ...

  6. LA3602DNA序列

    题意:      给你一个一些DNA序列(只有ACGT)然后让你构造一个序列,使得所有的序列到他的Hamming距离最小,所有的序列包括构造的序列长度都是N,Hamming表示两个序列的不同字符位置个 ...

  7. POJ2195费用流+BFS建图

    题意:       给你一个n*m的地图,上面有w个人,和w个房子,每个人都要进房子,每个房子只能进一个人,问所有人都进房子的路径总和最少是多少? 思路:       比较简单的最大流,直接建立两排, ...

  8. CVE-2012-3569:VMware OVF Tool 格式化字符串漏洞调试分析

    0x01 简介 VMware OVF Tool 是一个命令行实用程序,允许您从许多 VMware 产品导入和导出 OVF 包.在 2.1.0 - 2.1.3 之间的版本中存在格式化字符串漏洞,通过修改 ...

  9. What are CBR, VBV and CPB?

    转自:https://codesequoia.wordpress.com/2010/04/19/what-are-cbr-vbv-and-cpb/ It's common mistake to to ...

  10. 浅尝js垃圾回收机制

    局部作用域内的变量,在函数执行结束之后就会被js的垃圾回收机制销毁   为什么要销毁局部变量? => 为了释放内存   js垃圾回收机制何时会销毁局部变量 : 如果局部变量无法再得到访问,就会被 ...