Qt程序设计(三):气象自动监测站-加入MySQL数据库

本文介绍了如何在QT中实现与MySQL数据库的交互,包括数据库环境的搭建、数据表的建立以及通过SQL语句进行登录验证和数据写入的具体操作。

  继上文完成了气象要素数据的串口接收后,接下来的要实现的功能是数据的存储。本文设计选用的是MySQL数据库。QT对主流的大多数数据库都有完美的支持,选用哪个数据库关系不大。

数据库环境搭建

  博主在搭建数据库环境时没有使用MySQL的官方套件,而是使用了XAMPP这个软件包,从网上下载按照提示安装即可。打卡软件后开启Apache和MySQL两个服务即可。

这里写图片描述

  开启Apache的时候很有可能会碰到两个问题,一个是提示“80端口被占用”;解决方法只要点击XAMPP软件界面右边的“Netstat”,找到占用80端口的进程,在任务管理器中结束掉即可。另一个问题是因为博主的Windows环境中安装过VMware虚拟机,导致提示如下错误:

10:40:53  [Apache]  Problem detected!
10:40:53  [Apache]  Port 443 in use by "E:\VMware\vmware-hostd.exe -u "C:\ProgramData\VMware\hostd\config.xml"" with PID 7788!
10:40:53  [Apache]  Apache WILL NOT start without the configured ports free!
10:40:53  [Apache]  You need to uninstall/disable/reconfigure the blocking application
10:40:53  [Apache]  or reconfigure Apache and the Control Panel to listen on a different port

  由于两个软件共同监听了443端口,导致Apache服务器无法打开。解决方法只要将XAMPP安装目录中的“apache/conf/extra/httpd-ssl.conf”中的“Listen 443”改为其它端口号即可。

  两个服务都开启后,在http://localhost/phpmyadmin/ 中便可登入数据库,首先要做的是创建一个账号,为了测试方便我把账号类型设置为“localhost”,这样服务器地址就是“127.0.0.1”。创建完毕后先将XAMPP的服务关闭,把Apache的Config设置为“phpMyAdmin(config.inc.php)”。从新打开服务,再打开刚才的主页,便可以用新的用户来登入。

建立数据表

  本设计中需要用到两张数据库表,一个用于存储用户信息,表结构如下所示:

这里写图片描述

  这个表用于在登录时向数据库确认用户名和密码是否正确。另一个表用来存储气象要素信息,表结构如下所示:

这里写图片描述

  博主的设计本意是将采集数据和采集时间一起写入,奈何在Date设置为datetime类型后,向数据库中写入时间时老是不成功。因此将其改为了char型,存储时将时间转换为字符串存入。

QT数据库操作

  首先要知道一点,直接安装后的QT虽然支持MySQL数据库,但直接使用会出现无法连接数据库等类似的错误。必须要做的一点是安装一个mysql(注意不是mysql管理工具),将其目录下lib中的libmysql.dll复制到QT安装目录的bin文件夹下,这样在运行时就会找到相关的运行库。

  博主这里与QT5.8配套,安装的是mysql-5.7.18。注意一定要自己安装一次mysql,不要到网页上随便找一个libmysq.dll文件,版本不匹配同样也是无法工作的。

  使用前和串口类似,需要挂载相关类的库,并在.pro文件中增加QT的值:

#include <QtSql>
#include <QSqlDatabase>
#include <QSqlQuery>

QT       += sql

  数据库的配置如下:

    QSqlDatabase db;

    //设置数据库
    db = QSqlDatabase::addDatabase("QMYSQL");
    db.setHostName("127.0.0.1");
    db.setUserName("liuqi");
    db.setPassword("henhenhahe");
    db.setDatabaseName("user");
    if(!db.open())
    {
        qDebug() << "Failed to connect to database.";
    }

  注意这个初始化数据库的代码我是放在“登录”按钮的触发槽函数中的,更细致的说,是把数据库类QSqlDatabase的申明放在了函数中,也就是说这个数据库变量具有局部作用域,在函数结束后,数据库连接也会自动销毁。这样做的目的是什么?主要是为了避免数据库访问时的冲突问题,登录时建立数据库连接只是为了确认用户名和密码而已,而这个连接在其它地方并没有用,如果不再此处销毁,在后面写入气象要素数据时建立连接会遇到数据库连接已被占用的问题。

  与登录相关的代码如下:

    //检查用户名和密码输入是否为空
    if(username.size() == 0 || password.size() == 0)
    {
        ui->infoLabel->setText(QStringLiteral("请输入用户名与密码"));
    }
    //输入有效则进行登录操作
    else
    {
        QSqlQuery query;
        if (query.exec("SELECT id FROM user WHERE username = '"+username+"' AND password = '"+password+"'"))
        {
            if(query.size() > 0)
            {
                ui->infoLabel->setText(QStringLiteral("登录成功!"));
                db.close();
                w.show();
                this->close();
            }
            else
            {
                ui->infoLabel->setText(QStringLiteral("用户名或密码错误,请重新输入"));
            }
        }
        else
        {
            ui->infoLabel->setText(QStringLiteral("无法访问服务器,请稍后再试"));
            qDebug() << query.lastError().text();
        }
    }

  其中与数据库操作有关的关键部分为:

 QSqlQuery query;
        if (query.exec("SELECT id FROM user WHERE username = '"+username+"' AND password = '"+password+"'"))

  申明一个QSqlQuery变量,使用这个类的exec()方法就可以执行数据库操作,该函数只需要传入一个字符串类型的SQL语句即可,此处使用的是SELECT语句,找出数据库表中的username和passord和文本框的输入对比判断。

  再串口连接时,我们同时也配置一个数据库连接,先假设配置过程和上面一样,只是改变了数据库表的名字,让它连接到存储气象要素数据的数据表。运行时就会发现提示:

