• 9215阅读
  • 15回复

[提问]关于Qt编程的几个问题 [复制链接]

上一主题 下一主题
离线pxzpxz1234
 

只看楼主 正序阅读 楼主  发表于: 2015-07-31
我是个新手,现在在学习Qt编程。最近编程序遇到了几个问题,想向论坛里的大神们请教,问题如果很菜希望不要笑话我啊!谢谢!
问题背景:我现在在编写一个通过以太网从外部设备(测量仪)读取测量数据界面,并在QTextBrowser中显示的程序。程序可以执行
一、程序执行时,应用程序输出一栏会显示
       QObject::connect: Cannot queue arguments of type 'QTextBlock'
       (Make sure 'QTextBlock' is registered using qRegisterMetaType().)
       QObject::connect: Cannot queue arguments of type 'QTextCursor'
       (Make sure 'QTextCursor' is registered using qRegisterMetaType().)
大概的原因我上网查过,是因为不可以跨线程发射有参数的信号。我确实写了子线程实时读取以太网上的数据并显示出来,因为写在ui线程中会卡死。这个问题怎么解决呢?
二、实时显示测量数据的过程中,如果我一次读取的数据较大,有时候会导致QTextBrowser中显示的数据有缺失,就是显示几个字符,又少了一两个字符这样的情况。我声明了char型数组储存读取到的数据,用append函数显示在QTextBrowser中,同时将char型数组转换成了QString型变量,使用文字流写入文本文档中。但是字符缺失的时候,文本文档中的数据没有缺失,说明数据读取应该没有问题。是因为数据太多太快,导致程序反应不过来了吗?数据写入文件约有90KB/s
三、程序执行时,应用程序输出一栏会显示
QBasicTimer::start: Timers cannot be started from another thread
我没有声明或使用这个类的东西啊,为什么会报这个问题?

希望大神们指点迷津,谢谢!
离线z55716368

只看该作者 15楼 发表于: 2015-08-04
学习学习
每一个问题 都是一次进步
离线pxzpxz1234

只看该作者 14楼 发表于: 2015-08-03
回 dbzhang800 的帖子
dbzhang800:一样的原因,多线程没用对。
QTcpSocket 也不是线程安全的(其实Qt中的绝大多数类都不是线程安全的),所以不能在多个线程中访问同一个QTcpSocket。
不过QTcpSocket是可重入的,所以你可以在主线程或次线程中使用QTcpSocket,一个QTcpSocket只能在一个线程内使用。 (2015-08-02 21:35) 

原来如此,我再修改看看。谢谢指点!
离线dbzhang800

只看该作者 13楼 发表于: 2015-08-02
一样的原因,多线程没用对。

QTcpSocket 也不是线程安全的(其实Qt中的绝大多数类都不是线程安全的),所以不能在多个线程中访问同一个QTcpSocket。

不过QTcpSocket是可重入的,所以你可以在主线程或次线程中使用QTcpSocket,一个QTcpSocket只能在一个线程内使用。
离线pxzpxz1234

只看该作者 12楼 发表于: 2015-08-02
回 dbzhang800 的帖子
dbzhang800:多线程用法错误造成的。QWidget及其所有的派生类都不是线程‘安全的,所以它们都只能在主线程使用 (2015-08-02 08:55) 

原来如此,学习了!谢谢!
另外还想请教一个问题:
QSocketNotifier: Socket notifiers cannot be enabled or disabled from another thread
这个错误应该如何修改呢?
打扰了!
离线pxzpxz1234

只看该作者 11楼 发表于: 2015-08-02
回 yanwuyue 的帖子
yanwuyue:Qt的所有UI只能在主线程操作,次线程读完数据用信号发回主线程,记得不要用指针类的,如char*,多线程信号默认是异步的,指针说不定到用的时候已经变成野指针了,反正信号不建议传指针 (2015-08-02 09:36) 

按照你说的进行了修改,问题确实解决了!真是学习了,非常感谢!
另外还想请教一个问题,现在的程序在输出数据的过程中会报下面这个问题:
QSocketNotifier: Socket notifiers cannot be enabled or disabled from another thread

这个问题并没有影响到程序的执行结果,不过我还是想把它除掉。请问该如何去写呢?打扰你了!
离线yanwuyue

只看该作者 10楼 发表于: 2015-08-02
Qt的所有UI只能在主线程操作,次线程读完数据用信号发回主线程,记得不要用指针类的,如char*,多线程信号默认是异步的,指针说不定到用的时候已经变成野指针了,反正信号不建议传指针
离线彩阳

