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. QComboBox delegate: Best way to show the popup menu immediately?
Qt 6.11 is out! See what's new in the release blog

QComboBox delegate: Best way to show the popup menu immediately?

Scheduled Pinned Locked Moved General and Desktop
8 Posts 7 Posters 8.9k Views 1 Watching
  • 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.
  • JKSHJ Offline
    JKSHJ Offline
    JKSH
    Moderators
    wrote on last edited by Chris Kawa
    #1

    Hi all,

    I've subclassed QStyledItemDelegate to provide a QComboBox editor (tutorial: http://qt-project.org/wiki/Combo_Boxes_in_Item_Views )

    The problem is, 3 clicks are needed to bring up the popup menu. We can reduce this to 2 clicks if the popup menu is shown as soon as the editor is created and shown.

    The cleanest way I can think of doing this is to use a single-shot timer to call showPopup() after creating the QComboBox. The timer is required because we need to wait for the QComboBox to be positioned + shown before calling showPopup(), or else the menu will pop up in the wrong place.

    QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const override
    {
        Q_UNUSED(option);
        Q_UNUSED(index);
    
        auto cb = new QComboBox(parent);
        cb->addItems({"A", "B", "C"});
    
        // ASSUMPTION: The QComboBox will be shown in the correct
        // location before control returns to the event loop, thus
        // ensuring that that the timer will fire at the right time.
        QTimer::singleShot(0, [cb]
        {
            cb->showPopup();
        });
        return cb;
    }
    

    Is there a nicer way to do this? I'm also looking for a way to modify the behaviour of Qt's default delegate, which creates a QComboBox if QAbstractItemModel::data() returns a Boolean value. It would be great if I didn't have to implement my own delegate just for this.

    Thanks in advance!

    Qt Doc Search for browsers: forum.qt.io/topic/35616/web-browser-extension-for-improved-doc-searches

    1 Reply Last reply
    0
    • Chris KawaC Offline
      Chris KawaC Offline
      Chris Kawa
      Lifetime Qt Champion
      wrote on last edited by Chris Kawa
      #2

      I don't know if it's nicer (has some ugliness to it too) but another way is to install the delegate as an event filter for the created widget and listen for FocusIn event.The upside is that it doesn't make any event loop related assumptions:

      bool ComboBoxItemDelegate::eventFilter(QObject *src, QEvent *evt) {
          if(evt->type() == QEvent::FocusIn) {
              auto cb = qobject_cast<QComboBox*>(src);
              if(cb) {
                  cb->showPopup();
                  //important to handle it only the first time, otherwise will result in
                  //focus glitches
                  cb->removeEventFilter(this);
              }
          }
          return QStyledItemDelegate::eventFilter(src, evt);
      }
      

      Now the ugly part is that the createEditor() method is const so to install the filter you need a const_cast. Oh well, that's what they're for I guess :/

          ...
          auto cb = new QComboBox(parent);
          cb->installEventFilter(const_cast<ComboBoxItemDelegate*>(this));
          cb->addItems({"A", "B", "C"});
          ...
      

      As for the second question - inside createEditor() you could call the base implementation if needed and treat the resulting widget similarly. I don't know how to do it without a custom delegate, sorry.

      J 1 Reply Last reply
      3
      • JKSHJ Offline
        JKSHJ Offline
        JKSH
        Moderators
        wrote on last edited by
        #3

        Thanks Chris!

        Your event filter technique looks useful for handling other kinds of interactions with editors too (e.g. dismissing the editor when the mouse leaves the cell). I'll play around with it.

        It looks like I'll have to do some extra work no matter what, but we usually survive that :D

        Qt Doc Search for browsers: forum.qt.io/topic/35616/web-browser-extension-for-improved-doc-searches

        S 1 Reply Last reply
        0
        • Chris KawaC Chris Kawa

          I don't know if it's nicer (has some ugliness to it too) but another way is to install the delegate as an event filter for the created widget and listen for FocusIn event.The upside is that it doesn't make any event loop related assumptions:

          bool ComboBoxItemDelegate::eventFilter(QObject *src, QEvent *evt) {
              if(evt->type() == QEvent::FocusIn) {
                  auto cb = qobject_cast<QComboBox*>(src);
                  if(cb) {
                      cb->showPopup();
                      //important to handle it only the first time, otherwise will result in
                      //focus glitches
                      cb->removeEventFilter(this);
                  }
              }
              return QStyledItemDelegate::eventFilter(src, evt);
          }
          

          Now the ugly part is that the createEditor() method is const so to install the filter you need a const_cast. Oh well, that's what they're for I guess :/

              ...
              auto cb = new QComboBox(parent);
              cb->installEventFilter(const_cast<ComboBoxItemDelegate*>(this));
              cb->addItems({"A", "B", "C"});
              ...
          

          As for the second question - inside createEditor() you could call the base implementation if needed and treat the resulting widget similarly. I don't know how to do it without a custom delegate, sorry.

          J Offline
          J Offline
          jarzec
          wrote on last edited by jarzec
          #4

          @Chris-Kawa First of all, thanks for your solution :D.

          I am not sure if this is due to changes in version of Qt, but I had the same problem and it seems the ugly bit is not needed, because the delegate is installed as event filter in the editor by default somewhere along the pipeline.

          Additionally, I had to modify your solution as I also wanted to have the combo box commit when an item is selected (and not after an extra hit on Enter). To achieve all this I did:

          QWidget *MyItemDelegate::createEditor(QWidget *parent,
                                                 const QStyleOptionViewItem &option,
                                                 const QModelIndex &index) const {
            ...
            QComboBox *combo_box = new QComboBox(parent);
            ...
            // Make sure data is commited when an item is chosen in the combo box
            connect(
          	  combo_box,
          	  static_cast<void (QComboBox::*)(int)>(&QComboBox::activated), [=]() {
                          // This event will be captured by the delegate eventFilter()
          		QApplication::sendEvent(
          			combo_box,
          			new QKeyEvent(QEvent::KeyPress, Qt::Key_Enter, Qt::NoModifier));
          	  });
            ...
          }
          
          bool MyItemDelegate::eventFilter(QObject *object, QEvent *event) {
            // Show combo box popup when the box gains focus
            if (event->type() == QEvent::FocusIn) {
              auto *combo_box = qobject_cast<QComboBox *>(object);
              auto *focus_event = dynamic_cast<QFocusEvent *>(event);
              if (combo_box && focus_event &&
                  // Do not consider focus gained when the popup closes
                  focus_event->reason() != Qt::PopupFocusReason) {
                combo_box->showPopup();
              }
            }
          
            return QStyledItemDelegate::eventFilter(object, event);
          }
          
          1 Reply Last reply
          3
          • JKSHJ JKSH

            Thanks Chris!

            Your event filter technique looks useful for handling other kinds of interactions with editors too (e.g. dismissing the editor when the mouse leaves the cell). I'll play around with it.

            It looks like I'll have to do some extra work no matter what, but we usually survive that :D

            S Offline
            S Offline
            sampats
            wrote on last edited by
            #5

            @JKSH Hi JKSH, I also have the exact same requirement, i.e. to show the combo box editor's drop down in single click. I first tried QTimer::singleShot() and connected to a slot in QComboBox-subclass, which calls QComboBox::showPopup(). Also after looking at this post, tried the eventfilter method suggested by Chris Kawa. In both methods when I click on the table cell, combo box and the drop down appears, but immediately after that, the drop down collapses automatically. Have you seen similar issue ? I'm developing on Linux.

            QWidget * qDrawToolsDelegate_C::createEditor(QWidget * parent, const QStyleOptionViewItem & option, const QModelIndex & index) const
            {
                if (!_model) return NULL;
                
                if (_model->columnType(index.column()) == COL_TYPE_LINE_STYLE) {
                    qLineStyleComboBox_C * lineStyleCombobox = new qLineStyleComboBox_C(parent);
                    QTimer::singleShot(delay, lineStyleCombobox, SLOT(slotShowPopup()));
                    return lineStyleCombobox;
                }
                else if (_model->columnType(index.column()) == COL_TYPE_FILL_PATTERN) {
                    qFillStippleComboBox_C * fillPatternCombobox = new qFillStippleComboBox_C(parent);
                    fillPatternCombobox->installEventFilter(const_cast<qDrawToolsDelegate_C *>(this));
                    return fillPatternCombobox;
                }
                return QStyledItemDelegate::createEditor(parent, option, index);
            }
            

            I have used timer for qLineStyleComboBox_C and event filter for qFillStippleComboBox_C. But result is the same. On click, editor and drop down appears/flashes and collapses.

            1 Reply Last reply
            0
            • P Offline
              P Offline
              Pavel Celba
              wrote on last edited by Pavel Celba
              #6

              Have you seen similar issue ?

              It's caused by following behavior:
              The combo is created (byCreateEditor), combo is set (by setEditorData) and show popup is called. Then the combo is set visible and immediatelly receives mouse release event, on which the combo box calls hide popup. It appears to me as almost Qt bug.

              Solution is likely to block last mouse release event on combo box by installing event filter in the delegate or by overriding editorEvent of item delegate.

              F 1 Reply Last reply
              2
              • P Pavel Celba

                Have you seen similar issue ?

                It's caused by following behavior:
                The combo is created (byCreateEditor), combo is set (by setEditorData) and show popup is called. Then the combo is set visible and immediatelly receives mouse release event, on which the combo box calls hide popup. It appears to me as almost Qt bug.

                Solution is likely to block last mouse release event on combo box by installing event filter in the delegate or by overriding editorEvent of item delegate.

                F Offline
                F Offline
                fanyha
                wrote on last edited by
                #7

                @Pavel-Celba would you please describe how do resolve this issue in detail? because I've met same issue now and seems hard to resolve. the combo dropdown list pop up and soon disappear

                Y 1 Reply Last reply
                0
                • F fanyha

                  @Pavel-Celba would you please describe how do resolve this issue in detail? because I've met same issue now and seems hard to resolve. the combo dropdown list pop up and soon disappear

                  Y Offline
                  Y Offline
                  Yergin
                  wrote on last edited by
                  #8

                  @fanyha I had this problem too. What worked for me was calling QComboBox::showPopup from QStyledItemDelegate::eventFilter using QTimer::singleShot as follows:

                  bool DeviceChannelAssignmentDelegate::eventFilter(QObject* src, QEvent* evt)
                  {
                      if (evt->type() == QEvent::FocusIn)
                      {
                          auto combo = qobject_cast<QComboBox*>(src);
                          if (combo && _firstPopup)
                          {
                              QTimer::singleShot(0, [=]{combo->showPopup();});
                              _firstPopup = false;
                          }
                      }
                      return QStyledItemDelegate::eventFilter(src, evt);
                  }
                  

                  I set _firstPopup to true in QStyledItemDelegate::createEditor and also connect QComboBox::activated to a slot which emits DeviceChannelAssignmentDelegate::commitData and hides the QComboBox like so:

                  QWidget* DeviceChannelAssignmentDelegate::createEditor(
                      QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const
                  {
                      auto combo = new QComboBox(parent);
                      ...
                      connect(combo, &QComboBox::activated, this, &DeviceChannelAssignmentDelegate::emitCommitData);
                      _firstPopup = true;
                      return combo;
                  }
                  
                  void DeviceChannelAssignmentDelegate::emitCommitData()
                  {
                      auto combo = qobject_cast<QComboBox*>(sender());
                      emit commitData(combo);
                      combo->hide();
                  }
                  

                  The end result is that when I double-click on item in the QTreeView, the QComboBox popup menu shows up immediately, and the QComboBox then disappears after selecting from the popup's choices which works well for me.

                  1 Reply Last reply
                  0
                  • gbettegaG gbettega referenced this topic on

                  • Login

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