查看: 797|回复: 0

[分享] 【经验分享】i.MX6ULL开发:嵌入式Qt-做一个秒表

[复制链接]
  • TA的每日心情
    开心
    2020-12-18 12:56
  • 签到天数: 55 天

    [LV.5]常住居民I

    71

    主题

    221

    帖子

    0

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    1588
    最后登录
    2024-4-22
    发表于 2022-9-5 16:59:39 | 显示全部楼层 |阅读模式
    之前的文章:嵌入式Qt-动手编写并运行自己的第1个ARM-Qt程序
    介绍了如何编写第一个嵌入式Qt程序,实现了一个电子时钟的演示。
    本篇,继续进行Qt实践,仿照手机中的秒表,实现一个相同功能的秒表:
    图片 1.png
    回顾上一次的Qt开发流程,整个Qt的开发都是通过敲代码实现的,实际上,还可以利用Qt Creater的UI界面功能,通过图像化的配置来开发图形界面,本篇就使用这种方法来进行开发。
    1 新建Qt工程
    Qt工程创建的具体步骤可参照之前的文章:嵌入式Qt-动手编写并运行自己的第1个ARM-Qt程序,这里只说明不同之处。
    上篇是通过代码实现页面设计的,本篇要借助Qt Creater的UI界面设计功能,因此要把下面的创建页面勾选上:
    图片 2.png
    创建完成之后的Qt默认工程结构如下:
    图片 3.png
    双击widget.ui,即可打开UI设置页面,如下图:
    图片 4.png
    这里先简单熟悉下各个功能区:
    图片 5.png
    2 代码编写2.1 ui界面设计
    修改界面的尺寸,我的Linux板子屏幕的分辨率是800x480,因此调整到对应的尺寸:
    图片 6.png
    从左侧拖入一个Label,然后可以修改字体的大小:
    图片 7.png
    再从左侧拖入其它需要用到的组件(PushButton、TextBrower)和位置调节组件(弹簧形状的HorizontalSpacer、VericalSpacer)
    图片 8.png
    进行水平布局和竖直布局,选中对应的组件,例如3个按钮和中间的2个弹簧,点击上方工具栏中的水平布局按钮:
    图片 9.png
    3个按键的水平布局效果如下:
    图片 10.png
    然后再依次对其它组件进行布局:
    图片 11.png
    字体可以调整到居中显示:
    图片 12.png
    鼠标选中最大的组合组件,拖拽边缘调整到合适的外尺寸。然后选中不同级别的组合组件,调整layoutStretch的参数,实现按比例显示各个组件(相当于调节各个弹簧组件的弹力大小)
    图片 13.png
    点击左下角上面那个三角图标,运行,查看效果:
    图片 14.png
    注意左边留的空白是给秒表的表盘留的。

    2.2 QTimer与QTime介绍
    QTimer 类为定时器提供了一个高级编程接口,提供重复和单次计时。
    QTime 类提供时钟时间功能,QTime 对象包含一个时钟时间,它可以表示为自午夜以来的小时数、分钟数、秒数和毫秒数。
    Qt Creater提供了方便的帮助文档,可以在Qt Creater中直接查看对应功能函数的使用,比如搜索QTimer,就可以看到对应的介绍,以及可用的API函数:
    图片 17.png
    本篇需要用到QTimer的功能有:
    • start:启动定时器
    • stop:停止定时器

    再看看QTime的介绍:
    图片 16.png
    本篇需要用到QTime的功能有:
    • setHMS:设置初始时间
    • addMSecs:增加一个时间(毫秒单位)
    • toString:时间转为字符串格式
    • minute:获取分钟
    • second:获取秒
    • msec:获取毫秒

    2.3 对应按钮的函数
    为了编写出更易看懂的代码,在编写代码之前,需要修改对应的组件的默认名称为便于理解的名称,比如我将3个按键的名称分别改为了:
    • Btn_Start:开始按钮,并同时具有暂停/继续功能
    • Btn_Reset:复位按钮
    • Btn_Hit:打点按钮,用于记录不同名次的时间




    然后还要手动添加QTimer和QTime对象,用于实现秒表的计时功能:
    图片 18.png
    2.3.1 开始按钮的处理
    Qt编程中重要处理就是信号和槽机制,它可用通过手动通过connet函数实现,而对于使用Qt Creater的图形界面设计方式,通常也是继续通过界面实现信号和和槽的连接:在开始按钮上右键,选则“转到槽...”:
    图片 19.png
    然后有多种按钮信号可以选择,因为开始按钮同时具有暂停/继续的功能,这里使用toggled功能,利用按钮的按下和松开状态,来实现暂停/继续的功能:
    图片 20.png
    点击OK之后,会自动跳到到代码页面,并自动生成对应的槽函数框架,然后就可以在里面编译对应的业务逻辑代码了:
    开始按钮的具体业务逻辑代码如下,当首次按下时,checked为true,此时启动timer,记录此时的时间戳,然后将按钮的文字显示为“暂停”,同时将复位和打点按钮置灰,使这两个按钮不能再按下,因为暂停的时候执行复位和打点无意义。
    timer每隔一段时间会触发超时,这里ADD_TIME_MSEC设置的是30ms,超时时间到后,编写对应的超时处理函数timeout_slot以及声明对应的信号和槽的处理。
    1. <font face="Arial">void Widget::on_Btn_Start_toggled(bool checked)
    2. {
    3.    if (checked)
    4.    {
    5.        timer.start(ADD_TIME_MSEC);
    6.        lastTime = QTime::currentTime();//记录时间戳
    7.        ui->Btn_Start->setText("暂停");
    8.        ui->Btn_Reset->setEnabled(false);
    9.        ui->Btn_Hit->setEnabled(true);
    10.    }
    11.    else
    12.    {
    13.        timer.stop();
    14.        ui->Btn_Start->setText("继续");
    15.        ui->Btn_Reset->setEnabled(true);
    16.        ui->Btn_Hit->setEnabled(false);
    17.    }
    18. }

    19. connect(&timer, SIGNAL(timeout()), this, SLOT(timeout_slot()));
    20. void Widget::timeout_slot()
    21. {
    22.    //qDebug("hello");
    23.    QTime nowTime = QTime::currentTime();
    24.    time = time.addMSecs(lastTime.msecsTo(nowTime));
    25.    lastTime = nowTime;
    26.    ui->Txt_ShowTime->setText(time.toString("mm:ss.zzz"));
    27. }</font>
    复制代码
    超时时间到了之后,计算一些两次的时间差值,然后通过addMSecs函数来累加时间。
    2.3.2 复位按钮的处理
    复位按钮也是通过右键来调整到槽,注意这里使用clicked函数即可,因为复位按钮只需要使用它的点击按下功能:
    对应的槽函数的具体实现如下:
    1. <font face="Arial">void Widget::on_Btn_Reset_clicked()
    2. {
    3.    m_iHitCnt = 0;
    4.    timer.stop();
    5.    time.setHMS(0,0,0,0);
    6.    ui->Txt_ShowTime->setText("00:00:00");
    7.    ui->Txt_ShowItem->clear();

    8.    ui->Btn_Start->setText("开始");
    9.    ui->Btn_Start->setChecked(false);
    10.    ui->Btn_Reset->setEnabled(false);
    11.    ui->Btn_Hit->setEnabled(false);
    12. }</font>
    复制代码
    主要是将时间归零,将显示情况,并将各个按钮的显示状态复位为默认显示状态。
    2.3.3 打点按钮的处理
    打点按钮与复位按钮一样,也是只使用clicked函数即可,对应的槽函数的具体实现如下:
    1. <font face="Arial">void Widget::on_Btn_Hit_clicked()
    2. {
    3.    QString temp;
    4.    m_iHitCnt++;
    5.    temp.sprintf("--计次 %d--", m_iHitCnt);
    6.    ui->Txt_ShowItem->setFontPointSize(9);
    7.    ui->Txt_ShowItem->append(temp);
    8.    ui->Txt_ShowItem->setFontPointSize(12);
    9.    ui->Txt_ShowItem->append(time.toString("[mm:ss.zzz]"));
    10. }</font>
    复制代码
    打点功能用于在秒表的运行过程中,记录不同名次的时间,并显示在右侧的文本显示框中。
    这里通过setFontPointSize函数来设置不同大小的字体显示。
    2.4 秒表表盘的实现
    之前这篇文章:嵌入式Qt-动手编写并运行自己的第1个ARM-Qt程序,通过代码的方式,实现了一个时钟表盘的显示,本篇在这个的基础上,修改代码,实现一个显示秒和分的秒表表盘,具体修改后的代码如下:
    1. <font face="Arial">connect(&timer, SIGNAL(timeout()), this, SLOT(update()));
    2. connect(ui->Btn_Reset, SIGNAL(clicked()), this, SLOT(update()));

    3. void Widget::paintEvent(QPaintEvent *event)
    4. {
    5.    int side = qMin(width(), height());
    6.    //QTime time = QTime::currentTime();

    7.    QPainter painter(this);
    8.    painter.setRenderHint(QPainter::Antialiasing);
    9.    painter.translate(width()/3, height()*2/5); //画图的基准位置
    10.    painter.scale(side/300.0, side/300.0); //随窗口尺寸自动缩放

    11.    //表盘(3个同心圆)
    12.    for (int i=0; i<PANEL_RADIUS_NUM; i++)
    13.    {
    14.        QBrush brush(stPanelParaArr[i].color);
    15.        QPen pen(stPanelParaArr[i].color);
    16.        painter.setBrush(brush);
    17.        painter.setPen(pen);
    18.        painter.drawEllipse(-stPanelParaArr[i].radius, -stPanelParaArr[i].radius, 2*stPanelParaArr[i].radius, 2*stPanelParaArr[i].radius);
    19.    }

    20.    //秒的刻度
    21.    painter.setPen(secondColor);
    22.    for (int i = 0; i < 60; i++)
    23.    {
    24.        if ((i % 5) == 0)
    25.        {
    26.            painter.drawLine(PANEL_RADIUS3-8, 0, PANEL_RADIUS3, 0);
    27.            QFont font("TimesNewRoman", SEC_NUM_SIZE);
    28.            painter.setFont(font);
    29.            painter.drawText(-SEC_NUM_SIZE, -(CLOCK_RADIUS-15), 2*SEC_NUM_SIZE, 2*SEC_NUM_SIZE, Qt::AlignHCenter, QString::number(i==0? 60 : i));
    30.        }
    31.        else
    32.        {
    33.            painter.drawLine(PANEL_RADIUS3-5, 0, PANEL_RADIUS3, 0);
    34.        }
    35.        //秒再细分5个格
    36.        for (int j = 0; j < 5; j++)
    37.        {
    38.            painter.rotate(6.0/5);
    39.            if (j != 4)
    40.            {
    41.                painter.drawLine(PANEL_RADIUS3-2, 0, PANEL_RADIUS3, 0);
    42.            }
    43.        }
    44.    }

    45.    //分钟的刻度
    46.    painter.setPen(minuteColor);
    47.    for (int k = 0; k < 30; k++)
    48.    {
    49.        if ((k % 5) == 0)
    50.        {
    51.            painter.rotate(-90.0);
    52.            painter.drawLine(PANEL_RADIUS4-8, 0, PANEL_RADIUS4, 0);
    53.            painter.rotate(90.0);

    54.            QFont font("TimesNewRoman", MIN_NUM_SIZE);
    55.            painter.setFont(font);
    56.            painter.drawText(-MIN_NUM_SIZE, -(PANEL_RADIUS4-10), 2*MIN_NUM_SIZE, 2*MIN_NUM_SIZE, Qt::AlignHCenter, QString::number(k==0? 30 : k));
    57.        }
    58.        else
    59.        {
    60.            painter.rotate(-90.0);
    61.            painter.drawLine(PANEL_RADIUS4-4, 0, PANEL_RADIUS4, 0);
    62.            painter.rotate(90.0);
    63.        }
    64.        painter.rotate(12.0);
    65.    }

    66.    //分钟的表针
    67.    painter.setPen(Qt::NoPen);
    68.    painter.setBrush(minuteColor);
    69.    painter.save();
    70.    painter.rotate(12.0 * (time.minute() + time.second() / 60.0));
    71.    painter.drawConvexPolygon(minuteHand, 3);
    72.    painter.restore();

    73.    //秒钟的表针
    74.    painter.setPen(Qt::NoPen);
    75.    painter.setBrush(secondColor);
    76.    painter.save();
    77.    //painter.rotate(6.0 * time.second());
    78.    painter.rotate(6.0 * (time.second()+time.msec()/1000.0));
    79.    painter.drawConvexPolygon(secondHand, 3);
    80.    painter.restore();

    81.    painter.end();
    82. }</font>
    复制代码
    主要修改是将之前的小时显示去掉,并改为两个时间环:外圈秒环和内圈分环,秒环的范围是0~60秒,分环的范围是0~30分。
    秒表表盘的显示效果如下:
    3 编译运行
    代码是在Window环境中的Qt Creater中编写的,首先是Windows中编译查看效果。
    3.1 Windows中编译
    在Windows中的运行效果如下图的右图,可以实现手机中秒表类似的计时效果:
    3.2 Ubuntu中编译
    将Windows中的QT工程源码:
    • .cpp文件
    • .h文件
    • .pro文件
    • .ui文件

    复制到Ubuntu中,注意.user文件是不需要的(它是Windows平台的编译配置)。
    然后使用ARM平台的编译工具链,我的是在”/home/xxpcb/myTest/imx6ull/otherlib/qt/qt-everywhere-src-5.12.9/arm-qt/“,这里需要先用到它的qmake工具先自动生成Makefile文件,再通过make指令进行编译。
    使用qmake生成Makefile,进入程序源码目录,执行qmake指令:
    1. <font face="Arial">/home/xxpcb/myTest/imx6ull/otherlib/qt/qt-everywhere-src-5.12.9/arm-qt/bin/qmake</font>
    复制代码
    成功执行之后,就可以看到自动生成的Makefile文件,然后执行make指令进行编译得到可执行文件。
    3.3 Linux板子中运行
    将可执行文件放到已配置了qt运行环境的Linux板子中,运行并查看效果
    注:
    Ubuntu中的具体编译过程,可参考之前这篇文章:嵌入式Qt-动手编写并运行自己的第1个ARM-Qt程序
    Ubuntu中Qt的交叉编译环境的配置,可参考之前这篇文章:嵌入式Linux-Qt环境搭建
    4 总结
    本篇通过一个秒表的实例,介绍了如何使用Qt Creator的UI界面设计功能,进行Qt的开发,并将代码进行交叉编译,放入i.MX6ULL的Linux环境中测试运行情况。


    图片 15.png
    签到
    回复

    使用道具 举报

    您需要登录后才可以回帖 注册/登录

    本版积分规则

    关闭

    站长推荐上一条 /4 下一条

    Archiver|手机版|小黑屋|恩智浦技术社区

    GMT+8, 2024-4-24 15:34 , Processed in 0.118505 second(s), 20 queries , MemCache On.

    Powered by Discuz! X3.4

    Copyright © 2001-2024, Tencent Cloud.

    快速回复 返回顶部 返回列表