I want to change the color and the text of items displayed by a QComboBox, depending on some conditions, but without changing the data in the model itself.
I figured out how to change the color, which was rather easy:
//---------------------------------------------------------------------------------
void
ComboPriorityDelegate::paint(QPainter* p_painter, const QStyleOptionViewItem& p_option, const QModelIndex& p_index) const
{
QStyleOptionViewItem newOption(p_option);
// Set the color
newOption.palette.setColor(QPalette::Text, QColor(255, 0, 0));
QItemDelegate::paint(p_painter, newOption, p_index);
}
Just to explain, in my actual code, I have some conditions there, so I do not want to modify each item.
However, I cannot figure out how I would change the displayed text. I tried setting the text property of the newOption, but it seems like that has nothing to do with the actual text being displayed.
In addition, I need to change the text back to its original form as soon as the item is selected.
I found out that p_index.data().toString() gives me the displayed text, but that doesn't help me modifying it. What I need is an easy way to modify the text attribute of the QLabel (or whatever the QComboBox uses to display item text), without affecting the model itself.
I know that there would be workarounds, like remove the item from the combo box that I want to change and inserting a changed version, but I hope that there is an easier way.
Adding a proxy model on top of your model and changing its data() method seems the easiest solution to me.
There is QIdentityProxyModel which you can simply subclass and override data(). Use your original model as the proxy model's source, and the proxy model as the combobox's model.
Actual Answer:
It seems that what I want to do is simply not possible just using a delegate.
Workaround:
So what I did instead was using an event filter to set the correct suffix for the item texts as well as their color before the drop-down opens (listen to MousePressed event).
And to make sure that the text suffix is removed when an item is selected, I added a slot to the event filter class that must be connected to the activated() signal of the QComboBox. That slot then checks for the suffix and removes it.
This is not a nice thing, but at least it requires only one additional class and two lines of code to make use of. And it should be independent of the data model used.
Related
Quick question
Is there an easy/quick way to map a QListWidget or QStandardItemModel (for QListView) item to my application logic?
Complete Question
Note: I will use undistinctly both QListWidget or QListView and itsQStandardItemModel. From my current point of view for this question, switching from one to another is trivial.
I face usually the need to have a QListView or equivalent in an HMI, which shows some text value and need to react on selection/click.
In the SLOT, in order to perform the required action, the row need to be identified. Qt::DisplayRole is NOT appropriate because some texts could be duplicated and QStrings are not the best way to identify data in Computer Science.
// Click on a row
connect( &myView, &QListView::clicked,
[&myView, this]( const QModelIndex &idx)
{
// E.G. need to update the database for this row. Which row?
});
Possible solutions:
Maintain a map to retrieve the ID from the QModelIndex row.
Save in the model row any ID, making easy to apply any operation.
First option is tedious: it requires to connect the model for keeping the map and the model synchronized.. Same logic again and again.
Second option seem by far the best: I save the (e.g. database id) and use it afterward; But, QListView model (up to what I know) does NOT include this very friendly and useful ID. So until now I had extended again and again the models for QListView.
How to map the QModelIndex back to my application logic? Do I really have to extend the model for that simple operation?
RELATED QUESTION: QTreeView: maintaining mapping between QModelIndex and underlying data
What about creating the QAbstractModel manually, this way you have complete control how the QModelIndexes are created, and you can use that to do stuff.
I'v implemented thousands of Qt Models, and I have never liked the QStandardItemModel approach as I would usually need to write more boilerplate than if I had come with the model myself.
Inherit from your QAbstractList/Table/TreeModel
implement index()
create a method for direct item access
.
// Click on a row
connect( &myView, &QListView::clicked,
[&myView, this]( const QModelIndex &idx)
{
auto& myItem = idx.model()->directAccess(idx.row());
});
profit.
upon construction of QTreeWidgetItem you can pass a list of strings, so when you insert it in a table(QTreeWidget), you get the strings listed on a row. However, from the methods of the table you can also call setItemWidget and set a text widget or any sort of widget to be in that row, but it seems incompatible with having a string list, since the widget is drawn over the strings. There is also a setData method for the QTreeWidgetItem, which sets some data that can be retreived, but isn't visible to the user. Is there a cookie-cutter way of properly using all three data storage methods? Are they even compatible or must I stick to only one?
The Constructor of QTreeWidgetItem is convenient to immediately list the desired content.
When inserting a custom widget in a cell, you need to change its autoFillBackgroundproperty to true, so that it is not transparent. See the QTreewidget::setItemWidget description:
The given widget's autoFillBackground property must be set to true,
otherwise the widget's background will be transparent, showing both
the model data and the tree widget item.
QTreeWidgetItem::setData can be used when already having an item and you want to change one of its contents.
Of course you can combine any of these methods, but it is hard to say, which approach is best without knowing your use case. Just one more hint: If you just need a plain stupid representation of data that does not change, using QTreeWidget is fine. But if your displayed data can change, e.g. objects get deleted, added, changed in various locations of your code, a QTreeView with a custom data model might be a better choice.
I am using a QTableWidget and I have a requirement that the user is able to highlight specific text in a cell, but the cell contents should not change if the user accidentally erases or modifies some cell contents. I was thinking that the simplest way to accomplish this would be to just ignore any edits that occur when the user finishes editing a cell. Any ideas how to do this?
Using C++ 98 and QT
You can access the table widget items and modify their properties You want to disable the Qt::ItemIsEditable flag :
QTableWidgetItem* item;
item->setFlags(item->flags() & ~(Qt::ItemIsEditable));
A good way is to set the item prototype before inserting cells into the table. Right after creating the table
const QtableItem* protoitem = table->itemPrototype();
QtableItem* newprotoitem = protoitem->clone();
newprotoitem->>setFlags(item->flags() & ~(Qt::ItemIsEditable));
table->setItemPrototype(newprotoitem);
Now every new cell in the table will have the editable flag disabled. If the user double click it will not open the text edit in the cell.
ps: Do not delete newprotoitem afterwards.
This is late, but for follow-on searches:
The best way to do this is to subclass a delegate (QStyledItemDelegate is the least problematic - no abstract virtuals).
In the delegate, override 'setModelData()' to a stub. The editor will still come up, and you can still change its contents, but the edit won't 'take'. As soon as you leave the cell, it will revert to its original contents.
If you want to prevent the editor from accepting keys (QLineEdit), override 'createEditor()' in your delegate. Call the base class to create an editor, check its type, and then install an event filter on the editor to reject keypress/keyrelease events.
Return the editor in your override.
Works for me, although I did have to const_cast 'this' (to non-const) to install the event filter.
I have a QTableView linked to a QSqlTableModel. In this table, there is a column that is a integer. Each value of this one matches with a QString. I already used a QComboBox delegate in order to select the QString when editing. But when the value is edited, it is displayed as an integer.
What I want to do is to display the value as a QString with the corresponding integer value.
I looked in QAbstractProxyModel class but I am not totally sure that it can solve my problem.
Can you confirm this way (subclassing QAbstractProxyModel) or using another one?
Thanks for your answers.
You should reimplement your delegate paint method to display text.
You can check classic star delegate example. In your case everything is much simplier: you should draw just a text.
Aloha
I have a QTableWidget with two columns that are currently using a ComboboxDelegate (my subclass of QItemDelegate) to present options to the user. I'd like the choice in the first column to effect the options available in the second, for the current row only.
E.g have a list of cars in the first column, and in the second a list of colours which are available for that car. Other rows to have different cars selected and thus different colour choices available.
From what I can see, I can only set an item delegate per row or column, so I can't see how to change the options in the second column's delegate without affecting all the other rows.
Is this possible? I'd really like to avoid going to a full view/model separation as I have quite a bit of code looking at this QTableWidget already (and I'm under time pressure)
Well for those interested; I went back to my pre-delegate approach, which was to use QTableWidget::setItemWidget() to provide a combobox widget for each cell.
I subclassed qcombobox to take a reference to the table, and connected the combobox CurrentIndexChanged with a slot to update the table data.
(setting a widget in a cell does not affect the tablewidget data unless you do this).
Using a full combobox like this is more expensive than an itemdelegate, but my tables are very small so I can get away with it. The rendering of the combobox is not as nice as the delegate (the combobox is visible all the time instead of only during editing in the delegate's case), but with time I'm sure I can improve on this.