QObject成员函数connect()函数

1:首先要链接的两个类必须继承于QObject,同时添加 Q_OBJECT。

2:在qt中QObject::connect中填写的signal和slot函数,一定要填写参数类型。

因为类中的函数可以,也就是,重载函数名一样,参数不一样,如果QObject::connect中的函数没有参数类型,则无法正确连接。

3:QObject::connect中的signal 和 slot 函数一定要有参数类型, 但是,不可以有参数:

You must use the SIGNAL() and SLOT() macros when specifying the signal and the method, for example:


QLabel *label = new QLabel;

QScrollBar *scrollBar = new QScrollBar;

QObject::connect(scrollBar, SIGNAL(valueChanged(int)), label, SLOT(setNum(int)));

This example ensures that the label always displays the current scroll bar value. Note that the signal and slots parameters must not contain any variable names, only the type. E.g. the following would not work
and return false:


// WRONG -- 必须有参数类型,但是不能有变量名

QObject::connect(scrollBar, SIGNAL(valueChanged(int value)), label, SLOT(setNum(int value)));

4:在参数部分,如果定义了:


typedef unsigned char BYTE;

//signal:

void  Class1::setBuf( BYTE * );

 

//pulic slots: 

void Class2::setBuf( unsigned char * )

{

    MessageBoxQ( "消息响应" );

}

这样,通常可以用BYTE替换unsigned char ;

但是在


QObject::connect( pClass1, SIGNAL(setBuf(BYTE *)), pClass2, SLOT(setBuf(unsigned char *)));

编译时不会有问题的,但是消息无法响应。

修改方法:

将void Class2::setBuf( unsigned char *) 修改为 void Class2::setBuf( BYTE *)


QObject::connect( pClass1, SIGNAL(setBuf(BYTE *)), pClass2, SLOT(setBuf(BYTE *)));

可能错误的原因:

SIGNAL 和 SLOT是将其内容转为字符串,所以,如果消息传递前 函数和参数 是按照字符串严格比较话,那么 “BYTE” 和“unsigned char” 就不同了。具体原因需要分析源码

5:关于 QObject::connect 函数声明:


QMetaObject::Connectionconnect(const QObject * sender, const char * signal, const QObject * receiver, const char * method, Qt::ConnectionType type = Qt::AutoConnection);

从这里可以看出: 最后一个参数是设置连接类型,默认参数是Qt::AutoConnection;

Qt::ConnectionType描述:


enum Qt::ConnectionType

 

This enum describes the types of connection that can be used between signals and slots. In particular, it determines whether a particular signal is delivered to a slot immediately or queued for delivery at a later time.

 

Constant                           Value                                   Description

Qt::AutoConnection               0(default)       If the signal is emitted from a different thread than the receiving object, the signal is queued, behaving as Qt::QueuedConnection. Otherwise, the slot is invoked directly, behaving as Qt::DirectConnection. The type of connection is determined when the signal is emitted.

Qt::DirectConnection                 1            The slot is invoked immediately, when the signal is emitted.

Qt::QueuedConnection                 2            The slot is invoked when control returns to the event loop of the receiver‘s thread. The slot is executed in the receiver‘s thread.

Qt::BlockingQueuedConnection         3            Same as QueuedConnection, except the current thread blocks until the slot returns. This connection type should only be used where the emitter and receiver are in different threads.

这里注意:Qt::BlockingQueuedConnection 用于“线程间”阻塞执行;

如果在线程间用Qt::DirectConnection,(Qt::DirectConnection 使用在同一线程中链接作为参数) 虽然会阻塞执行,但是slot函数中如果有UI类的话会提示错误;

如果为默认函数参数,触发消息后,立即执行后面的代码,然后某一时刻再执行slot函数,这样非常不稳定,不安全;

当然还是要根据具体需求设置参数。

6:connect中的常量参数,引用参数,一般参数:

(1)、SIGNAL函数,常量引用参数, SLOT函数的参数一般链接也应该是常量引用参数;如:


