如何在 QML 中模拟 C 风格的“ifdef”宏?

我需要关闭 QML 代码的某些部分,因为此代码已用于演示目的,它将在最终版本中删除。但是该产品将与这些演示功能一起使用很长时间,因此我不能使用具有演示功能的单独分支并不断地将所有新功能合并到该分支 - 这很不方便。所以让这段代码运行会很好,但在需要时很容易关闭和/或删除。在 C 和 C++ 中,我为此使用 ifdef 宏,但在 QML 中是否可以这样做?

stack overflow How to simulate C-style "ifdef" macro in QML?
原文答案
author avatar

接受的答案

正如@Mark建议的那样,可以在QML中使用上下文属性来决定是否启用了某个宏,但是他提供的示例并未解决这种情况,当您需要根据此决定实例化一个对象时,但仅在代码块内覆盖大小写。因此,我将提供我所做的完整示例。

在我的例子中,我只在编译期间定义了宏时才向 QML 公开一个类:

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

然后在 QML 方面,我检查这个宏并决定是否必须创建 SmtpClient 对象:

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
    }
}

我认为 Loader 也应该完成这项工作,因此您可以创建一个单独的组件,如果定义了 C++ 宏,则在 Loader 的 sourcesourceComponent 属性中引用它,但我不确定,因为不确定该组件是否会静态检查(在我的情况下) SmtpClient 类型是否可用


答案:

作者头像

它与#ifdef 不同,因为决策都发生在运行时,但我有时会使用这种方法,它给出了类似的使用模式。

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"));

然后,从项目中的任何 .qml 文件中

if (DEMO_MODE) {

} else {

}
作者头像

如果您可以将这些零件重构为自己的组件,则有两个选择:

  1. File selectors
  2. Loader

第一个选项可能比使用 #ifdef 的宏观更接近C风格 Loader 宏,因为它在文件级别上工作。但是,只要您不过分使用 Loader ,但是(例如,作为大视图中的代表),他们都应该做得很好。

作者头像

#ifdef 是预处理器指令,即在 C 或 C++ 编译器看到代码之前在构建时处理的东西。您可以使用您选择的文本操作语言执行相同的操作,在构建时处理 QML 文件,然后再进行处理,例如由 Qt 资源编译器。

作者头像

您还可以将 DebugC 类与函数一起使用

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

然后在qml中注册这个类

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

现在您可以轻松调用 stName.isDebuggingEnable() 方法来检查 QML 文件中是否启用了调试。这只是一个把戏

作者头像

QML 在运行时不是 compiled 而是 interpreted ,因此 QML 不能使用 preprocessor 指令。但这不是限制,因为可以从 c++ 端向 QML 引入属性,因此可以在 C++ 端放置指令来为这些属性配备人员,同时基于这些属性做出决策,正常 directly ,使用 Javascript可以在 QML 端使用。

作者头像

除了此处的一些其他答案(基于属性从 javascript 动态添加 QML 代码,或使用文件选择器),如果您想要拥有不同的 QML 组件实现或禁用 QML 组件,另一种方法可能会起作用,基于编译时条件或设置,将 CMake 配置为在构建时包含备用 QML 文件,并在构建的可执行文件的资源中将 QML_RESOURCE_ALIAS 设置为所需的 QML 组件名称(由其他 QML 文件引用)。例如:

  1. 创建一个 QML 组件的两个版本,例如 MyComponent_enabled.qml 定义了一个组件,而 MyComponent_disabled.qml 是一个空项(只是 import QtQuick ; Item {} 。)

  2. 在 CMakeLists 中,选择使用哪一个。例如。

    
    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}
    ...
    )


3. 使用  `MyComponent {...}`  在其他 QML 文件中实例化  `MyComponent` 。

(这当然会让 QtCreator 有点困惑——它只会在文件列表等中显示一个或另一个 QML 文件。可能有办法让 QtCreator 或其他 IDE 了解这两个文件,但这是另一个话题。)