Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to simulate C-style "ifdef" macro in QML?

Tags:

macros

qt

qt5

qml

I need to switch off some parts of QML code, because this code has been done for demo purposes and it will be removed in final release. But the product will be used with these demo features for long, so I can't use a separate branch with demo features and constantly merge all new features to that branch - it's just not convenient. So it'd be nice to have this code running but easy to switch off and/or remove when needed. In C and C++ I use ifdef macro for that, but is it possible to do the same in QML?

like image 361
rightaway717 Avatar asked Nov 14 '16 08:11

rightaway717


5 Answers

As @Mark suggested, context properties can be used in QML to decide in run time if some macro is enabled or not, but the example he provides does not address the case, when you need to instantiate an object based on this decision, but only covers case when inside a code block. So I will provide the full example of what I did.

In my case I exposed a class to QML only when a macro was defined during compile time:

main.cpp:

#ifdef SMTP_SUPPORT
qmlRegisterType<SmtpClientHelper>("com.some.plugin", 1, 0, "SmtpClient");
engine.rootContext()->setContextProperty("SMTP_SUPPORT", QVariant(true));
#else
engine.rootContext()->setContextProperty("SMTP_SUPPORT", QVariant(false));
#endif // SMTP_SUPPORT

Then on QML side I check this macro and decide if SmtpClient object must be created:

qml:

import QtQuick 2.0;
import com.some.plugin 1.0

Item {
    id: root
    // ...

    property var smtpClient // No inside any function, need to instantiate the object

    Component.onCompleted: {
        if (SMTP_SUPPORT) {
            smtpClient = Qt.createQmlObject('                                                 \
                import QtQuick 2.0;                                                           \
                import com.some.plugin 1.0;                                                   \
                SmtpClient {                                                                  \
                    id: smtpClient;                                                           \
                                                                                              \
                    function setSenderEmail(email) {                                          \
                        senderEmail = email;                                                  \
                        storage.save("common", "clientEmail", email);                         \
                    }                                                                         \
                }                                                                             \
            ', root, "SmtpClient");
        }
    }

    // Reference smtpClient normally, like if it was statically created
    TextInput {
        id: senderEmailLogin
        anchors.fill: parent
        font.pixelSize: Globals.defaultFontSize
        text: smtpClient ? smtpClient.senderEmail : ""
        onEditingFinished: if (smtpClient) smtpClient.setSenderEmail(text)
        activeFocusOnPress: true
    }
}

I think Loader should also do the job, so you could create a separate component, reference it in Loader's source or sourceComponent properties if C++ macro is defined, but I am not sure, because not sure if this component will be statically checked if (in my case) SmtpClient type is available

like image 68
rightaway717 Avatar answered Nov 02 '22 08:11

rightaway717


It's not the same as an #ifdef because the decision making all happens at runtime, but I sometimes use this approach, which gives a similar usage pattern.

main.cpp

    QQmlApplicationEngine engine;
#ifdef DEMO_MODE
    engine.rootContext()->setContextProperty("DEMO_MODE", QVariant(true));
#else
    engine.rootContext()->setContextProperty("DEMO_MODE", QVariant(false));
#endif
    engine.load(QUrl("qrc:/ui/MainQmlFile.qml"));

then, from any .qml file in the project

if (DEMO_MODE) {

} else {

}
like image 44
Mark Ch Avatar answered Nov 02 '22 07:11

Mark Ch


If you can refactor those parts into their own components, you have two options:

  1. File selectors
  2. Loader

The first option is perhaps a bit closer to a C-style #ifdef macro than using Loader, as it works at the file level. As long as you don't overuse Loader, though (e.g. as a delegate in a large view), they should both do the job fine.

like image 32
Mitch Avatar answered Nov 02 '22 07:11

Mitch


#ifdef is a preprocessor instructions, i.e. something processed at build time before the C or C++ compiler gets to see the code. You could do the same using your text manipulation language of choice, processing the QML files at build time before they get processed e.g. by the Qt resource compiler.

like image 41
Kevin Krammer Avatar answered Nov 02 '22 08:11

Kevin Krammer


You can also use class DebugC with function

Q_INVOKABLE bool isDebuggingEnable()
{
  #ifdef QT_DEBUG
    return true;
  #else
    return false;
  #endif
}

and then register this class in qml

viewer->rootContext()->setContextProperty("stName", DebugCObj)

now you can easily call stName.isDebuggingEnable() method for checking debugging is enable or not in your QML file. its just a trick

like image 1
SourabhKus Avatar answered Nov 02 '22 07:11

SourabhKus