signals:

void resultReady( const QString & s);

 

public slots:

void slot_string( const QString & str )

{

    // str = "123";

    qDebug()<<str;

}

 

connect(this, SIGNAL(resultReady(const QString&)), this, SLOT(slot_string(const QString&)), Qt::DirectConnection);

因为上述是const参数,所以slot中无法修改参数数据。如果希望修改数据,有两个方法:

(1): 赋值一个新的变量应用;

(2): 修改slot函数参数:


void slot_string( QString str )

{

    str = "123";

    qDebug()<<str;

}

connect(this, SIGNAL(resultReady(const QString&)), this, SLOT(slot_string(QString)) , Qt::DirectConnection);

注:大部分的Qt的SIGNAL函数都是常量引用参数。

(2)、 SIGNAL函数, 引用参数, SLOT函数的参数也可以是 引用参数 或 一般参数;

(3)、SIGNAL函数和SLOT函数都是一般参数;

说明:

A、这里主要讲的是区别就是在connect的时候,SGNAL ,SLOT的参数在引用于非引用对效率的影响和参数数据的修改;

B、在线程间参数传递的时候,SGNAL和SLOT位于两个不同的线程,如果connect的链接类型是Qt::QueuedConnection非阻塞运行,SLOT的参数是一般参数,那么SLOT函数的参数就是一个数据copy,在线程emit SGNAL后,如果线程中作为从参数的数据丢失或改变,但是SLOT函数用因为有数据备份,所以SLOT函数中的参数数据不会受到影响。

7:关于connect函数:

connect函数后多种重载方法,常用的有:

方法一:


QMetaObject::Connection QObject::connect(const QObject * sender, const char * signal, const QObject * receiver, const char * method, Qt::ConnectionType type = Qt::AutoConnection) [static]

Example:


QLabel *label = new QLabel;

QScrollBar *scrollBar = new QScrollBar;

QObject::connect( scrollBar, SIGNAL(valueChanged(int)), label, SLOT(setNum(int)) );

方法二:


QMetaObject::Connection QObject::connect(const QObject * sender, PointerToMemberFunction signal, const QObject * receiver, PointerToMemberFunction method, Qt::ConnectionType type) [static]

Example:


QLabel *label = new QLabel;

QLineEdit *lineEdit = new QLineEdit;

QObject::connect( lineEdit, &QLineEdit::textChanged, label, &QLabel::setText );

//在此程序中, lineEdit参数自动传递到label中的setText()槽中

说明:

建议用方法2,从应用方便讲:

(1):这个方法不需要填写参数类型。

(2):在上述“第6点”中,不论SIGNAL函数和SLOT函数是引用参数或非引用参数,会自动根据实际情况进行参数传递(实参或形参)。

对于方法二,另外再列举几段代码:


Class DoubanFM : public QObject

{

signals:

    void receivedNewList(const QList<DoubanFMSong>& songs);

private slots:

    void onReceivedNewList(QNetworkReply *reply);

};

 

void DoubanFM::onReceivedNewList(QNetworkReply *reply) {

    QTextCodec *codec = QTextCodec::codecForName("utf-8");

    QString all = codec->toUnicode(reply->readAll());

 

    QList<DoubanFMSong> songs;

    QJsonParseError parseerr;

    QVariant result = QJsonDocument::fromJson(all.toUtf8(), &parseerr).toVariant();

 

    if (parseerr.error == QJsonParseError::NoError) {

        QVariantMap obj = result.toMap();

        if (obj["r"].toInt() != 0) {

            if (obj["err"].toString() == "expired") {

                qDebug() << Q_FUNC_INFO << "User expired. Relogin";

                userReLogin();

            }

            else

                qDebug() << Q_FUNC_INFO << "Err" << obj["err"].toString();

            reply->deleteLater();

            return;

        }

        QVariantList songList = obj["song"].toList();

        foreach(const QVariant& item, songList) {

            QVariantMap song = item.toMap();

            DoubanFMSong s;

            s.album = song["album"].toString();

            s.picture = song["picture"].toString();

            s.ssid = song["ssid"].toString();

            s.artist = song["artist"].toString();

            s.url = song["url"].toString();

            s.company = song["company"].toString();

            s.title = song["title"].toString();

            s.public_time = song["public_time"].toString();

            s.sid = song["sid"].toUInt();

            s.aid = song["aid"].toUInt();

            s.albumtitle = song["albumtitle"].toString();

            s.like = song["like"].toBool();

            songs.push_back(s);

        }

    }

    emit receivedNewList(songs);

    reply->deleteLater();

}

