CheckBox in QComboBox

Posted June 5th @ 2:05 am by da-crystal

Qt is one of the best GUI Widget cross-platform libraries that I ever worked with ;) .

clip_image002

 

At some point, I was in need for a Combo box with a list of Checkbox item.  And that had not been provided as out-of- shelf widget.  For some reason, one of them as in the following quotes:

 “a combo box is a way to represent a single selection, not to hide other GUI elements”.

First let understand the QComboBox. The Combo box usually has two main parts:  current item view and items view (popup) .  Current view is view displaying the selected item.  While items view display a list of items to be select. 

Now let work with the items view to handle item as checkbox rather than a label ;) .  In order to customize our items view we need to assignee an item delegate to the items view.   And that led us to create a customized (inherited) QItemDelegate let called CheckBoxListDelegate:

 

class CheckBoxListDelegate : public QItemDelegate

{

public:

    CheckBoxListDelegate(QObject *parent)

             : QItemDelegate(parent)

      {

            ;

      }

 

      void paint(QPainter *painter, const QStyleOptionViewItem &option,

                          const QModelIndex &index) const

      {

            //Get item data

            bool value = index.data(Qt::UserRole).toBool();

            QString text = index.data(Qt::DisplayRole).toString();

           

            // fill style options with item data

            const QStyle *style = QApplication::style();

            QStyleOptionButton opt;

            opt.state |= value ? QStyle::State_On : QStyle::State_Off;

            opt.state |= QStyle::State_Enabled;

            opt.text = text;

            opt.rect = option.rect;

 

            // draw item data as CheckBox

            style->drawControl(QStyle::CE_CheckBox,&opt,painter);

      }

 

    QWidget *createEditor(QWidget *parent,

             const QStyleOptionViewItem & option ,

             const QModelIndex & index ) const

      {

            // create check box as our editor

             QCheckBox *editor = new QCheckBox(parent);

             return editor;

      }

 

       void setEditorData(QWidget *editor,

                                                             const QModelIndex &index) const

       {

             //set editor data

             QCheckBox *myEditor = static_cast<QCheckBox*>(editor);

             myEditor->setText(index.data(Qt::DisplayRole).toString());

             myEditor->setChecked(index.data(Qt::UserRole).toBool());

       }

 

       void setModelData(QWidget *editor, QAbstractItemModel *model,

                                                            const QModelIndex &index) const

       {

             //get the value from the editor (CheckBox)

             QCheckBox *myEditor = static_cast<QCheckBox*>(editor);

             bool value = myEditor->isChecked();

 

             //set model data

             QMap<int,QVariant> data;

             data.insert(Qt::DisplayRole,myEditor->text());

             data.insert(Qt::UserRole,value);

             model->setItemData(index,data);

       }

 

       void updateEditorGeometry(QWidget *editor,

             const QStyleOptionViewItem &option, const QModelIndex &index ) const

       {

             editor->setGeometry(option.rect);

       }

 };

 

Then we need to tell our combo box to use that delegate as default delegate for the items view that it own.   Also we need to change the way it display the selected item.  Header of our CheckBoxList will look like:

class CheckBoxList: public QComboBox

{

      Q_OBJECT;

 

public:

      CheckBoxList(QWidget *widget = 0);

      virtual ~CheckBoxList();

      bool eventFilter(QObject *object, QEvent *event);

      virtual void paintEvent(QPaintEvent *);

      void SetDisplayText(QString text);

      QString GetDisplayText() const;

 

private:

      QString m_DisplayText;

};    

 

And the implementation:

CheckBoxList::CheckBoxList(QWidget *widget )

:QComboBox(widget),m_DisplayText(0)

{

      // set delegate items view

      view()->setItemDelegate(new CheckBoxListDelegate(this));

     

      // Enable editing on items view

      view()->setEditTriggers(QAbstractItemView::CurrentChanged);

     

      // set "CheckBoxList::eventFilter" as event filter for items view

      view()->viewport()->installEventFilter(this);

     

      // it just cool to have it as defualt ;)

      view()->setAlternatingRowColors(true);

}

 

 

