QMetaObject之invokeMethod说明和使用

 

QMetaObject之invokeMethod 点滴记录(有时间在看看)

分类: QT编程

目录(?)[+]

QMetaObject之invokeMethod 点滴记录
2010-10-18 16:53

起源

C++ GUI Qt4 编程 一书多线程部分提到invokeMethod的用法

QMetaObject::invokeMethod(label, SLOT(setText(const QString&)), Q_ARG(QString, "Hello"));

而 Qt Manual 中介绍却是

  • You only need to pass the name of the signal or slot to this function, not the entire signature. For example, to asynchronously invoke the animateClick() slot on a QPushButton, use the following code:

QMetaObject::invokeMethod(pushButton, "animateClick");

这可怎么办?一个是官方的图书,一个是官方的Manual。是否意味着两种方式都可以呢,还是说Qt的早期版本用的是前者?

查 Qt4.7/Qt4.6/Qt4.5/Qt4.4/Qt4.3/Qt4.2/Qt4.1/Qt4.0 ,结果发现都没有提到前面的用法。是不是书的出错呢?网上搜一下:确实有人抱怨它不工作

测试

本着事实就是的精神,还是先写个程序测试一下:

#include <QtCore/QObject> 
#include <QtCore/QDebug> 
#include <QtCore/QCoreApplication>  
class Test : public QObject 
{ 
    Q_OBJECT 
public: 
    Test(QObject * parent):QObject(parent) 
    { 
        connect(this, SIGNAL(sig1(QString)), SLOT(slot1(QString))); 
        QMetaObject::invokeMethod(this, "sig1", Q_ARG(QString, "constructor")); 
    } 
    Q_INVOKABLE void method1(const QString& t) 
    { 
        qDebug()<<"from method:"<<t; 
    } 
 
signals: 
    void sig1(const QString& t); 
 
public slots: 
    void slot1(const QString& t) 
    { 
        qDebug()<<"from slot:"<<t; 
    } 
 };
 #include "main.moc" 
 int main(int argc, char *argv[]) 
{ 
    QCoreApplication a(argc, argv); 
    Test obj(0); 
    QMetaObject::invokeMethod(&obj, "slot1", Q_ARG(QString, "Hello")); 
    QMetaObject::invokeMethod(&obj, "method1", Q_ARG(QString, "Hello")); 
    QMetaObject::invokeMethod(&obj, SLOT(slot1(QString)), Q_ARG(QString, "Hello with SLOT")); 
    QMetaObject::invokeMethod(&obj, METHOD(method1(QString)), Q_ARG(QString, "Hello with METHOD")); 
    return a.exec(); 
}

确实如他人所说,SLOT这种用法不工作

from slot: "constructor" 
from slot: "Hello" 
from method: "Hello" 
QMetaObject::invokeMethod: No such method Test::1slot1(QString)(QString) 
QMetaObject::invokeMethod: No such method Test::0method1(QString)(QString)

源码

顺便看看源码吧

bool QMetaObject::invokeMethod(QObject *obj, 
                               const char *member, 
                               Qt::ConnectionType type, 
                               QGenericReturnArgument ret, 
                               QGenericArgument val0, 
                               QGenericArgument val1, 
                               QGenericArgument val2, 
                               QGenericArgument val3, 
                               QGenericArgument val4,
                               QGenericArgument val5, 
                               QGenericArgument val6, 
                               QGenericArgument val7, 
                               QGenericArgument val8, 
                               QGenericArgument val9) 
{ 
    if (!obj) 
        return false; 
 
    QVarLengthArray<char, 512> sig; 
    int len = qstrlen(member); 
    if (len <= 0) 
        return false;

生成函数原型字符串(从这儿可以看到书中方法不工作的原因)

    sig.append(member, len); 
    sig.append('('); 
 
    const char *typeNames[] = {ret.name(), val0.name(), val1.name(), val2.name(), val3.name(), 
                               val4.name(), val5.name(), val6.name(), val7.name(), val8.name(), 
                               val9.name()}; 
     int paramCount; 
    for (paramCount = 1; paramCount < MaximumParamCount; ++paramCount) { 
        len = qstrlen(typeNames[paramCount]);  
       if (len <= 0) 
            break; 
        sig.append(typeNames[paramCount], len); 
        sig.append(','); 
    } 
    if (paramCount == 1) 
        sig.append(')'); // no parameters 
    else 
        sig[sig.size() - 1] = ')'; 
    sig.append('\0');

在元对象系统中看该函数信息是否存在

    int idx = obj->metaObject()->indexOfMethod(sig.constData()); 
    if (idx < 0) { 
        QByteArray norm = QMetaObject::normalizedSignature(sig.constData()); 
        idx = obj->metaObject()->indexOfMethod(norm.constData()); 
    } 

    if (idx < 0 || idx >= obj->metaObject()->methodCount()) { 
        qWarning("QMetaObject::invokeMethod: No such method %s::%s",                  obj->metaObject()->className(), sig.constData()); 
        return false; 
    }

获得相应的 QMetaMethod,调用其 invoke 方法

    QMetaMethod method = obj->metaObject()->method(idx); 
    return method.invoke(obj, type, ret, 
                         val0, val1, val2, val3, val4, val5, val6, val7, val8, val9); 
}