此段代码从豆瓣fm的Qt实现代码中摘录的,该槽函数获取到用户的getNewList请求后,处理豆瓣API远端传来的数据后,将数据记录在const QList<DoubanFMSong>& songs中,同时发射receivedNewList信号。

现在当豆瓣fm的播放器接收到该信号后,就会对newList进行处理,下面是处理代码:


DoubanPlayer::DoubanPlayer(QObject *parent) :

    QObject(parent),

    doubanfm(DoubanFM::getInstance()),

    _channel(-INT_MAX), _volume(0), _can_control(true),

    bufplaylist(nullptr), _kbps(64)

{

    //匿名函数 [this](){}

    connect(&doubanfm, &DoubanFM::receivedNewList, [this] (const QList<DoubanFMSong>& rcvsongs) {

        this->songs = rcvsongs;

        qDebug() << "Received new playlist with" << rcvsongs.size() << "songs";

        QMediaPlaylist *playlist = player.playlist();

        if (playlist == nullptr) playlist = new QMediaPlaylist(&player);

        playlist->clear();

        for (const DoubanFMSong& song : this->songs) {

            playlist->addMedia(QUrl(song.url));

        }

        if (player.playlist() == nullptr) {

            connect(playlist, SIGNAL(currentIndexChanged(int)), this, SLOT(currentIndexChanged(int)));

            // FIXME: Crash on KDE4.9 libkdecore.so.5

            // Segmentation Fault by unknown reason

            player.setPlaylist(playlist);

        }

        if (player.state() != QMediaPlayer::PlayingState)

            player.play();

        setCanControl(true);

    });

}

对上面的代码,有几个地方需要着重进行阐述:

(1)、匿名函数

对于C++匿名函数,可以参考C++11匿名函数

其中有一个对匿名函数的格式声明:


[captures](params)

{

    Statments;

}

对于captures有如下规则:


[]        不截取任何变量

[&]       截取外部作用域中所有变量,并作为引用在函数体中使用

[=]       截取外部作用域中所有变量,并拷贝一份在函数体中使用

[=, &foo] 截取外部作用域中所有变量,并拷贝一份在函数体中使用,但是对 foo 变量使用引用

[bar]     截取 bar 变量并且拷贝一份在函数体重使用,同时不截取其他变量

[this]    截取当前类中的 this 指针。如果已经使用了 & 或者 = 就默认添加此选项。

因此当使用this时,实际指代的该DoubanPlayer类,由该类执行匿名函数,该匿名函数即为槽函数。

(2)、connect槽函数的一些小问题

关于参数传递问题,由connect语句可知,该connect语句采用上述第二种方法,那么在传递参数时,signal的参数songs默认传递给slot的rcvsongs。

时间: 2024-11-04 04:06:38

QObject成员函数connect()函数的相关文章

QT QObject::connect函数的学习

从Qobject(QObject.h)源码中可以看到QObject::connect的定义是这样的: [cpp] view plaincopy static bool connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType = #ifdef qdoc Qt::AutoConnection #else #ifdef QT3_S

C++:运算符重载函数之成员运算符重载函数

5.2.3 成员运算符重载函数 在C++中可以把运算符重载函数定义为某个类的成员函数,称之为成员运算符重载函数. 1. 定义成员运算符重载函数的语法形式 (1)在类的内部,定义成员运算符重载函数的格式如下: 函数类型 operator 运算符(形参表) {       函数体 } (2)成员运算符重载函数也可以在类中声明成员函数的原型,在类外定义. 在类的内部,声明成员运算符重载函数原型的格式如下: class X{      ...      函数类型 operator运算符(参数表); };

