4.QT4中的connect的实现

信号槽通过connect进行连接,connect的源码在qobject.cpp中

QT4中的connect的声明如下

static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType = Qt::AutoConnection);

使用时,一般是这样的

connect(sender, SIGNAL(destroyed()), this, SLOT(objectDestroyed()));

其中的SIGNAL和SLOT定义如下,在qobjectdefs.h中

# define SLOT(a)     "1"#a
# define SIGNAL(a)   "2"#a

#a 得到参数的字符串形式,对于槽函数的宏,前面加 "1",对于信号的宏,前面加 "2",则上面的connect代码就变成如下形式

connect(sender, "2destroyed()", this, "1objectDestroyed()");

connect具体实现如下

QMetaObject::Connection QObject::connect(const QObject *sender, const char *signal,
                                     const QObject *receiver, const char *method,
                                     Qt::ConnectionType type)
{
    if (sender == nullptr || receiver == nullptr || signal == nullptr || method == nullptr) {
        qWarning("QObject::connect: Cannot connect %s::%s to %s::%s",
                 sender ? sender->metaObject()->className() : "(nullptr)",
                 (signal && *signal) ? signal+1 : "(nullptr)",
                 receiver ? receiver->metaObject()->className() : "(nullptr)",
                 (method && *method) ? method+1 : "(nullptr)");
        return QMetaObject::Connection(0);
    }//检查sender、receiver、signal、method是否为空,为空,返回个空连接
    
    QByteArray tmp_signal_name;

    if (!check_signal_macro(sender, signal, "connect", "bind"))//检查signal字符串中是否有2,如果没有,空连接返回
        return QMetaObject::Connection(0);
        
    const QMetaObject *smeta = sender->metaObject();
    const char *signal_arg = signal;
    ++signal; //跳过2,指向具体的字符串
    QArgumentTypeArray signalTypes;
    Q_ASSERT(QMetaObjectPrivate::get(smeta)->revision >= 7);
    QByteArray signalName = QMetaObjectPrivate::decodeMethodSignature(signal, signalTypes);
    int signal_index = QMetaObjectPrivate::indexOfSignalRelative(
            &smeta, signalName, signalTypes.size(), signalTypes.constData());//计算信号的相对序号
    if (signal_index < 0) {
        // check for normalized signatures
        tmp_signal_name = QMetaObject::normalizedSignature(signal - 1);
        signal = tmp_signal_name.constData() + 1;

        signalTypes.clear();
        signalName = QMetaObjectPrivate::decodeMethodSignature(signal, signalTypes);
        smeta = sender->metaObject();
        signal_index = QMetaObjectPrivate::indexOfSignalRelative(
                &smeta, signalName, signalTypes.size(), signalTypes.constData());
    }//如果相对序号不能存在,normalized后,再计算一次信号函数的相对序号
    if (signal_index < 0) {
        err_method_notfound(sender, signal_arg, "connect");
        err_info_about_objects("connect", sender, receiver);
        return QMetaObject::Connection(0);
    }//如果normalized后,信号函数还不存在,返回空连接
    
    signal_index = QMetaObjectPrivate::originalClone(smeta, signal_index);
    signal_index += QMetaObjectPrivate::signalOffset(smeta);//将signal_index加上偏移,变为绝对序号

    QByteArray tmp_method_name;
    int membcode = extract_code(method);

    if (!check_method_code(membcode, receiver, method, "connect"))
        return QMetaObject::Connection(0);//检查槽函数是否有字符1
    const char *method_arg = method;
    ++method; //处理方法同信号

    QArgumentTypeArray methodTypes;
    QByteArray methodName = QMetaObjectPrivate::decodeMethodSignature(method, methodTypes);
    const QMetaObject *rmeta = receiver->metaObject();
    int method_index_relative = -1;
    Q_ASSERT(QMetaObjectPrivate::get(rmeta)->revision >= 7);
    switch (membcode) {//根据membcode是1还是2计算槽函数的相对序号,因为信号可以连接信号,所以在考虑槽函数的时候要处理槽函数是信号的情况
    case QSLOT_CODE:
        method_index_relative = QMetaObjectPrivate::indexOfSlotRelative(
                &rmeta, methodName, methodTypes.size(), methodTypes.constData());
        break;
    case QSIGNAL_CODE:
        method_index_relative = QMetaObjectPrivate::indexOfSignalRelative(
                &rmeta, methodName, methodTypes.size(), methodTypes.constData());
        break;
    }
    if (method_index_relative < 0) {//如果得到的槽函数的相对序号不合法,normalized并再计算槽函数的相对序号
        // check for normalized methods
        tmp_method_name = QMetaObject::normalizedSignature(method);
        method = tmp_method_name.constData();

        methodTypes.clear();
        methodName = QMetaObjectPrivate::decodeMethodSignature(method, methodTypes);
        // rmeta may have been modified above
        rmeta = receiver->metaObject();
        switch (membcode) {
        case QSLOT_CODE:
            method_index_relative = QMetaObjectPrivate::indexOfSlotRelative(
                    &rmeta, methodName, methodTypes.size(), methodTypes.constData());
            break;
        case QSIGNAL_CODE:
            method_index_relative = QMetaObjectPrivate::indexOfSignalRelative(
                    &rmeta, methodName, methodTypes.size(), methodTypes.constData());
            break;
        }
    }

    if (method_index_relative < 0) {//再次判断,如果槽函数的序号还是不合法的,返回空连接
        err_method_notfound(receiver, method_arg, "connect");
        err_info_about_objects("connect", sender, receiver);
        return QMetaObject::Connection(0);
    }

    if (!QMetaObjectPrivate::checkConnectArgs(signalTypes.size(), signalTypes.constData(),
                                              methodTypes.size(), methodTypes.constData())) {
        qWarning("QObject::connect: Incompatible sender/receiver arguments"
                 "\n        %s::%s --> %s::%s",
                 sender->metaObject()->className(), signal,
                 receiver->metaObject()->className(), method);
        return QMetaObject::Connection(0);
    }//检查信号函数和槽函数的参数是否兼容,不兼容,返回空连接

    int *types = 0;
    if ((type == Qt::QueuedConnection)
            && !(types = queuedConnectionTypes(signalTypes.constData(), signalTypes.size()))) {
        return QMetaObject::Connection(0);
    }//如果此时信号槽的连接方式是QueuedConnection,但是queuedConnectionTypes的返回值为空(信号的类型数据没有获取到),那么也返回一个空连接

#ifndef QT_NO_DEBUG
    QMetaMethod smethod = QMetaObjectPrivate::signal(smeta, signal_index);
    QMetaMethod rmethod = rmeta->method(method_index_relative + rmeta->methodOffset());
    check_and_warn_compat(smeta, smethod, rmeta, rmethod);
#endif
    QMetaObject::Connection handle = QMetaObject::Connection(QMetaObjectPrivate::connect(
        sender, signal_index, smeta, receiver, method_index_relative, rmeta ,type, types));//前面的check都没问题,进行连接,调用QMetaObjectPrivate::connect
    return handle;
}