QMetaMethod::invoke

接着看看它的源码,首先:

* 如果指定了返回值,检查返回值的类型是否和QMetaMethod 的中的一致

if (returnValue.data()) { 
        const char *retType = typeName(); 
        if (qstrcmp(returnValue.name(), retType) != 0) { 
            // normalize the return value as well  
           // the trick here is to make a function signature out of the return type 
            // so that we can call normalizedSignature() and avoid duplicating code 
            QByteArray unnormalized; 
            int len = qstrlen(returnValue.name()); 
             unnormalized.reserve(len + 3); 
            unnormalized = "_("; 
       // the function is called "_" 
            unnormalized.append(returnValue.name()); 
            unnormalized.append(')'); 
             QByteArray normalized = QMetaObject::normalizedSignature(unnormalized.constData()); 
            normalized.truncate(normalized.length() - 1); // drop the ending ')' 
             if (qstrcmp(normalized.constData() + 2, retType) != 0) 
                return false; 
        } 
    }
  • 为了利用现有的代码来规范化这儿返回值的类型,这儿构造了一个函数_(typeOfReturn)

* 检查参数个数,传递的参数是否不少于需要的参数

* 检查Connection的类型,处理AutoConnection

// check connection type 
    QThread *currentThread = QThread::currentThread(); 
    QThread *objectThread = object->thread(); 
    if (connectionType == Qt::AutoConnection) { 
        connectionType = currentThread == objectThread 
                         ? Qt::DirectConnection 
                         : Qt::QueuedConnection; 
    }
  • 对于 直连的,直接调 metacall,它进而去调用对象的 qt_metacall

if (connectionType == Qt::DirectConnection) { 
        return QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, methodIndex, param) < 0;
  • 对于 Queued 的连接,post 相应的事件,进而转到对象的event()函数中

if (connectionType == Qt::QueuedConnection) { 
            QCoreApplication::postEvent(object, new QMetaCallEvent(methodIndex, 
                                                                   0, 
                                                                   -1, 
                                                                   nargs, 
                                                                   types, 
                                                                   args));
  • 对于 bolckedqueued 的连接,使用了信号量

            QSemaphore semaphore; 
            QCoreApplication::postEvent(object, new QMetaCallEvent(methodIndex, 
                                                                   0, 
                                                                   -1, 
                                                                   nargs, 
                                                                   types, 
                                                                   args, 
                                                                   &semaphore)); 
            semaphore.acquire();
  • 2
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
`QMetaObject.invokeMethod()`是PyQt5中一个非常有用的函数,可以在主线程中执行一些耗时的操作,避免阻塞UI线程。具体用法如下: ```python QMetaObject.invokeMethod(receiver, method, Qt.ConnectionType, argument, argument_type, delay) ``` 其中,各参数含义如下: - `receiver`: 接收信号的对象。 - `method`: 要调用的方法名。 - `Qt.ConnectionType`: 连接类型,可以是Qt.AutoConnection、Qt.DirectConnection、Qt.QueuedConnection、Qt.BlockingQueuedConnection等。 - `argument`: 传递的参数。 - `argument_type`: 参数类型。 - `delay`: 延时,单位为毫秒。 需要注意的是,如果要在主线程中执行一个函数,需要保证该函数是可重入的,即不会引起线程安全问题。 下面是一个简单的例子,展示了`QMetaObject.invokeMethod()`的用法: ```python from PyQt5.QtCore import QMetaObject, Qt, pyqtSlot, QObject class Worker(QObject): finished = pyqtSignal() def __init__(self): super().__init__() def do_work(self): # 模拟耗时操作 for i in range(1000000): pass # 发送信号 self.finished.emit() class MainWindow(QWidget): def __init__(self): super().__init__() self.worker = Worker() self.worker.finished.connect(self.on_worker_finished) layout = QVBoxLayout() self.button = QPushButton('Start') self.button.clicked.connect(self.on_button_clicked) layout.addWidget(self.button) self.setLayout(layout) def on_button_clicked(self): # 在主线程中调用do_work函数 QMetaObject.invokeMethod(self.worker, 'do_work', Qt.QueuedConnection) def on_worker_finished(self): # 在主线程中更新UI self.button.setText('Finished') if __name__ == '__main__': app = QApplication(sys.argv) window = MainWindow() window.show() sys.exit(app.exec_()) ``` 在这个例子中,`Worker`类中的`do_work()`函数模拟了一个耗时的操作,在`MainWindow`的`on_button_clicked()`函数中使用`QMetaObject.invokeMethod()`将该函数放入消息队列中,等待主线程空闲时执行。当`Worker`对象完成工作时,会发出一个信号,该信号连接到`MainWindow`的`on_worker_finished()`函数中,更新UI。这样,即使在耗时操作进行时,UI仍然能够响应用户的操作。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值