Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. Using a Qt 5 Widget within a QML application?
QtWS25 Last Chance

Using a Qt 5 Widget within a QML application?

Scheduled Pinned Locked Moved Unsolved General and Desktop
qt5.5qtwidgets
5 Posts 4 Posters 9.2k 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.
  • ? Offline
    ? Offline
    A Former User
    wrote on 20 Jan 2016, 19:09 last edited by
    #1

    I am writing a Qt 5 application with QML and would like to know if it is possible to integrate a Qt 5 Widget into the same UI of that application.

    1. Is it possible?
    2. If yes, how is it achieved?
    3. Are there any restrictions, caveats or problems associated with doing this?

    Thanks,

    Felix

    R 1 Reply Last reply 21 Jan 2016, 10:17
    0
    • M Offline
      M Offline
      mrjj
      Lifetime Qt Champion
      wrote on 20 Jan 2016, 19:16 last edited by
      #2

      Hi and welcome.
      I have not huge experience with QML but i know you can expand it
      http://doc.qt.io/qt-5/qtqml-tutorials-extending-qml-example.html
      so i assume its also possible with normal widgets in some way.

      Since you can show QML inside widget application, it is possible to mix.
      http://doc.qt.io/qt-4.8/qml-integration.html

      So yes, I think with some research its possible.

      R 1 Reply Last reply 20 Jan 2016, 20:34
      0
      • M mrjj
        20 Jan 2016, 19:16

        Hi and welcome.
        I have not huge experience with QML but i know you can expand it
        http://doc.qt.io/qt-5/qtqml-tutorials-extending-qml-example.html
        so i assume its also possible with normal widgets in some way.

        Since you can show QML inside widget application, it is possible to mix.
        http://doc.qt.io/qt-4.8/qml-integration.html

        So yes, I think with some research its possible.

        R Offline
        R Offline
        raven-worx
        Moderators
        wrote on 20 Jan 2016, 20:34 last edited by raven-worx
        #3

        sorry thats not officially supported by Qt.
        The problem is that QML uses SceneGraph for painting, where QWidgets use a raster engine for painting. So they unfortunately dont play very well together.
        But not impossible, but but far not efficient. You would need to render the widget as a texture on every paint event. Additionally you would need to forward the events to the widget.

        --- SUPPORT REQUESTS VIA CHAT WILL BE IGNORED ---
        If you have a question please use the forum so others can benefit from the solution in the future

        1 Reply Last reply
        1
        • ? A Former User
          20 Jan 2016, 19:09

          I am writing a Qt 5 application with QML and would like to know if it is possible to integrate a Qt 5 Widget into the same UI of that application.

          1. Is it possible?
          2. If yes, how is it achieved?
          3. Are there any restrictions, caveats or problems associated with doing this?

          Thanks,

          Felix

          R Offline
          R Offline
          raven-worx
          Moderators
          wrote on 21 Jan 2016, 10:17 last edited by
          #4

          @Lucan1d
          you made me curious, and so i played around a bit.
          This example renders the widget and forwards mouse- and key-events to the widget (i used a QTextEdit in this case).
          But it also points out the problems very well:

          • you can't use widgets with popup windows (e.g. combobox)
          • cursor blinking and text-selection isn't painted (may be fixed somehow - didn't investigate further)
          • correct focus handling might be pretty tricky to implement
          • correct state handling is essential for visual appearance

          But i think it might be a good starting point. It still needs a lot of work to behave as someone would expect it to behave.

          qmlRegisterType<MyQmlWidget>("com.test.qml", 1, 0, "MyQmlWidget");
          QQuickView *qmlView = new QQuickView;
          qmlView->setSource(QUrl::fromLocalFile("test.qml"));
          qmlView->show();
          

          test.qml

          import QtQuick 2.0
          import com.test.qml 1.0
          
          MyQmlWidget {
              focus: true
          }
          

          h

          class MyQmlWidget : public QQuickPaintedItem
          {
              Q_OBJECT
          
          public:
              MyQmlWidget( QWidget* widget = new QTextEdit );
          
              virtual bool eventFilter(QObject *, QEvent *);
              virtual void paint(QPainter * painter);
          
              virtual bool event(QEvent * e);
          
          protected:
              void setWidget( QWidget* widget );
          
              QWidget*    m_Widget;
          };
          

          cpp

          MyQmlWidget::MyQmlWidget( QWidget* widget )
              : QQuickPaintedItem()
          {
              this->setWidget( widget );
              this->setAcceptedMouseButtons(Qt::AllButtons);
          }
          
          bool MyQmlWidget::eventFilter(QObject * watched, QEvent * event)
          {
              if( watched == m_Widget )
              {
                  switch( event->type() )
                  {
                      case QEvent::Paint:
                      case QEvent::UpdateRequest:
                          this->update();
                      break;
                  }
              }
              return QQuickPaintedItem::eventFilter(watched, event);
          }
          
          void MyQmlWidget::paint(QPainter *painter)
          {
              if( !m_Widget )
                  return;
          
              m_Widget->render( painter );
          }
          
          bool MyQmlWidget::event(QEvent *event)
          {
              switch( event->type() )
              {
                  case QEvent::MouseButtonPress:
                  case QEvent::MouseButtonRelease:
                  case QEvent::MouseButtonDblClick:
                  case QEvent::KeyPress:
                  case QEvent::KeyRelease:
                  {
                      qDebug() << "SEND EVENT:" << event;
                      QApplication::sendEvent( m_Widget, event );
                      return true;
                  }
                  break;
                  case QEvent::FocusIn:
                      this->forceActiveFocus();
                  break;
              }
              return QQuickPaintedItem::event(event);
          }
          
          void MyQmlWidget::setWidget(QWidget *widget)
          {
              m_Widget = widget;
              m_Widget->installEventFilter( this );
          
              const QSizeF sh = m_Widget->sizeHint();
              this->setImplicitSize( sh.width(), sh.height() );
          }
          

          --- SUPPORT REQUESTS VIA CHAT WILL BE IGNORED ---
          If you have a question please use the forum so others can benefit from the solution in the future

          alex_spataruA 1 Reply Last reply 10 Feb 2021, 18:19
          0
          • R raven-worx
            21 Jan 2016, 10:17

            @Lucan1d
            you made me curious, and so i played around a bit.
            This example renders the widget and forwards mouse- and key-events to the widget (i used a QTextEdit in this case).
            But it also points out the problems very well:

            • you can't use widgets with popup windows (e.g. combobox)
            • cursor blinking and text-selection isn't painted (may be fixed somehow - didn't investigate further)
            • correct focus handling might be pretty tricky to implement
            • correct state handling is essential for visual appearance

            But i think it might be a good starting point. It still needs a lot of work to behave as someone would expect it to behave.

            qmlRegisterType<MyQmlWidget>("com.test.qml", 1, 0, "MyQmlWidget");
            QQuickView *qmlView = new QQuickView;
            qmlView->setSource(QUrl::fromLocalFile("test.qml"));
            qmlView->show();
            

            test.qml

            import QtQuick 2.0
            import com.test.qml 1.0
            
            MyQmlWidget {
                focus: true
            }
            

            h

            class MyQmlWidget : public QQuickPaintedItem
            {
                Q_OBJECT
            
            public:
                MyQmlWidget( QWidget* widget = new QTextEdit );
            
                virtual bool eventFilter(QObject *, QEvent *);
                virtual void paint(QPainter * painter);
            
                virtual bool event(QEvent * e);
            
            protected:
                void setWidget( QWidget* widget );
            
                QWidget*    m_Widget;
            };
            

            cpp

            MyQmlWidget::MyQmlWidget( QWidget* widget )
                : QQuickPaintedItem()
            {
                this->setWidget( widget );
                this->setAcceptedMouseButtons(Qt::AllButtons);
            }
            
            bool MyQmlWidget::eventFilter(QObject * watched, QEvent * event)
            {
                if( watched == m_Widget )
                {
                    switch( event->type() )
                    {
                        case QEvent::Paint:
                        case QEvent::UpdateRequest:
                            this->update();
                        break;
                    }
                }
                return QQuickPaintedItem::eventFilter(watched, event);
            }
            
            void MyQmlWidget::paint(QPainter *painter)
            {
                if( !m_Widget )
                    return;
            
                m_Widget->render( painter );
            }
            
            bool MyQmlWidget::event(QEvent *event)
            {
                switch( event->type() )
                {
                    case QEvent::MouseButtonPress:
                    case QEvent::MouseButtonRelease:
                    case QEvent::MouseButtonDblClick:
                    case QEvent::KeyPress:
                    case QEvent::KeyRelease:
                    {
                        qDebug() << "SEND EVENT:" << event;
                        QApplication::sendEvent( m_Widget, event );
                        return true;
                    }
                    break;
                    case QEvent::FocusIn:
                        this->forceActiveFocus();
                    break;
                }
                return QQuickPaintedItem::event(event);
            }
            
            void MyQmlWidget::setWidget(QWidget *widget)
            {
                m_Widget = widget;
                m_Widget->installEventFilter( this );
            
                const QSizeF sh = m_Widget->sizeHint();
                this->setImplicitSize( sh.width(), sh.height() );
            }
            
            alex_spataruA Offline
            alex_spataruA Offline
            alex_spataru
            wrote on 10 Feb 2021, 18:19 last edited by
            #5

            Here is a method of capturing mouse events and wheel events based on the previous answer. Unfortunately, this method requires us to access protected members of the QWidget. I don't know how legal it is, but it works:

            Constructor:

            QmlPlainTextEdit::QmlPlainTextEdit(QQuickItem *parent)
                : QQuickPaintedItem(parent)
            {
                // Set item flags
                setFlag(ItemHasContents, true);
                setFlag(ItemAcceptsInputMethod, true);
                setFlag(ItemIsFocusScope, true);
                setAcceptedMouseButtons(Qt::AllButtons);
            
                // Initialize the text edit widget
                m_textEdit = new QPlainTextEdit();
                m_textEdit->installEventFilter(this);
                m_textEdit->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
            
                // Set the QML item's implicit size
                auto hint = m_textEdit->sizeHint();
                setImplicitSize(hint.width(), hint.height());
            
                // Resize QPlainTextEdit to fit QML item
                connect(this, &QQuickPaintedItem::widthChanged, this,
                        &QmlPlainTextEdit::updateWidgetSize);
                connect(this, &QQuickPaintedItem::heightChanged, this,
                        &QmlPlainTextEdit::updateWidgetSize);
            }
            

            Events:

            bool QmlPlainTextEdit::event(QEvent *event)
            {
                switch (event->type())
                {
                case QEvent::FocusIn:
                    forceActiveFocus();
                    return QQuickPaintedItem::event(event);
                    break;
                case QEvent::Wheel:
                    processWheelEvents(static_cast<QWheelEvent*>(event));
                    return true;
                    break;
                case QEvent::MouseButtonPress:
                case QEvent::MouseButtonRelease:
                case QEvent::MouseButtonDblClick:
                case QEvent::MouseMove:
                    processMouseEvents(static_cast<QMouseEvent*>(event));
                    return true;
                    break;
                default:
                    break;
                }
            
                return QApplication::sendEvent(m_textEdit, event);
            }
            

            Event filter:

            bool QmlPlainTextEdit::eventFilter(QObject *watched, QEvent *event)
            {
                Q_ASSERT(m_textEdit);
            
                if (watched == m_textEdit)
                {
                    switch (event->type())
                    {
                    case QEvent::Paint:
                    case QEvent::UpdateRequest:
                        update();
                        break;
                    default:
                        break;
                    }
                }
            
                return QQuickPaintedItem::eventFilter(watched, event);
            }
            

            Paint function:

            void QmlPlainTextEdit::paint(QPainter *painter)
            {
                if (m_textEdit && painter)
                    m_textEdit->render(painter);
            }
            

            Process mouse/wheel events:

            Doing some tests, I found out that QPlainTextEdit::event() returned false for mouse and wheel events. The quick and dirty solution was to call the appropriate event handlers directly. However, these functions are protected, so we need to do some unorthodox things in order to call these functions:

            void QmlPlainTextEdit::processMouseEvents(QMouseEvent* event)
            {
                class Hack : public QPlainTextEdit{
                public:
                    using QPlainTextEdit::mousePressEvent;
                    using QPlainTextEdit::mouseMoveEvent;
                    using QPlainTextEdit::mouseReleaseEvent;
                    using QPlainTextEdit::mouseDoubleClickEvent;
                };
            
                auto hack = static_cast<Hack*>(m_textEdit);
                switch(event->type()) {
                case QEvent::MouseButtonPress:
                    hack->mousePressEvent(event);
                    break;
                case QEvent::MouseMove:
                    hack->mouseMoveEvent(event);
                    break;
                case QEvent::MouseButtonRelease:
                    hack->mouseReleaseEvent(event);
                    break;
                case QEvent::MouseButtonDblClick:
                    hack->mouseDoubleClickEvent(event);
                    break;
                default:
                    break;
                }
            }
            
            void QmlPlainTextEdit::processWheelEvents(QWheelEvent* event)
            {
                class Hack : public QPlainTextEdit{
                public:
                    using QPlainTextEdit::wheelEvent;
                };
            
                static_cast<Hack*>(m_textEdit)->wheelEvent(event);
            }
            

            Resize widget to fit QML item:

            void QmlPlainTextEdit::updateWidgetSize()
            {
                m_textEdit->setGeometry(0, 0, static_cast<int>(width()), static_cast<int>(height()));
                update();
            }
            

            To use this class in QML, add in main.cpp:

            qmlRegisterType<QmlPlainTextEdit>("QtWidgets", 1, 0, "QmlPlainTextEdit");
            

            And finally, the QML item would be used like:

            QmlPlainTextEdit {
               id: textEdit
               focus: true
               Layout.fillWidth: true
               Layout.fillHeight: true
            
               onFocusChanged: {
                   textEdit.forceActiveFocus()
                }
            }
            

            If anybody has a better way to process mouse/wheel events for this scenario, please post it here. I was stuck with this issue for three days and this is the only solution that I found.

            1 Reply Last reply
            1

            • Login

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