【转载】C/C++杂记:深入理解数据成员指针、函数成员指针

原文:C/C++杂记:深入理解数据成员指针.函数成员指针 1. 数据成员指针 对于普通指针变量来说,其值是它所指向的地址,0表示空指针.而对于数据成员指针变量来说,其值是数据成员所在地址相对于对象起始地址的偏移值,空指针用-1表示.例: 代码示例:   2. 函数成员指针 函数成员指针与普通函数指针相比,其size为普通函数指针的两倍(x64下为16字节),分为:ptr和adj两部分. (1) 非虚函数成员指针 ptr部分内容为函数指针(指向一个全局函数,该函数的第一个参数为this指针),ad

linux 非阻塞 connect函数

开发测试环境:虚拟机CentOS,windows网络调试助手        非阻塞模式有3种用途        1.三次握手同时做其他的处理.connect要花一个往返时间完成,从几毫秒的局域网到几百毫秒或几秒的广域网.这段时间可能有一些其他的处理要执行,比如数据准备,预处理等.        2.用这种技术建立多个连接.这在web浏览器中很普遍.        3.由于程序用select等待连接完成,可以设置一个select等待时间限制,从而缩短connect超时时间.多数实现中,connec

connect函数的用法

无论流式套接字(如TCP)还是数据报(如UDP),均可以使用connect函数.对于流式套接字,使用connect函数后,建立固定地址的连接,之后可以使用send/rev函数进行数据收发.对于数据报,可以不使用connect函数进行连接.此时需要使用sendto/revfrom函数进行收发,且每一次收发都要指明收发地址.也可以像流式套接字那样使用connect函数建立固定连接,然后使用send/rev函数进行收发.此时不需要每次收发都指定地址,但是也意味着只能向一个固定地址收发数据.另外注意,一

connect函数详解

不得不说,客户端的connect函数和服务端的accept函数是一对好基友,如果客户端没有去connect, 那么服务端的accept会一直在那里傻傻地痴痴地等待,我们先来看看connect函数的原型吧: WINSOCK_API_LINKAGE int WSAAPI connect( SOCKET s, const struct sockaddr FAR * name, int namelen ); 第一个参数是客户端的套接字(表明即将发起连接请求),第二个参数是服务端的套接字所在的“地方”(“

成员函数的函数配接器

在STL标准库中除了提供常规的函数配接器外,还提供了两个针对成员函数的函数配接器,其主要功能是通过这些配接器,你可以针对每一个元素直接去调用其成员函数. 这样的成员函数配接器有两个,分别是: mem_fun_ref(op) 调用对象的成员函数op mem_fun(op) 调用对象指针的成员函数op 这两个函数配接器都是去调用对象中的成员函数op,对于这两个成员函数,以前是只能调用对象的const成员函数,但现在的情况好像有所改变,对所有的成员函数都可以调用了. 具体的示例如下: #include

sdut 3-6 静态数据成员与静态成员函数

3-6 静态数据成员与静态成员函数 Time Limit: 1000MS Memory limit: 65536K 题目描述 通过本题目的练习可以掌握静态数据成员和静态成员函数的用法 要求设计一个点类Point,它具有两个double型的数据成员x,y.和一个静态数据成员count ,用以记录系统中创建点对象的数目.为该类设计构造函数和析构函数,在其中对count的值做修改,体现点的数目的动态变化.并为其添加一个静态成员函数用以输出count的值:成员函数showPoint()用于输出点的信息.

c++ 指向类成员函数的函数指针

// ConsoleApplication34.cpp : 定义控制台应用程序的入口点. // #include "stdafx.h" #include <iostream> using namespace std; class Parent { public: Parent() { //cout << "我是爹" << endl; } virtual void print() { cout << "我是爹&