只看该作者 9楼 发表于: 2015-08-02
建议同上。
上海Qt开发联盟,热忱地欢迎你的加入!
离线dbzhang800

只看该作者 8楼 发表于: 2015-08-02
多线程用法错误造成的。QWidget及其所有的派生类都不是线程‘安全的,所以它们都只能在主线程使用
离线pxzpxz1234

只看该作者 7楼 发表于: 2015-08-02
回 kindboy18 的帖子
kindboy18:第一个问题:发送带参数的信号为何会出现这样的问题呢?我为何没有遇到过?是不是你的形参是自定义类型,如果是自定义类型要用qRegisterMetaType注册一下的
第二个问题:QTcpSocket读数据一次只能读8k,你可以用append(m_tcpSocket->readAll())这个试一下
第三个问题:可能是 .. (2015-08-01 16:44) 

还有就是我使用QTcpSocket读数据时,每次最多读2500个字节(缓冲区设置了大小),这个没有超过8k。不过一秒钟就有近90k了。
离线pxzpxz1234

只看该作者 6楼 发表于: 2015-08-02
回 kindboy18 的帖子
kindboy18:第一个问题:发送带参数的信号为何会出现这样的问题呢?我为何没有遇到过?是不是你的形参是自定义类型,如果是自定义类型要用qRegisterMetaType注册一下的
第二个问题:QTcpSocket读数据一次只能读8k,你可以用append(m_tcpSocket->readAll())这个试一下
第三个问题:可能是 .. (2015-08-01 16:44) 

谢谢您的关注和回答!
代码有点多,只贴了相关部分

//窗体的头文件
#ifndef COMM_H
#define COMM_H

#include <QWidget>
#include <QtNetwork/QtNetwork>

//添加部件头文件
#include <QLabel>
#include <QPushButton>
#include <QTextBrowser>
#include <QComboBox>
#include <QGroupBox>
#include <QCheckBox>

#include "message.h"

using namespace std;

namespace Ui {
class comm;
}

class comm : public QWidget
{
    Q_OBJECT

public:
    explicit comm(QWidget *parent = 0);
    ~comm();

protected:
    int sendCommand(const char *command);

private:
    Ui::comm *ui;
    int ifStreamData;
    int ifConnected;
    Message* m;

//定义各种部件的变量
    QLabel* chooseLabel;
    QLabel* sendLabel;
    QLabel* receiveLabel;

    QGroupBox* basicSettingsgroupBox;
    QGroupBox* filterCfggroupBox;
    QGroupBox* otherCmdgroupBox;

    QPushButton* setAccessModeButton;
    QPushButton* ipAddrButton;
    QPushButton* setScanCfgButton;
    QPushButton* LCMcfgButton;
    QPushButton* scanDataCfgButton;
    QPushButton* outputRangeButton;
    QPushButton* writeAllButton;
    QPushButton* LCMstateButton;
    QPushButton* deviceStateButton;
    QPushButton* loadFacDefButton;
    QPushButton* rebootButton;
    QPushButton* clearButton;
    QPushButton* sendButton;

    QCheckBox* particleFiltercheckBox;
    QCheckBox* meanFiltercheckBox;
    QCheckBox* nto1pulseFiltercheckBox;
    QCheckBox* fogFiltercheckBox;

    QComboBox* sendDatacomboBox;

    QTextBrowser* sendBrowser;
    QTextBrowser* receiveBrowser;

//画部件的函数声明
    void drawcomponents();

signals:
    void threadSend(char* sendText);

private slots:
//槽函数声明
    void setTextContents0();
    void setTextContents1();
    void setTextContents2();
    void setTextContents3();
    void setTextContents4();
    void setTextContents5();
    void setTextContents6();
    void setTextContents7();
    void setTextContents8();
    void setTextContents9();
    void setTextContents10();

    void setTextContents11(int);
    void setTextContents12(int);
    void setTextContents13(int);
    void setTextContents14(int);

    void setTextContents15(int);

    int sendSlot();
    void programEnabled();
};

#endif // COMM_H

//窗体的源文件中相关代码
#include "comm.h"
#include "ui_comm.h"
#include "device.h"
#include <iostream>
#include <conio.h>
#include <QtGui>
#pragma execution_character_set("utf-8")

#define IP_ADDR "sWN EIIpAddr C0 A8 0 1"