QMetaObjectPrivate::connect也在qobject.cpp中

QObjectPrivate::Connection *QMetaObjectPrivate::connect(const QObject *sender,
                                 int signal_index, const QMetaObject *smeta,
                                 const QObject *receiver, int method_index,
                                 const QMetaObject *rmeta, int type, int *types)
//sender 是发送信号的对象;signal_index 信号绝对序号;smeta 是发送信号的对象的元对象;
//receiver 是信号的接收对象;method_index 是槽函数的相对序号;rmeta 是信号的接收对象的元对象;
//type 是连接类型;types 是指向的是入队时,信号的类型数据。
{
    QObject *s = const_cast<QObject *>(sender);
    QObject *r = const_cast<QObject *>(receiver);//去底层const

    int method_offset = rmeta ? rmeta->methodOffset() : 0;//加上偏移量之后,得到槽函数的绝对序号
    Q_ASSERT(!rmeta || QMetaObjectPrivate::get(rmeta)->revision >= 6);
    QObjectPrivate::StaticMetaCallFunction callFunction = rmeta ? rmeta->d.static_metacall : nullptr;//函数指针指向接收端元对象的qt_static_metacall(在MOC文件中实现)

    QOrderedMutexLocker locker(signalSlotLock(sender),
                               signalSlotLock(receiver));//互斥锁,为了多线程顺序访问

    QObjectPrivate::ConnectionData *scd  = QObjectPrivate::get(s)->connections.loadRelaxed();//获取sender的ConnectionData
    if (type & Qt::UniqueConnection && scd) {//如果连接类型有UniqueConnection并且sender的ConnectionData有数据
        if (scd->signalVectorCount() > signal_index) {//判断信号的绝对索引小于信号的数量
            const QObjectPrivate::Connection *c2 = scd->signalVector.loadRelaxed()->at(signal_index).first.loadRelaxed();

            int method_index_absolute = method_index + method_offset;

            while (c2) {
                if (!c2->isSlotObject && c2->receiver.loadRelaxed() == receiver && c2->method() == method_index_absolute)
                    return nullptr;
                c2 = c2->nextConnectionList.loadRelaxed();
            }//遍历ConnectionList,如果发现ConnectionList中的receiver和传入的receiver相等并且该connection中的方法索引和绝对索引相等,那么就认为不满足UniqueConnection,直接退出
        }
        type &= Qt::UniqueConnection - 1;
    }

    std::unique_ptr<QObjectPrivate::Connection> c{new QObjectPrivate::Connection};//创建一个QObjectPrivate::Connection并对其中的数据进行赋值
    c->sender = s;
    c->signal_index = signal_index;
    c->receiver.storeRelaxed(r);
    QThreadData *td = r->d_func()->threadData;
    td->ref();
    c->receiverThreadData.storeRelaxed(td);
    c->method_relative = method_index;
    c->method_offset = method_offset;
    c->connectionType = type;
    c->isSlotObject = false;
    c->argumentTypes.storeRelaxed(types);
    c->callFunction = callFunction;

    QObjectPrivate::get(s)->addConnection(signal_index, c.get());//根据信号的绝对序号,添加QObjectPrivate::Connection对象

    locker.unlock();//解锁
    QMetaMethod smethod = QMetaObjectPrivate::signal(smeta, signal_index);
    if (smethod.isValid())
        s->connectNotify(smethod);

    return c.release();
}

