Custom QComboBox
-
Hi,
how can I have a a QComboBox with two texts? One left aligned and one right aligned.
From what I can see that's not directly possible and needs to be implemented manually.So I have basically subclassed QAbstractTableModel and added two columns and a few rows. I added this model to my QComboBox.
I tried changing the view to QTableView, but obviously I then get all the styling with it. And I cannot really believe that this is the way to go.Do I have to change the delegate? How would I do that?
I also get a little bit confused when I have to change the view or when I have to change the delegate.That all seems to me quite complicated as I basically just want to achieve something very easy.
-
That all seems to me quite complicated as I basically just want to achieve something very easy.
It's "easy" when you look at it, but it's non-standard so you have to do it manually.
I also get a little bit confused when I have to change the view or when I have to change the delegate.
A delegate is responsible for customizing a single item in the view - how it looks, how it behaves, how it edits. It only cares about single item. It does not specify how many items are there or how they are laid out (in a list, tree, table etc.). It only handles single item.
The view is the type of widget you want to use for displaying the menu. If you use a treeview it's gonna be a tree. If you use a table widget it's gonna be a table and so on. A view uses a delegate to handle its items - painting, editing etc.
Do I have to change the delegate? How would I do that?
For this you can either change the view to display a tree or a table, but you can also keep the original list view and change the delegate to paint text from two columns in a single item.
For example you can use a delegate like this:class MyDelegate : public QStyledItemDelegate { public: using QStyledItemDelegate::QStyledItemDelegate; void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override { // draw the regular text from column 0 QStyledItemDelegate::paint(painter, option, index); // draw the right aligned text from column 1 QString text = index.sibling(index.row(), 1).data(Qt::DisplayRole).toString(); painter->drawText(option.rect, Qt::AlignRight | Qt::AlignVCenter, text); } };
and then set it on your combo:
comboBox->setItemDelegate(new MyDelegate());
You can of course customize it further to meet your needs.
-
Hi Chris,
thanks for that. I was actually pretty close!
Obviously, I was talking about tables and not trees! So I made QAbstractTableModel.Changing the view gives me something similar I want, but I guess with changing the delegate I have more control over the look. So I'm going with that.
I have a few questions to the code though, just to understand everything better.
- Am I right that QModelIndex always is the first element in my row (because I use a AbstractTableModel), but have set a QListView? So one delegate is a complete row in that model
- Where do I get the styling from for the text? QStyledItemDelegate::paint applies highlighting and text displayed by that method is then bold I think? At least it's better visible on the blue highlighting background
- How do I manipulate the actual content of the QComboBox? it seems like the one delegate visible all the time (so not part of the popup) is not controlled by this paint method? At least my second text is not displayed there
-
Am I right that QModelIndex always is the first element in my row (because I use a AbstractTableModel), but have set a QListView? So one delegate is a complete row in that model
It's not a rule in general. It depends on the view type. A list will always only use column 0 and so will only call the delegate's paint for indices in column 0. A tree view or a table would call this delegate's paint with indices from other columns too.
So to be clear - the code I posted is only valid for a list view (like the default one) and also has a hardcoded assumption that the model has column 1. In reality you should probably check if the result ofsibling()
call returns a valid index, but atoString()
would return an empty string if it's not, so I just skipped the check.Where do I get the styling from for the text? QStyledItemDelegate::paint applies highlighting and text displayed by that method is then bold I think? At least it's better visible on the blue highlighting background
All information used for painting is in the
option
structure you get as the argument ofpaint()
. It has the font, the state (hovered, selected etc.) and bunch of other info.How do I manipulate the actual content of the QComboBox? it seems like the one delegate visible all the time (so not part of the popup) is not controlled by this paint method? At least my second text is not displayed there
I'm not sure I understand. You said you created your own model. If that's the case you manipulate it as you would with any other model. Either via methods provided by
QAbstractItemModel
interface or your custom ones. If you don't want to implement everything yourself you can use one of the models provided by Qt, for exampleQStandardItemModel
in which you edit stuff by changing itsQStandardItem
's. -
I guess, I don't have to do checks as I have created the model and use it only for that specific model
Sorry I meant if you have a QComboBox then you have your box and you see the currently selected item in it. If you click on that box the popups open with all the other options. While the items in the popup have two columns and two texts, the actual selected item in the box itself is only one colunm.
So if you have that view, you only see the left text. In the popup you see both. How can I change how the content of that comboBox is displayed? -
Yes, the item delegate is only used for the popup. For the combo control itself you'd have to subclass QComboBox and override its
paintEvent()
method and do basically the same thing as in the delegate - draw your other text manually. -
I understand, thanks!
I just tried tried to get the style information from the option structure, but I can figure out where the style option for the font comes from. I tried opt.font and sett this for painter->setFont(), but still text, which is highlightes, is hardly visible. Where is the styling for text, which is selected. opt->backgroundBrush wasn't it either.