Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt
  • Unsolved
Collapse
qtws26_logo

The Qt Event of the Year: Qt World Summit 2026

Save Your Spot >
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. Arrow appearance for QComboBox in QTableWidget
Qt 6.12 LTS is out! See what's new in the release blog

Arrow appearance for QComboBox in QTableWidget

Scheduled Pinned Locked Moved Solved General and Desktop
6 Posts 2 Posters 1.0k 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.
  • T Offline
    T Offline
    tgbe
    wrote on last edited by
    #1

    I would like to have a QTableWidget with a column that uses QComboBox for input. Using a QStyledItemDelegate with QComboBox works as in the example below.

    My problem is that the combo-box arrow only appears for cells that are being edited, when I would like them to appear always.

    Appearance with another cell selected:
    other cell selected

    Appearance with a combo-box cell selected:
    combo-box cell selected

    Is there a way to keep the combo-box look for deselected cells?

    main.cpp:

    #include <QApplication>
    #include <QTableWidget>
    
    #include "comboboxitemdelegate.h"
    
    
    int main(int argc, char *argv[])
    {
        QApplication a(argc, argv);
    
        QTableWidget tw;
        ComboBoxItemDelegate* cbid = new ComboBoxItemDelegate(&tw);
        tw.setItemDelegateForColumn(1, cbid);
    
        tw.setColumnCount(2);
        tw.setRowCount(2);
        tw.show();
    
        return a.exec();
    }
    
    

    comboboxitemdelegate.h:

    #ifndef COMBOBOXITEMDELEGATE_H
    #define COMBOBOXITEMDELEGATE_H
    
    #include <QStyledItemDelegate>
    
    class ComboBoxItemDelegate : public QStyledItemDelegate
    {
        Q_OBJECT
    public:
        explicit ComboBoxItemDelegate(QObject *parent = nullptr);
    
        QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
        void setEditorData(QWidget *editor, const QModelIndex &index) const override;
        void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override;
    };
    
    #endif // COMBOBOXITEMDELEGATE_H
    

    comboboxitemdelegate.cpp:

    #include "comboboxitemdelegate.h"
    
    #include <QComboBox>
    
    ComboBoxItemDelegate::ComboBoxItemDelegate(QObject *parent)
        : QStyledItemDelegate{parent}
    { }
    
    QWidget *ComboBoxItemDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
    {
        QComboBox *cb = new QComboBox(parent);
        cb->addItem("foo");
        cb->addItem("bar");
        return cb;
    }
    
    void ComboBoxItemDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
    {
        QComboBox *cb = qobject_cast<QComboBox *>(editor);
        Q_ASSERT(cb);
        const QString currentText = index.data(Qt::EditRole).toString();
        const int cbIndex = cb->findText(currentText);
        if (cbIndex >= 0)
            cb->setCurrentIndex(cbIndex);
    }
    
    void ComboBoxItemDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
    {
        QComboBox *cb = qobject_cast<QComboBox *>(editor);
        Q_ASSERT(cb);
        model->setData(index, cb->currentText(), Qt::EditRole);
    }
    
    Chris KawaC 1 Reply Last reply
    1
    • T tgbe

      I would like to have a QTableWidget with a column that uses QComboBox for input. Using a QStyledItemDelegate with QComboBox works as in the example below.

      My problem is that the combo-box arrow only appears for cells that are being edited, when I would like them to appear always.

      Appearance with another cell selected:
      other cell selected

      Appearance with a combo-box cell selected:
      combo-box cell selected

      Is there a way to keep the combo-box look for deselected cells?

      main.cpp:

      #include <QApplication>
      #include <QTableWidget>
      
      #include "comboboxitemdelegate.h"
      
      
      int main(int argc, char *argv[])
      {
          QApplication a(argc, argv);
      
          QTableWidget tw;
          ComboBoxItemDelegate* cbid = new ComboBoxItemDelegate(&tw);
          tw.setItemDelegateForColumn(1, cbid);
      
          tw.setColumnCount(2);
          tw.setRowCount(2);
          tw.show();
      
          return a.exec();
      }
      
      

      comboboxitemdelegate.h:

      #ifndef COMBOBOXITEMDELEGATE_H
      #define COMBOBOXITEMDELEGATE_H
      
      #include <QStyledItemDelegate>
      
      class ComboBoxItemDelegate : public QStyledItemDelegate
      {
          Q_OBJECT
      public:
          explicit ComboBoxItemDelegate(QObject *parent = nullptr);
      
          QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
          void setEditorData(QWidget *editor, const QModelIndex &index) const override;
          void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override;
      };
      
      #endif // COMBOBOXITEMDELEGATE_H
      

      comboboxitemdelegate.cpp:

      #include "comboboxitemdelegate.h"
      
      #include <QComboBox>
      
      ComboBoxItemDelegate::ComboBoxItemDelegate(QObject *parent)
          : QStyledItemDelegate{parent}
      { }
      
      QWidget *ComboBoxItemDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
      {
          QComboBox *cb = new QComboBox(parent);
          cb->addItem("foo");
          cb->addItem("bar");
          return cb;
      }
      
      void ComboBoxItemDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
      {
          QComboBox *cb = qobject_cast<QComboBox *>(editor);
          Q_ASSERT(cb);
          const QString currentText = index.data(Qt::EditRole).toString();
          const int cbIndex = cb->findText(currentText);
          if (cbIndex >= 0)
              cb->setCurrentIndex(cbIndex);
      }
      
      void ComboBoxItemDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
      {
          QComboBox *cb = qobject_cast<QComboBox *>(editor);
          Q_ASSERT(cb);
          model->setData(index, cb->currentText(), Qt::EditRole);
      }
      
      Chris KawaC Offline
      Chris KawaC Offline
      Chris Kawa
      Lifetime Qt Champion
      wrote on last edited by
      #2

      @tgbe The delegate only creates a widget when an item is being edited. If you want the item to look like a combo when it's not being edited you can override the delegate's paint method and use QStyle methods to draw a combo there.

      T 1 Reply Last reply
      3
      • Chris KawaC Chris Kawa

        @tgbe The delegate only creates a widget when an item is being edited. If you want the item to look like a combo when it's not being edited you can override the delegate's paint method and use QStyle methods to draw a combo there.

        T Offline
        T Offline
        tgbe
        wrote on last edited by
        #3

        @Chris-Kawa Thank you, I did not realize this was the intended behavior.

        I dug some more and found this Stack Overflow thread, which seems to implement your suggestion:

        https://stackoverflow.com/questions/47844275/display-qcombobox-down-arrow-in-custom-tree-view

        Another method seems to be using openPersistentEditor().

        Chris KawaC 1 Reply Last reply
        0
        • T tgbe has marked this topic as solved on
        • T tgbe

          @Chris-Kawa Thank you, I did not realize this was the intended behavior.

          I dug some more and found this Stack Overflow thread, which seems to implement your suggestion:

          https://stackoverflow.com/questions/47844275/display-qcombobox-down-arrow-in-custom-tree-view

          Another method seems to be using openPersistentEditor().

          Chris KawaC Offline
          Chris KawaC Offline
          Chris Kawa
          Lifetime Qt Champion
          wrote on last edited by
          #4

          @tgbe This is a performance optimization. You don't want to have many widgets visible all at once, especially if your data set is large. The intended design in this case is to use custom painting on the items that are not currently edited. Painting a picture is a lot cheaper than creating a widget and managing its memory, geometry and events.

          T 1 Reply Last reply
          1
          • Chris KawaC Chris Kawa

            @tgbe This is a performance optimization. You don't want to have many widgets visible all at once, especially if your data set is large. The intended design in this case is to use custom painting on the items that are not currently edited. Painting a picture is a lot cheaper than creating a widget and managing its memory, geometry and events.

            T Offline
            T Offline
            tgbe
            wrote on last edited by
            #5

            @Chris-Kawa Ok, overriding paint() seems to be the efficient way. I added this to the example above, and it works nicely:

            void ComboBoxItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
            {
                QStyleOptionComboBox comboBoxOption;
                comboBoxOption.rect = option.rect;
                comboBoxOption.currentText = index.data(Qt::DisplayRole).toString();
            
                QApplication::style()->drawComplexControl(QStyle::CC_ComboBox, &comboBoxOption, painter);
                QApplication::style()->drawControl(QStyle::CE_ComboBoxLabel, &comboBoxOption, painter);
            }
            

            I only have one small issue with this solution: It takes several clicks for the combo-box popup to appear, even with QAbstractItemView::AllEditTriggers set for the QTableWidget. I assume the first click creates the combo box, which then goes into line-edit mode. Then it reacts to further clicks like a static combo box. If I click outside the table first, it even takes three clicks for the popup to appear.

            Ideally, I would like to line-edit when the text area is clicked and pop-up when the arrow button is clicked. Is there a way to achieve this?

            Chris KawaC 1 Reply Last reply
            0
            • T tgbe

              @Chris-Kawa Ok, overriding paint() seems to be the efficient way. I added this to the example above, and it works nicely:

              void ComboBoxItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
              {
                  QStyleOptionComboBox comboBoxOption;
                  comboBoxOption.rect = option.rect;
                  comboBoxOption.currentText = index.data(Qt::DisplayRole).toString();
              
                  QApplication::style()->drawComplexControl(QStyle::CC_ComboBox, &comboBoxOption, painter);
                  QApplication::style()->drawControl(QStyle::CE_ComboBoxLabel, &comboBoxOption, painter);
              }
              

              I only have one small issue with this solution: It takes several clicks for the combo-box popup to appear, even with QAbstractItemView::AllEditTriggers set for the QTableWidget. I assume the first click creates the combo box, which then goes into line-edit mode. Then it reacts to further clicks like a static combo box. If I click outside the table first, it even takes three clicks for the popup to appear.

              Ideally, I would like to line-edit when the text area is clicked and pop-up when the arrow button is clicked. Is there a way to achieve this?

              Chris KawaC Offline
              Chris KawaC Offline
              Chris Kawa
              Lifetime Qt Champion
              wrote on last edited by Chris Kawa
              #6

              @tgbe Unfortunately there's a bit of time between creating a widget, showing it and it starting to process events, so the click that caused it to be created can't be directly forwarded to it.
              The way I usually deal with it is synthetize a dummy press and release events and send them to the widget using postEvent() once it's shown. Since there are multiple possible trigger types you can override editorEvent() to check what type of event caused the edit (click, keyboard etc.) and get the mouse position for the dummy click.
              I do this often enough that I wish this was a built-in option in the Qt delegates, but sadly it's not, so here we are.

              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
              • Unsolved