CheckBoxList::~CheckBoxList()

{

      ;

}

 

 

bool CheckBoxList::eventFilter(QObject *object, QEvent *event)

{

      // don’t close items view after we release the mouse button

      // by simple eating MouseButtonRelease in viewport of items view

      if(event->type() == QEvent::MouseButtonRelease && object==view()->viewport())

      {

            return true;

      }

      return QComboBox::eventFilter(object,event);

}

 

 

void CheckBoxList::paintEvent(QPaintEvent *)

{

    QStylePainter painter(this);

    painter.setPen(palette().color(QPalette::Text));

 

    // draw the combobox frame, focusrect and selected etc.

    QStyleOptionComboBox opt;

    initStyleOption(&opt);

 

      // if no display text been set , use "…" as default

      if(m_DisplayText.isNull())

            opt.currentText = "…";

      else

            opt.currentText = m_DisplayText;

    painter.drawComplexControl(QStyle::CC_ComboBox, opt);

 

    // draw the icon and text

    painter.drawControl(QStyle::CE_ComboBoxLabel, opt);

}

 

 

void CheckBoxList::SetDisplayText(QString text)

{

      m_DisplayText = text;

}

 

QString CheckBoxList::GetDisplayText() const

{

      return m_DisplayText;

}

 

And that’s it ;) see it in action:

      CheckBoxList list;

      list.addItem("Qt",true);

      list.addItem("CMake",true);

      list.addItem("10.99$",false);

      list.addItem("Da-Crystal",true);

 

clip_image004

clip_image002

 

6 Comments

  1. abdulaziz
    June 6, 2008 at 23:39

    thanks,

    this was helpful

  2. koosh
    June 21, 2008 at 03:47

    hmmm… ‘selecting’ an item is always interpreted as ‘checking’ the item. i was expecting a click on the box to change the checkbox, while a click on the text would select that item in the combo box. am i missing something?

    also the width of the combo box and list isn’t being adjusted for the width of the checkbox, so the last couple characters are clipped for long lines.

    using qt 4.3.2.

  3. da-crystal
    June 21, 2008 at 11:29

    That’s not what this CheckboxList is meant to do. This CheckBoxList is like a ListView of Checkbox items but in a ComboBox dress ;) just to save some space.

    About the second part (thanks for highlighting that) just remove the "updateEditorGeometry" method from "CheckBoxListDelegate" class.

    Can you provide an example/scenario that clarifies what you mentioned above?

    Regards

  4. koosh
    June 21, 2008 at 22:39

    Ok, now I understand the operation.

    I have a combobox to select a certain choice from a list of items. Only one item is currently chosen (hence the combobox), but each item also has a boolean property that can be set independent of the currently chosen item. I was hoping to use a combo box with a checkbox next to each item (like you’ve done), to save space.

    Any ideas on what to modify in your code to make it work like this?

  5. da-crystal
    June 22, 2008 at 13:34

    Maybe the following will help you:

    just remove all the methods in CheckBoxList, except the contractor. Also, modify the constructor to be as follow :

    CheckBoxList::CheckBoxList(QWidget *widget )

    :QComboBox(widget),m_DisplayText(0)

    {

    view()->setItemDelegate(new MyDelegate(this));

    view()->setEditTriggers(QAbstractItemView::CurrentChanged);

    }

    Let me know if that work :p.

  6. koosh
    June 23, 2008 at 06:01

    I decided on another approach – using a popup context menu for items in the QComboBox, when the user right clicks during a selection. This will allow me to handle other things besides just a single bool (checkbox).

    Thanks for the info, anyway, your code helped me to get my new approach working.

Sorry, comments for this entry are closed at this time.