//构造函数
comm::comm(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::comm)
{
    //绘制窗体
    ui->setupUi(this);
    resize(600,620);
    drawcomponents();

    //设置参数
    ifStreamData = 0;
    ifConnected = 0;

    //启动子线程
    m = new Message(receiveBrowser);
    connect(this, SIGNAL(threadSend(char*)), m, SLOT(sendCommand(char*)));
    connect(m, SIGNAL(deviceConnected()), this, SLOT(programEnabled()));
    m->start();
}

//绘制窗体函数
void comm::drawcomponents()
{
    ......        //绘制组件,连接信号与槽

    connect(sendButton, SIGNAL(clicked()), this, SLOT(sendSlot()));
}

//发送按键对应的处理函数
int comm::sendSlot()
{
    if(ifConnected == 0)
    {
        receiveBrowser->append("PC is not connected to the device!\n");
        return -1;
    }

    QString qstringCommand = sendBrowser->toPlainText();
    QByteArray qbytearrayCommand = qstringCommand.toLatin1();
    char* charCommand = qbytearrayCommand.data();

    emit threadSend(charCommand);

    return 0;
}

//设备成功连接后,使窗体可以操作的函数
void comm::programEnabled()
{
    ifConnected = 1;
}

.......        //其它按键对应的槽函数,肯定没影响

//析构函数
comm::~comm()
{
    delete ui;
}

//子线程头文件
#ifndef MESSAGE
#define MESSAGE

#include <QThread>
#include <QtNetwork/QtNetwork>
#include <QTextBrowser>
#include <QFile>
#include <QTextStream>

using namespace std;

class Message : public QThread
{
    Q_OBJECT

public:
    Message(QTextBrowser* q);
    ~Message();
    int getConnectionStaus();

protected:
    void run();

private:
    QTcpSocket* clientSocket;
    QTextBrowser* qtb;
    QFile* outfile;
    QTextStream outfileStream;

    int isConnected;

signals:
    void deviceConnected();

private slots:
    void successConnection();
    int sendCommand(char *command);
};

#endif // MESSAGE

//子线程源文件
#include "message.h"
#include "device.h"

#include <QApplication>

//构造函数
Message::Message(QTextBrowser* q)
{
    isConnected = 0;
    qtb = q;                                                //将界面中的文本浏览框的指针传到子线程,以便于进行输出操作
    outfile = new QFile("F:\\data.txt");
    outfileStream.setDevice(outfile);
}

//获取设备连接状态
int Message::getConnectionStaus()
{
    return isConnected;
}

//设备连接成功的处理函数
void Message::successConnection()
{
    isConnected = 1;
    qtb->append("Device connected!\n");
    emit deviceConnected();
}

//发送命令的函数
int Message::sendCommand(char *command)
{
    quint8 bytesToSend = 2;                                 //命令的长度加上起始符号和终止符号即为要发送的字节数
    char* messageToSend;                                    //处理后的待发送的消息

    //计算需要发送的字节数
    quint8 i = 0;
    while(command[i++] != 0) bytesToSend++;

    //对命令进行处理
    messageToSend = new char [bytesToSend];
    *messageToSend = 2;                                     //起始符号的ASCII值
    for(i = 1; i <= bytesToSend - 2; i++)
        *(messageToSend + i) = command[i-1];                //将想要发送的命令送到发送缓冲区中
    *(messageToSend + bytesToSend - 1) = 3;                 //终止符号的ASCII值

    //如果实际发送的字节数不等于需要发送的字节数,说明发送命令失败
    int bytesWritten = clientSocket->write(messageToSend, bytesToSend);
    if(bytesWritten != bytesToSend)
    {
        qtb->append("Failed to send command!\n");
        return -1;
    }
    else qtb->append("Send successfully!\n");

    return 0;
}

//子线程的执行函数
void Message::run()
{
    int recvLength = 0;
    clientSocket = new QTcpSocket();                                                    //创建套接字

    connect(clientSocket, SIGNAL(connected()), this, SLOT(successConnection()));        //套接字成功连接设备后执行相应的处理函数

    clientSocket->connectToHost(QHostAddress(DEVICE_IP_ADDRESS), DEVICE_TCP_PORT);      //连接到设备

    while(1)
    {
        if(!clientSocket->waitForReadyRead(1000)) continue;                             //等待设备向PC发送报文

        char readBuf[2500]{0};

        recvLength = clientSocket->read(readBuf, 2500);                                 //读取报文

        qtb->append(readBuf);

        if(!outfile->open(QIODevice::WriteOnly|QIODevice::Text|QIODevice::Append))      //打开文件。如果文件不存在,会创建该文件。写入的数据为文本格式,且不覆盖原来的数据
        {
            qtb->append("Failed to write data to document!");
            break;
        }
        QString temp(readBuf);
        outfileStream<<temp<<endl;
        outfile->close();

        qtb->append("");
        qtb->moveCursor(QTextCursor::End);                                              //输出滚屏
    }
}

