Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. QML and Qt Quick
  4. QML-Dialog with flexible method calls possible?
Forum Updated to NodeBB v4.3 + New Features

QML-Dialog with flexible method calls possible?

Scheduled Pinned Locked Moved Solved QML and Qt Quick
4 Posts 2 Posters 379 Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • S Offline
    S Offline
    SeDi
    wrote on 12 Jun 2021, 21:22 last edited by SeDi 6 Dec 2021, 21:48
    #1

    This idea seems intriguing for me, but I think it might be a naive question to ask. Please excuse, if this is the case.

    Is it possible to have a QML Dialog call C++ methods that can be changed at runtime?
    Simplified code idea:

    Let's say we have two sets of methods in C++:

    void Controller:numberOne();
    void Controller:numberTwo();
    
    void Controller:characterOne();
    void Controller:characterTwo();
    

    And we have a QML Dialog

    Dialog {
        property var firstMethod: Controller.characterOne()
        property var secondMethod: Controller.characterTwo();
    
        Button { 
            text = "first"
            onClicked: firstMethod
       }
        Button { 
            text = "second"
            onClicked: secondMethod
       }
    }
    

    I can open a QML Dialog and set its values with

    QQmlProperty::write(dialog, "someProperty", someValue);
    QMetaObject::invokeMethod(dialog, "open");
    

    I'd like to be able to change the methods that are used when buttons are clicked, like this:

    QQmlProperty::write(dialog, "firstMethod", this->numberOne());
    QQmlProperty::write(dialog, "secondMethod", this->numberTwo());
    

    This, surely, doesn't work "as is".
    But is there a way to achieve something similar?

    What I want is a single QML Dialog that can be reused for many totally different use cases.

    1 Reply Last reply
    0
    • S Offline
      S Offline
      SeDi
      wrote on 17 Jun 2021, 20:52 last edited by
      #2

      Seems to have been a very naive question indeed. Sorry :-(
      So it's probably just not possible.
      Any hints are highly welcome.

      1 Reply Last reply
      0
      • G Offline
        G Offline
        GrecKo
        Qt Champions 2018
        wrote on 18 Jun 2021, 09:59 last edited by
        #3

        Nice usage of the Cunningham's Law.

        First I'd like to state that what you are doing is bad practice.
        You should not reach into QML from C++.
        Expose object, properties, signals, functions in C++ and use them in QML instead.
        Some relevant links:

        • https://doc.qt.io/qt-5/qtquick-bestpractices.html#interacting-with-qml-from-c
        • http://doc.qt.io/qt-5/qtqml-cppintegration-overview.html#interacting-with-qml-objects-from-c
        • https://youtu.be/vzs5VPTf4QQ?t=23m20s

        That being said you could pass a pointer to your object and the name of the function to be invoked to call it from QML: object[functionName]()

        1 Reply Last reply
        2
        • S Offline
          S Offline
          SeDi
          wrote on 26 Jul 2021, 13:08 last edited by SeDi
          #4

          @GrecKo said in QML-Dialog with flexible method calls possible?:

          First I'd like to state that what you are doing is bad practice

          Thanks for that valuable assessment! Following this, I have re-thought my approach. I've got it working now by opening a QML file from C++ and feeding it with initial properties. I hope this is better.

          For anyone interested, here's how I do it. I have the possibility to use StandardButtons or user defined ones (initialized with a QStringList).

          userinteractionbox.qml

          import QtQuick 2.0
          import QtQuick.Controls 2.15
          
          Dialog {
              id: userInteractionBox
              property string text: ""
              property var userButtons: []
              property int initStandardButtons:    Dialog.NoButton
          
              contentItem: ScrollView {
                  contentWidth: availableWidth
                  contentHeight: 1000 
                  height: availableHeight
                  width: availableWidth
                  TextArea {
                      id: textArea
                      text: userInteractionBox.text
                      wrapMode: "WordWrap"
                  }
              }
              signal standardButtonClicked(int nr)
              signal userButtonClicked(int nr)
              footer: DialogButtonBox {
          
                  id: dbbx
                  // STANDARD BUTTONS
                  standardButtons: userInteractionBox.initStandardButtons
                  function processButton(button) {
                      switch (button) {
                          case standardButton(Dialog.Ok):                 userInteractionBox.standardButtonClicked(Dialog.Ok); break;
                          case standardButton(Dialog.Open):               userInteractionBox.standardButtonClicked(Dialog.Open); break;
                          case standardButton(Dialog.Save):               userInteractionBox.standardButtonClicked(Dialog.Save); break;
                          case standardButton(Dialog.Cancel):             userInteractionBox.standardButtonClicked(Dialog.Cancel); break;
                          case standardButton(Dialog.Close):              userInteractionBox.standardButtonClicked(Dialog.Close); break;
                          case standardButton(Dialog.Discard):            userInteractionBox.standardButtonClicked(Dialog.Discard); break;
                          case standardButton(Dialog.Apply):              userInteractionBox.standardButtonClicked(Dialog.Apply); break;
                          case standardButton(Dialog.Reset):              userInteractionBox.standardButtonClicked(Dialog.Reset); break;
                          case standardButton(Dialog.RestoreDefaults):    userInteractionBox.standardButtonClicked(Dialog.RestoreDefaults); break;
                          case standardButton(Dialog.Help):               userInteractionBox.standardButtonClicked(Dialog.Help); break;
                          case standardButton(Dialog.SaveAll):            userInteractionBox.standardButtonClicked(Dialog.SaveAll); break;
                          case standardButton(Dialog.Yes):                userInteractionBox.standardButtonClicked(Dialog.Yes); break;
                          case standardButton(Dialog.YesToAll):           userInteractionBox.standardButtonClicked(Dialog.YesToAll); break;
                          case standardButton(Dialog.No):                 userInteractionBox.standardButtonClicked(Dialog.No); break;
                          case standardButton(Dialog.NoToAll):            userInteractionBox.standardButtonClicked(Dialog.NoToAll); break;
                          case standardButton(Dialog.Abort):              userInteractionBox.standardButtonClicked(Dialog.Abort); break;
                          case standardButton(Dialog.Retry):              userInteractionBox.standardButtonClicked(Dialog.Retry); break;
                          case standardButton(Dialog.Ignore):             userInteractionBox.standardButtonClicked(Dialog.Ignore); break;
                      }
                  }
                  // USER BUTTONS
                  Repeater {
                      model: userButtons.length
                      Button {
                          visible: text !== ""
                          onClicked: userInteractionBox.userButtonClicked(index+1);
                          text: userButtons[index]
                      }
                  }
                  onClicked: {
                      processButton(button)
                  }
              }
          }
          

          For interaction I have a userinteractionbox.h

          #ifndef USERINTERACTIONBOX_H
          #define USERINTERACTIONBOX_H
          
          #include <QObject>
          #include <QQuickItem>
          #include <QQmlApplicationEngine>
          #include <QGuiApplication>
          #include <QQmlContext>
          #include <QQuickWindow>
          #include <QQuickView>
          #include <QtQuick/qquickitem.h>
          #include <QDebug>
          
          
          class UserInteractionBox : public QObject
          {
              Q_OBJECT
          public slots:
              void buttonClickedSlot(int buttonNr);
          public:
              UserInteractionBox(QQuickView* view, QString title, QString text);
              ~UserInteractionBox() override {}
          
              enum StandardButton {
                  NoButton           = 0x00000000,
                  Ok                 = 0x00000400,
                  Save               = 0x00000800,
                  SaveAll            = 0x00001000,
                  Open               = 0x00002000,
                  Yes                = 0x00004000,
                  YesToAll           = 0x00008000,
                  No                 = 0x00010000,
                  NoToAll            = 0x00020000,
                  Abort              = 0x00040000,
                  Retry              = 0x00080000,
                  Ignore             = 0x00100000,
                  Close              = 0x00200000,
                  Cancel             = 0x00400000,
                  Discard            = 0x00800000,
                  Help               = 0x01000000,
                  Apply              = 0x02000000,
                  Reset              = 0x04000000,
                  RestoreDefaults    = 0x08000000
              };
              Q_DECLARE_FLAGS(StandardButtons, StandardButton)
              Q_FLAG(StandardButton)
          
              enum ClosePolicyFlag {
                  NoAutoClose = 0x00,
                  CloseOnPressOutside = 0x01,
                  CloseOnPressOutsideParent = 0x02,
                  CloseOnReleaseOutside = 0x04,
                  CloseOnReleaseOutsideParent = 0x08,
                  CloseOnEscape = 0x10
              };
              Q_DECLARE_FLAGS(ClosePolicyFlags, ClosePolicyFlag)
              Q_FLAG(ClosePolicyFlag)
          
              void open();
              void close();
          
              QFlags<StandardButton> standardButtons() ;
              void setStandardButtons(QFlags<StandardButton> flags);
          
              int clickedButton() const;
          
              bool modal() const;
              void setModal(bool newModal);
          
              int width() const;
              void setWidth(int newWidth);
          
              int height() const;
              void setHeight(int newHeight);
          
              const ClosePolicyFlags &closePolicy() const;
              void setClosePolicy(const ClosePolicyFlags &newClosePolicy);
          
              const QStringList &userButtons() const;
              void setUserButtons(const QStringList &newUserButtons);
          
          signals:
              void standardButtonsChanged();
          
              void buttonClicked(int);
              void accepted();
              void applied();
              void discarded();
              void helpRequested();
              void rejected();
              void reset();
          
          
          private:
              QQmlApplicationEngine * m_engine = nullptr;
              QQuickWindow* m_mainWindow = nullptr;
              QQuickItem* m_item = nullptr;
              QQuickView* m_view = nullptr;
          
              QString m_title = QString();
              QString m_text = QString();
              QString m_addInfo = QString();
              QFlags<StandardButton> m_standardButtons = QFlags<StandardButton>(StandardButton::Ok|StandardButton::Cancel);
              QStringList m_userButtons;
              int m_clickedButton = StandardButton::NoButton;
              QObject* m_qmlObject = nullptr;
              bool m_modal = true;
              int m_width;
              int m_height;
              ClosePolicyFlags m_closePolicy = ClosePolicyFlag::NoAutoClose;
          };
          Q_DECLARE_METATYPE(UserInteractionBox::StandardButton)
          Q_DECLARE_METATYPE(QFlags<UserInteractionBox::StandardButton>)
          Q_DECLARE_OPERATORS_FOR_FLAGS(UserInteractionBox::StandardButtons)
          Q_DECLARE_METATYPE(UserInteractionBox::ClosePolicyFlag)
          Q_DECLARE_METATYPE(QFlags<UserInteractionBox::ClosePolicyFlag>)
          Q_DECLARE_OPERATORS_FOR_FLAGS(UserInteractionBox::ClosePolicyFlags)
          
          
          #endif // USERINTERACTIONBOX_H
          

          Implementation: userinteractionbox.cpp:

          #include "userinteractionbox.h"
          
          void UserInteractionBox::buttonClickedSlot(int buttonNr) {
              m_clickedButton = buttonNr;
          
              emit buttonClicked(buttonNr);
          
              switch (buttonNr) {
                  case  Ok              : emit accepted();      close(); break;
                  case  Open            : emit accepted();      close(); break;
                  case  Save            : emit accepted();      close(); break;
                  case  Cancel          : emit rejected();      close(); break;
                  case  Close           : emit rejected();      close(); break;
                  case  Discard         : emit discarded();     close(); break;
                  case  Apply           : emit applied();                break;
                  case  Reset           : emit reset();                  break;
                  case  RestoreDefaults : emit reset();                  break;
                  case  Help            : emit helpRequested();          break;
                  case  SaveAll         : emit accepted();      close(); break;
                  case  Yes             : emit accepted();      close(); break;
                  case  YesToAll        : emit accepted();      close(); break;
                  case  No              : emit rejected();      close(); break;
                  case  NoToAll         : emit rejected();      close(); break;
                  case  Abort           : emit rejected();      close(); break;
                  case  Retry           : emit accepted();      close(); break;
                  case  Ignore          : emit accepted();      close(); break;
                  case  NoButton        :                                break;
              }
          }
          
          UserInteractionBox::UserInteractionBox(QQuickView *view, QString title, QString text) {
              m_view = view;
              Q_ASSERT(m_view);
              m_engine = qobject_cast<QQmlApplicationEngine *>(m_view->engine());
              Q_ASSERT(m_engine);
          
              m_mainWindow = qobject_cast<QQuickWindow*>(m_engine->rootObjects().value(0));
              Q_ASSERT(m_mainWindow);
          
              m_height = m_mainWindow->height()*3/5;
              m_width = m_mainWindow->width()*3/5;
              m_title = title;
              m_text = text;
          }
          
          void UserInteractionBox::open() {
              close(); // just in case
          
              QQmlComponent boxComp( m_engine, QUrl( "qrc:/UserInteractionBox.qml" ) );
              if (!boxComp.errorString().isEmpty()) {
                  qDebug()<<boxComp.errorString();
              }
              Q_ASSERT(boxComp.errorString().isEmpty());
              Q_ASSERT(!boxComp.isNull()); if (boxComp.isNull()) return;
          
              QVariantMap initialProperties;
              initialProperties["parent"] = QVariant::fromValue(m_mainWindow->contentItem());
              initialProperties["title"] = m_title;
              initialProperties["text"] = m_text;
              initialProperties["modal"] = m_modal;
              initialProperties["width"] = m_width;
              initialProperties["height"] = m_height;
              initialProperties["initStandardButtons"] = QVariant::fromValue(int(m_standardButtons));
              initialProperties["userButtons"] = QVariant::fromValue(m_userButtons);
              initialProperties["closePolicy"] = QVariant::fromValue(int(m_closePolicy));
          
              QQmlContext *ctxt = m_view->rootContext();
              m_qmlObject = boxComp.createWithInitialProperties(initialProperties,ctxt);
              Q_ASSERT(m_qmlObject); if (!m_qmlObject) return;
          
              bool standardConnect =
                      QObject::connect(m_qmlObject,SIGNAL(standardButtonClicked(int)), this, SLOT(buttonClickedSlot(int)),Qt::UniqueConnection);
              Q_ASSERT(standardConnect);
              bool userConnect =
                      QObject::connect(m_qmlObject,SIGNAL(userButtonClicked(int)), this, SLOT(buttonClickedSlot(int)),Qt::UniqueConnection);
              Q_ASSERT(userConnect);
              QMetaObject::invokeMethod(m_qmlObject, "open");
          }
          QFlags<UserInteractionBox::StandardButton> UserInteractionBox::standardButtons() {
              return m_standardButtons;
          }
          
          void UserInteractionBox::setStandardButtons(QFlags<StandardButton> flags) {
              m_standardButtons = flags;
          }
          
          void UserInteractionBox::close()
          {
              if (m_qmlObject) {
                  QMetaObject::invokeMethod(m_qmlObject, "close");
                  m_qmlObject->deleteLater();
                  m_qmlObject = nullptr;
              }
          }
          
          int UserInteractionBox::clickedButton() const
          {
              return m_clickedButton;
          }
          
          bool UserInteractionBox::modal() const
          {
              return m_modal;
          }
          
          void UserInteractionBox::setModal(bool newModal)
          {
              m_modal = newModal;
          }
          
          int UserInteractionBox::width() const
          {
              return m_width;
          }
          
          void UserInteractionBox::setWidth(int newWidth)
          {
              m_width = newWidth;
          }
          
          int UserInteractionBox::height() const
          {
              return m_height;
          }
          
          void UserInteractionBox::setHeight(int newHeight)
          {
              m_height = newHeight;
          }
          
          const UserInteractionBox::ClosePolicyFlags &UserInteractionBox::closePolicy() const
          {
              return m_closePolicy;
          }
          
          void UserInteractionBox::setClosePolicy(const ClosePolicyFlags &newClosePolicy)
          {
              m_closePolicy = newClosePolicy;
          }
          
          const QStringList &UserInteractionBox::userButtons() const
          {
              return m_userButtons;
          }
          
          void UserInteractionBox::setUserButtons(const QStringList &newUserButtons)
          {
              m_userButtons = newUserButtons;
          }
          
          

          Now I can call it like this:

          auto box = new UserInteractionBox(m_view, "Some Title", "Some information\n\nSoSome question");
                  box->setStandardButtons(UserInteractionBox::StandardButton::YesToAll | UserInteractionBox::StandardButton::Cancel);
                  bool connect = QObject::connect(box, SIGNAL(accepted()), this, SLOT(someSlot()), Qt::UniqueConnection);
                  Q_ASSERT(connect);
                  box->open();
          

          I hope this code is not all too bad, I am only a hobby programmer. Perhaps it may be useful for people having a similar problem.

          1 Reply Last reply
          0

          • Login

          • Login or register to search.
          • First post
            Last post
          0
          • Categories
          • Recent
          • Tags
          • Popular
          • Users
          • Groups
          • Search
          • Get Qt Extensions
          • Unsolved