QSqlDatabasePrivate::addDatabase: duplicate connection name 'qt_sql_default_connection', old connection removed.

  这是因为我们在前面已经添加过这个数据库类型了“db = QSqlDatabase::addDatabase(“QMYSQL”);”,QMYSQL已经被QT人文是数据库的默认连接(qt_sql_default_connection),虽然db已经被销毁,但这数据库的添加确不会移除,因为我们这里没有使用新类型的数据库,所以只要将配置语句改为下面这样即可:

db = QSqlDatabase::database("qt_sql_default_connection");

  直接使用database函数调用默认连接而不是再添加一个数据库类型。
  向数据库中写入气象要素数据只要使用SQL中的INSERT语句即可:

query.exec("INSERT INTO weather (ID, Date, Temp, Humi, Press, Rain) VALUES ("+QString::number(k)+",'"+QString::number(dateTime.date().year())+"-"+QString::number(dateTime.date().month())+"-"+QString::number(dateTime.date().day())+" "+dateTime.time().toString()+"',"+QString::number(temp)+","+QString::number(humi)+","+QString::number(press)+","+QString::number(rain)+")")

  写入操作放在与串口绑定的槽函数中,提取出通信数据后调用上述语句即可写入数据库。另外值得注意的是,我这里在写入时间时,把它拆分为年、月、日和Time分别转换为字符串再叠加,这是因为我发现QDateTime类型的变量直接转化为字符串写入数据库时,年月日会变成”?“,为什么这样我也没找到原因。

  运行程序,查看数据库中写入的数据如下图:

这里写图片描述

  另外本文的程序虽然已经可以正常运行,但在数据库建表时其实有一处不合理的地方,那就用用ID作为了主键,这样如果写入时主键相同新数据就不会添加进去,因此这里更合理的应该是将Date改为主键,因为时间是不会出现重复的^_^。

void Widget::Select() //查询 { QString name = ui->lineEdit->text(); model->setFilter(QObject::tr("id = '%1'").arg(name)); //根据姓名进行筛选 model->select(); //显示结果 } void Widget::Delect() //删除当前行 { int curRow = ui->tableView->currentIndex().row(); //获取选中的行 model->removeRow(curRow); //删除该行 int ok = QMessageBox::warning(this,tr("删除当前行!"),tr("你确定" "删除当前行吗?"), QMessageBox::Yes,QMessageBox::No); if(ok == QMessageBox::No) { model->revertAll(); //如果不删除,则撤销 } else model->submitAll(); //否则提交,在数据库中删除该行 } void Widget::Add() //插入记录 { int rowNum = model->rowCount(); //获得表的行数 int id = 10; model->insertRow(rowNum); //添加一行 model->setData(model->index(rowNum,0),id); //model->submitAll(); //可以直接提交 } void Widget::Back() //返回全表 { model->setTable("student"); //重新关联表 model->setHeaderData(0, Qt::Horizontal, "Time"); model->setHeaderData(1, Qt::Horizontal, "Temperature"); model->select(); //这样才能再次显示整个表的内容 } void Widget::Amend() //提交修改 { model->database().transaction(); //开始事务操作 if (model->submitAll()) { model->database().commit(); //提交 } else { model->database().rollback(); //回滚 QMessageBox::warning(this, tr("tableModel"), tr("数据库错误: %1").arg(model->lastError().text())); } } void Widget::Get_time() { QString string; QTime current_time = QTime::currentTime(); int hour = current_time.hour(); int minute = current_time.minute(); int second = current_time.second(); // int msec = current_time.msec(); string=QString("%1").arg(hour)+":"+QString("%1").arg(minute) +":"+QString("%1").arg(second); ui->Receive->append(string); //qDebug() <rowCount(); //获得表的行数 // int id = 10; model->insertRow(rowNum); //添加一行 model->setData(model->index(rowNum,0),string); model->submitAll(); } void Widget::readMyCom() { QByteArray temp = myCom->readAll(); if(temp.size()!=0) { QString string; QTime current_time = QTime::currentTime(); int hour = current_time.hour(); int minute = current_time.minute(); int second = current_time.second(); // int msec = current_time.msec(); string=QString("%1").arg(hour)+":"+QString("%1").arg(minute) +":"+QString("%1").arg(second); ui->Receive->append(string); //qDebug() <rowCount(); //获得表的行数 // int id = 10; model->insertRow(rowNum); //添加一行 model->setData(model->index(rowNum,0),string); model->setData(model->index(rowNum,1),temp); model->submitAll(); data_light=temp.toInt(); } ui->Receive->append(temp); } void Widget::openCom() { QString portName = ui->portNameComboBox->currentText(); myCom = new Win_QextSerialPort(portName,QextSerialBase::EventDriven); myCom ->open(QIODevice::ReadWrite); if(ui->baudRateComboBox->currentText()==tr("9600")) myCom->setBaudRate(BAUD9600); else if(ui->baudRateComboBox->currentText()==tr("115200")) myCom->setBaudRate(BAUD115200); myCom->setFlowControl(FLOW_OFF); myCom->setTimeout(500); connect(myCom,SIGNAL(readyRead()),this,SLOT(readMyCom())); ui->openMyComBtn->setEnabled(false); ui->closeMyComBtn->setEnabled(true); ui->baudRateComboBox->setEnabled(false); ui->portNameComboBox->setEnabled(false); }
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值