Reputation: 2831
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?
Upvotes: 12
Views: 13233
Reputation: 1650
In addition to some of the other answers here (dynamically adding QML code from javascript based on a property, or using file selectors), another approach that may work if what you want is to have different QML component implementations, or disable a QML component, based on a compile time condition or setting, is to configure CMake to include alternate QML files at build time, and set QML_RESOURCE_ALIAS
to the desired QML component name (as referenced by other QML files) in the built executable's resources. For example:
Create two versions of a QML component, e.g. MyComponent_enabled.qml
which defines a component and MyComponent_disabled.qml
which is an empty item (just import QtQuick ; Item {}
.)
In CMakeLists, choose which one to use. E.g.
option(ENABLE_MY_COMPONENT "Enable MyComponent")
if(ENABLE_MY_COMPONENT)
set(MYCOMPONENT_QML_FILE MyComponent_enabled.qml)
else()
set(MYCOMPONENT_QML_FILE MyComponent_disabled.qml)
endif()
set_source_file_properties(${MYCOMPONENT_QML_FILE} PROPERTIES QT_RESOURCE_ALIAS MyComponent.qml)
qml_add_qml_module(yourapp
... other stuff ...
QML_FILES
... other QML files ...
${MYCOMPONENT_QML_FILE}
...
)
MyComponent {...}
to instantiate MyComponent
in other QML files.(This will of course baffle QtCreator a bit -- it will only show one or the other QML file in the file list etc. There may be ways to get QtCreator or other IDEs to know about both files but that's another topic.)
Upvotes: 0
Reputation: 2831
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
Upvotes: 8
Reputation: 3030
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 {
}
Upvotes: 4
Reputation: 4592
QML is not compiled but interpreted at run time, so preprocessor directives can't be used directly by QML.
But this is not a limitation, because it is possible to introduce properties to QML from c++ side, Thus directives can be placed on the C++ side to staff those properties, while decision making based on those properties, normal if/then
, with Javascript can be used in QML side.
Upvotes: 0
Reputation: 828
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
Upvotes: 1
Reputation: 5207
#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.
Upvotes: 2
Reputation: 24416
If you can refactor those parts into their own components, you have two options:
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.
Upvotes: 4