Qt World Summit: Register Today!

Hash table of Slots?

  • I would like to be able to create a hash table of slots so that functions can be assigned to action objects by name. Is this possible?

    For example, I would be able to map a connection as follows (where actionXML is action properties read from xml and slotTable is the hypothetical hash table of class functions that is created before the code below is reached):
    QAction myAction = new QAction(QIcon(actionXML.iconPath(), actionXML.text());
    connect(myAction, SIGNAL(triggered()), this, SLOT(slotTable.getMethod(actionXML.callbackSlotName())));@

  • Moderators

    Why do you need a hash table here?
    Can't you just do directly this?
    connect(myAction, SIGNAL(triggered()), this, actionXML.callbackSlotName());

  • I wasn't aware that worked. I thought SLOT() was a macro that returned a string for the sake of the compiler but in reality you still need to send the function to the SLOT Macro.

    For example if I have a class method named CallMe() I cannot use:
    @connect(myAction, SIGNAL(triggered()), this, "CallMe");@

    I actually tried that once and it didn't work. Neither did "CallMe()." I was told to use the SLOT macro. If I am able to just send a string in connect, how come the above line doesn't work?

  • Moderators

    SLOT macro is not magic. It's a macro ;) A macro that stringifies whatever you give it (with a little twist). For example SLOT ( whatever() ) will result in a string "1whatever()" (yes, there's a "1" in front in the current implementation). Of course you shouldn't rely on an implementation detail, but there's no harm in doing something like this:
    const char* methodNameSlottified = SLOT(foo());
    //you can print it somewhere if you want to see what it does

    connect(bar, SIGNAL(barSignal()), target, methodNameSlottified);

    Anyway, there's a connect() overload that takes QMetaMethod, so if it were me I would go for storing in a file the method name returned by the QMetaObject of that object, and then use something like this to connect it:
    const char* slotNameFromFile = ...
    QMetaObject* mo = yourObject->metaObject();
    QMetaMethod theSlot = mo->method(mo->indexOfSlot(slotNameFromFile));
    connect(src, signal, yourObject, theSlot);

  • Let me give a specific example of what I am trying to accomplish. I am trying to write a plugin interface that defines a QToolBar derived object using xml. All buttons/actions on the bar or menus that go on the bar follow a certain format. For example. All actions that represent plugin options on a menu to be checked/unchecked have the following format:

    <Text>Show Detailed Information</Text>

    When the above object on a menu is clicked, I want it to call the "onTextOptions" method of the plugin object. Each plugin will have a different set of callbacks since each plugin has different features.

    So basically an option is a QAction that gets added to an "option" hash table and is indexed by its "Name". The QAction text is defined by the "Text" tag and the icon is a custom check-mark when it is checked and blank when it isn't.

    Now I just need to connect it to the function with the name specified in the "slotID" tag.

    So according to what you have said... I would do this? (assume optionXML is the option xml just read from the file)

    @MyQAction myAction = new MyQAction(optionXML.text()); // a custom QAction class actually handles which icon is shown based on its state.

    const char* slotNameFromFile = optionXML.slotName(); //onTextOptions in example

    QMetaObject* mo = myPluginObject->metaObject();
    QMetaMethod theSlot = mo->method(mo->indexOfSlot(slotNameFromFile));
    connect(myAction, SIGNAL(triggered()), myPluginObject, theSlot);@

    Will this result in the desired call?

  • Moderators

    Actually I was a little brief. I hoped you could fill in the blanks with the help of documentation.

    So first step is to have a proper method name in the XML file. You've got two options here: just the name (e.g. "onTextOptions") or the full "QMetaObject signature":http://doc.qt.io/qt-5/qmetaobject.html#normalizedSignature (e.g. "onTextOptions()"). The latter is better in case of overloaded slots with different arguments.

    I guess your XML will be generated manually or by other means, but if you need to enumerate slot signatures of some object this is how you do it:
    auto mo = metaObject();
    for(int i=0; i < mo->methodCount(); ++i)
    if(mo->method(i).methodType() == QMetaMethod::Slot)
    qDebug() << mo->method(i).methodSignature();

    //you can replace methodSignature() with name() if you just want the name

    Now, to the second part. Read that in and pass to connect.
    This will be different, depending on whether you have full signature or just the name.
    The case with full signature:
    const char* slotNameFromFile = ...
    QMetaObject* mo = myPluginObject->metaObject();
    QMetaMethod theSlot = mo->method(mo->indexOfSlot(slotNameFromFile));
    connect(myAction, theSignalMetaMethod, myPluginObject, theSlot);
    So almost like you had it, except for a small detail. Take a look at "connect signatures":https://qt-project.org/doc/qt-5-snapshot/qobject.html#static-public-members. There is one that takes const char* for signal/slot and one that takes QMetaMethod for signal/slot. Unfortunately there is no mixed versions e.g const char* for the signal and QMetaMethod for the slot. So you need to get the QMetaMethod for the signal as well (just look it up in the methods list similar to enumerating the slots).

    If you want to store just the name, without full signature then you need to look that name up in the list of methods names (again similar to the above) to get the QMetaMethod back.

  • Ok thanks a bunch! I will research this stuff and ask later if I have any questions.

Log in to reply