//析构函数
Message::~Message()
{
    clientSocket->deleteLater();
    quit();
    wait();
}

关于Timer,我现在认为应该是QTcpSocket的waitForReadyRead函数使用了Timer来实现定时的功能。虽然程序实行起来没有影响,但是有时候还是会报错,导致程序终止。所以想解决这些问题,看看程序能不能不会出错退出。再次感谢您!
离线kindboy18

只看该作者 5楼 发表于: 2015-08-01
回 pxzpxz1234 的帖子
pxzpxz1234:首先谢谢你的关注和回答!
第一个问题:我没有直接使用QTextBlock和QTextCursor,这两个应该是QTextBrowser里面的。我只使用了QTextBrowser里的append函数。虽然程序执行没有问题,不过我还是想弄清楚为什么会报这个问题。我确实在网页上看到过,跨线程发送带参数的信号时,会报 .. (2015-08-01 15:20) 

第一个问题:发送带参数的信号为何会出现这样的问题呢?我为何没有遇到过?是不是你的形参是自定义类型,如果是自定义类型要用qRegisterMetaType注册一下的
第二个问题:QTcpSocket读数据一次只能读8k,你可以用append(m_tcpSocket->readAll())这个试一下
第三个问题:可能是你析构函数里出了问题,你把析构的对象都注释起来看看

另外,你可以把代码贴上来,不然我也别人也不好帮你分析的
离线pxzpxz1234

只看该作者 4楼 发表于: 2015-08-01
回 yanwuyue 的帖子
yanwuyue:Qt信号最大的好处就是跨线程发送,不可以跨线程发射有参数的信号是从哪看到的,问题2楼已经分析了 (2015-08-01 10:36) 

是下面这个链接
http://blog.163.com/ljf_gzhu/blog/static/1315534402014910114742133/
也许是我理解错了?
离线pxzpxz1234

只看该作者 3楼 发表于: 2015-08-01
回 kindboy18 的帖子
kindboy18:第一个问题:是因为你可能定义了自定义数据类型,所以要用qRegisterMetaType()注册一下,另外为何不能跨线程发送有参的信号,我写过很多了,是可以的,肯定是你的问题!
第二个问题:你为何不开辟一个线程,比如说每个三分钟开辟一个新线程来写数据!
第三个问题:Timer你为何要在 .. (2015-08-01 08:56) 

首先谢谢你的关注和回答!

第一个问题:我没有直接使用QTextBlock和QTextCursor,这两个应该是QTextBrowser里面的。我只使用了QTextBrowser里的append函数。虽然程序执行没有问题,不过我还是想弄清楚为什么会报这个问题。我确实在网页上看到过,跨线程发送带参数的信号时,会报类似于这方面的错误。这个是网页链接:http://blog.163.com/ljf_gzhu/blog/static/1315534402014910114742133/
可能是我理解错误?

第二个问题:我使用QTcpSocket类的waitForReadyRead函数来判断是否接收到数据,一旦接收到数据就执行代码显示出来。因为我现在写的是类似于调试助手的程序,所以还是希望可以实时显示测量数据。难道真的是因为单位时间内数据量太大,append函数处理时就会缺失一些字符吗?

第三个问题:我的程序里没有使用Timer,但是它还是输出了这样的问题。所以我就不清楚这是为什么了。。

再次感谢你的回答!
离线yanwuyue

只看该作者 2楼 发表于: 2015-08-01
Qt信号最大的好处就是跨线程发送,不可以跨线程发射有参数的信号是从哪看到的,问题2楼已经分析了
离线kindboy18

只看该作者 1楼 发表于: 2015-08-01
第一个问题:是因为你可能定义了自定义数据类型,所以要用qRegisterMetaType()注册一下,另外为何不能跨线程发送有参的信号,我写过很多了,是可以的,肯定是你的问题!
第二个问题:你为何不开辟一个线程,比如说每个三分钟开辟一个新线程来写数据!
第三个问题:Timer你为何要在线程中启动,你可以给主界面发送一个信号,主界面接收到这个信号就开启定时器啊!
快速回复
限100 字节
 
上一个 下一个