Search code examples
c++qtqmlqt-quickqt-design-studio

How to define C++ handled mouse clicks and other interactions in QML from within Qt Design Studio


I'm new to writing QtQuick/QML apps and I'm using Qt Design Studio. I've followed some examples that let me handle button clicks in C++ and it works fine... but I can't add the onClick handler to my QML from within Qt Design Studio, it says "Functions are not supported in a UI file". You can't even ignore this, it blocks the rendering of the preview.

Okay, so when I copy the QML files over to my Qt Creator project I can add "onClick" to the button in the QML file and it works fine... but I'm going to have HUNDREDS of these... and every time I want to test changes to the QML design I copy and overwrite the QML files from the Qt Design Studio project to my Qt Creator project. I can't be manually adding hundreds of these lines to the QML files each time I make a change.

What is the correct workflow here?

Here's some code, though I'm not sure how relevant it is since it's all pretty much boilerplate:

main.c

MyButtonClickHandler BCH;
view.rootContext()->setContextProperty("BCH", &BCH);

ui.qml

ButtonStyleA{
    text: "Button 1"
    onClicked: BCH.buttonClicked("Button 1")
}

myButtonClickHandler.h

class MyButtonClickHandler : public QObject {
    Q_OBJECT

    public:
    explicit MyButtonClickHandler(QObject *parent = nullptr);

    Q_INVOKABLE void buttonClicked(const QString str)
    {
        qDebug() << "Button was clicked! - " + str;
    }
};

Again this all works fine, my issue is the mere existence of the line "onClicked: BCH.buttonClicked("Button 1")" in "ui.qml" breaks Qt Design Studio and prevents the GUI preview from being drawn. There has to be a better way.


Solution

  • The simple answer here is that signal handlers and JS blocks are not supported in Qt Design Studio. Have a look at the UI Files documentation.

    The following features are not supported in .ui.qml files:

    • JavaScript blocks
    • Signal handlers

    The way of creating such a binding in QtDS that won't break the Form View (2D View)

    Button {
        text: "Button"
        onClicked: console.log("clicked")
    }
    

    would look as follows

    Button {
        id: button
        text: "Button"
    
        Connections {
            target: button
            onClicked: console.log("clicked")
        }
    }
    

    By using Connections the Form View renderer can ignore those JS blocks and be able to render the scene. Keep in mind this is only ignored by the Form View, but not by the Live Preview or the actual application.

    Another solution would be to use .qml instead of .ui.qml as it doesn't apply guard rails to guide the user to use QtDS supported features. That said, this solution could potentially break the Form View and the QtDS functionality in general as unsupported features won't be detected.

    When you pick one of the two solution you will face another problem with QtDS which is that C++ components aren't supported as they aren't compiled by the QML runtime. Hence the C++ types won't be recognized and QtDS will output errors. If you want to overcome this issue you could mock the C++ interface with custom QML types and when deploying replace them with their C++ counterparts.