上述代码中的loadrelaxed调用的其实就是C++11中的atomic类中的load方法,返回的是其中类或结构中包含的数据,详细见http://www.cplusplus.com/reference/atomic/atomic/load/

addConnection实现如下

void QObjectPrivate::addConnection(int signal, Connection *c)
{
    Q_ASSERT(c->sender == q_ptr);
    ensureConnectionData();
    ConnectionData *cd = connections.loadRelaxed();
    cd->resizeSignalVector(signal + 1);//为新信号的Connectionlist分配内存

    ConnectionList &connectionList = cd->connectionsForSignal(signal);//找到信号的connectionList
    if (connectionList.last.loadRelaxed()) {//添加新的信号的connectionlist
        Q_ASSERT(connectionList.last.loadRelaxed()->receiver.loadRelaxed());
        connectionList.last.loadRelaxed()->nextConnectionList.storeRelaxed(c);
    } else {
        connectionList.first.storeRelaxed(c);
    }
    c->id = ++cd->currentConnectionId;//设置新的connection的id
    c->prevConnectionList = connectionList.last.loadRelaxed();//设置新的connection的prevConnectionList
    connectionList.last.storeRelaxed(c);

    QObjectPrivate *rd = QObjectPrivate::get(c->receiver.loadRelaxed());
    rd->ensureConnectionData();

    c->prev = &(rd->connections.loadRelaxed()->senders);
    c->next = *c->prev;//将新的connection添加到对应的信号的connectionlist中
    *c->prev = c;
    if (c->next)
        c->next->prev = &c->next;
}

connection与connectionlist的整体的结构图如下

 

 

参考

Qt5.14源码

https://qtguide.ustclug.org/

 

欢迎大家评论交流,作者水平有限,如有错误,欢迎指出

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
PyQt5.QtChart 是 PyQt5 的一个模块,它提供了一些强大的图表组件,可以帮助我们快速实现各种类型的图表,包括动态曲线图。 下面是一个简单的示例,演示如何使用 PyQt5.QtChart 实现动态曲线图: ```python from PyQt5.QtWidgets import QApplication, QMainWindow, QGridLayout, QWidget from PyQt5.QtChart import QChart, QChartView, QLineSeries from PyQt5.QtCore import Qt, QTimer import random class MainWindow(QMainWindow): def __init__(self): super().__init__() # 创建曲线图和曲线 self.chart = QChart() self.series = QLineSeries() self.chart.addSeries(self.series) # 设置图表标题和坐标轴标签 self.chart.setTitle("Dynamic Curve") self.chart.setAnimationOptions(QChart.SeriesAnimations) self.chart.createDefaultAxes() self.chart.axes()[0].setTitleText("X") self.chart.axes()[1].setTitleText("Y") # 创建图表视图 self.chart_view = QChartView(self.chart) self.chart_view.setRenderHint(QPainter.Antialiasing) # 创建布局并添加图表视图 layout = QGridLayout() layout.addWidget(self.chart_view) # 创建窗口并设置布局 central_widget = QWidget() central_widget.setLayout(layout) self.setCentralWidget(central_widget) # 创建定时器并连接到更新函数 self.timer = QTimer(self) self.timer.timeout.connect(self.update_plot) self.timer.start(50) # 初始化计数器和数据列表 self.count = 0 self.data = [] def update_plot(self): # 更新计数器和数据列表 self.count += 1 self.data.append(random.randint(0, 100)) # 更新曲线数据 self.series.clear() for i in range(len(self.data)): self.series.append(i, self.data[i]) # 设置坐标轴范围 self.chart.axisX().setRange(max(0, self.count-100), self.count) self.chart.axisY().setRange(0, 100) if __name__ == '__main__': app = QApplication([]) window = MainWindow() window.show() app.exec_() ``` 在这个例子,我们创建了一个 QMainWindow 窗口,并在其添加了一个 QChartView 视图,用于显示动态曲线图。我们还创建了一个 QTimer 定时器,并将其连接到 update_plot() 函数,以定期更新曲线图的数据。 在 update_plot() 函数,我们生成一个随机数,并将其添加到数据列表。然后,我们清除曲线的所有数据,重新将数据添加到曲线。最后,我们根据数据的数量调整坐标轴的范围。 请注意,我们使用了一个计数器来记录数据的数量,以便我们可以根据需要滚动我们的曲线图。在这个例子,我们每次只显示最近的 100 个数据点。 运行这个示例程序,你将会看到一个动态曲线图,显示随机生成的数值。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值