Qt 中的那些“坑” (一)

本文记录自己在使用Qt过程中遇到的坑,有些是自己疏忽或认识不足导致的问题,有些是Qt自身的bug或陷阱。本文旨在总结自己遇到的问题和解决方法。

-----------------------------------------------本文将持续更新,如有帮助请收藏-------------------------------------------


1.关于Qt SerialPort

由于公司性质,做过几个上位机软件,主要通信接口就是串口、蓝牙和网口了。Qt4时代相信大家都是用的第三方QextSerialPort类,但是自Qt5开始Qt SerialPort已作为单独模块加入到Qt库中,可以,这很强势,既然有了官方库我们何必去到处找轮子,于是。。。

(1)Qt串口的信号槽

相信所有的教程介绍Qt串口都是这样开始的:

    ui->textEdit_recvSerial->append(QString("检测到端口列表:"));
    foreach (const QSerialPortInfo &info, QSerialPortInfo::availablePorts())
    {
        QSerialPort serial;
        serial.setPort(info);
        if (serial.open(QIODevice::ReadWrite))
        {
            ui->comboBox_serialport->addItem(info.portName());
            ui->textEdit_recvSerial->append(info.portName());
            ui->textEdit_recvSerial->append(info.description());
            ui->textEdit_recvSerial->append(info.manufacturer());
            serial.close();
        }
    }
    if(ui->comboBox_serialport->count()==0)ui->textEdit_recvSerial->append(QString("未检测到串口设备!"));


这是第一步,启动软件后自动扫描活动的串口设备,并保存相应的设备信息,当然也可以通过按钮实现手动刷新设备列表。然后就是建立信号槽的连接:
     mSerialport= new QSerialPort();
    //接收串口数据并显示
    connect(mSerialport,SIGNAL(readyRead()),this,SLOT(readUart()));
    //处理串口错误(中途断开连接等错误)
    connect(mSerialport, static_cast(&QSerialPort::error),
            this, &BeaconControlCenter::handleSerialError);

可以,我们通过readyRead()信号触发readUart()处理接收到的数据,槽函数里一般比较简单,诸如:

	mSerialData=mSerialport->readAll();
     或者:
      while (!mSerialPort.atEnd()) {
          QByteArray data = mSerialPort.read(100);
          ....
      }

而且还加入了错误处理机制,用于检测中途设备断开连接等可能的异常情况。看起来没问题,这些应该就是Qt串口类的核心代码了。
然而问题来了,这个代码能在工程上用么?肯定滴说,绝对不能,否则你会看到如下现象:当你发送较长的字符串时可能会被程序分成几次来收,像下图这样:

这是什么鬼?为啥断片了?完整的数据包其实是:ble01010619FF4C000215FDA50693A4E24FB1AFCFC6EB076478252719575CCD11223344#这是我蓝牙项目里用到的一个蓝牙广播包,但是在Qt串口这无情地拆成了三部分,并且还很“”友好地”帮你自动换行显示。。。于是我们开始心里开骂了:Qt这是要闹哪样,为啥我用串口调试助手接收的是好好的啊?这个其实不是Qt的bug,只是Qt串口每次将接收到的数据自动换行导致你以为是Qt的专属问题,事实上你用串口调试助手接收的数据也会是几部分,只是它不会换行而是连接在一起让你相信我一次收到了整个数据包,其实它从缓冲区分了几次读取!
原因实际上是我们对串口协议的认知:串口是异步工作的,纯粹的RS232串口只是定义了接口规范,并没有定义具体的传输协议,如果你不自行封装数据帧并进行解包,那么收到的数据就是以纯粹的数据流的形式接收(所以才有Modbus这类协议)。而Qt的ready()信号是一旦缓冲区接收数据就会触发,而Qt天真地以为这是用户分了若干次传输数据,所以帮你自动换行显示,于是有了上面的一幕。
好了,知道原因了。如何解决?答:超时接收机制+自定义数据帧
自定义数据帧根据自己的需要添加起始和结束码,尤其是结束码。我重点谈谈超时接收机制的实现:超时接收的方法其实就是一个QTimer的定时器,我们称其为接收超时定时器,当接收信号触发时我们不直接读取数据,而是启动定时器,并将数据append(连接)起来,当timeout的时候再获取完整的数据,为了获取完整的数据包你得根据自己的包长和实际情况设置定时的时长,代码如下:
//串口接收
void BeaconControlCenter::readUart()
{
    serialTimer->start(100);

    mSerialRecvData.append(mSerialport->readAll());

}
//超时处理
void BeaconControlCenter::serialTimerUpdate()
{
    serialTimer->stop();

    mSerialRecvNum += mSerialRecvData.count();

    if(ui->radioButton_recvHex->isChecked())
    {
        ui->textEdit_recvSerial->append(QString(mSerialRecvData.toHex()));//十六进制显示
    }
    else
    {
        ui->textEdit_recvSerial->append(QString(mSerialRecvData.data()));//字符模式显示
    }

    ui->label_serialRecvNum->setText(QString::number(mSerialRecvNum));

    //存储到BLE数据包缓存队列,每个BLE package为一个QString
    //此次换成你所需要的处理,比如我这里根据包长提取每个数据包加入到我的队列
    qDebug()<enqueue(tempStr);
        qDebug()< back();
    }

    mSerialRecvData.clear();

}
 
   
 
   
 
  

其实接收超时机制在modbus里已有运用,我们使用某种传输协议的时候已经帮我们做好了这些工作,否则我们就要自行处理这些问题。经过处理后,终于可以拆分数据包显示了:


数据包也能正常解析了:




2.关于Qt Multimedia

在.pro中加入:QT+=multimedia  头文件中包含QMediaPlayer等类库,就可以开始写一个简单的音视频播放器了。核心代码如下:

  playlist = new QMediaPlaylist;
  playlist->addMedia(QUrl("http://example.com/movie1.mp4"));
  playlist->addMedia(QUrl("http://example.com/movie2.mp4"));
  playlist->addMedia(QUrl("http://example.com/movie3.mp4"));
  playlist->setCurrentIndex(1);

  player = new QMediaPlayer;
  player->setPlaylist(playlist);

  videoWidget = new QVideoWidget;
  player->setVideoOutput(videoWidget);
  videoWidget->show();

  player->play();

然后当你跃跃一试准备运行的时候发现不能播放视频,原因是QMediaPlayer解码音视频文件底层依赖解码库,在Linux下需要安装gstreamer-ffmpeg;在Windows下需要安装LAV filters。因为 QMediaPlayer依赖于DirectShow服务,所以需要在不同平台下安装对应的底层库。

问题来了:我们发布的程序怎么保证所运行的平台上安装了这些库??

这个真的就是Qt的坑了,不是我们的错。所以在Qt里不会有人直接使用Qt Multimedia,哪怕是个简单的音视频播放,最好采用第三方框架,比如:ffmpeg、VLC、mplayer等。

如果你实在想用,只能在打包发布时将对应平台的依赖库一起打包进去。

-----------------------------------------------------------持续更新中---------------------------------------------------------------

你可能感兴趣的:(Qt)