Qt QTreeWidgetItem text contents vs widget item vs data - c++

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.

Related

How to hide the first column of a wxListCtrl in wxWidgets?

The context
In a wxWidgets (version 3.0.2) C++ application, I am trying to hide the first column of a wxListCtrl.
I did not find a member function to do this so I tried to set the width of the column to 0:
myListCtrl->SetColumnWidth(0, 0);
first argument being the column ID and second one the width in pixels (wxListCtrl documentation).
After running the program, the header of the first column is hidden as I wanted but the data of each row of the first column overlaps the data of each row of the second column (which is not hidden). It is obviously not what I want. The header and the data of the first column should be hidden.
The question
In wxWidgets 3.0.2, is there a way to hide the first column (header and data of each rows) of a wxListCtrl?
I don't believe you can. You have a few options.
Delete the column using DeleteColumn(int columnIndex). You aren't losing any data, just the display of it, so you can always re-insert the column and repopulate it if you need to re-add it. Obviously this could be time consuming if your data is excessively large.
Depending on your application, just don't create the column in the first place. You don't say why you want to hide it, so if you just don't want it, don't add it.
Implement your control as a virtual control which gives your application control over what to display where. The burden of data display management falls to you to do manually but you have a great deal more flexibility. Inherit the class with wxLC_VIRTUAL style and implement OnGetItemText http://docs.wxwidgets.org/3.0/classwx_list_ctrl.html#a92370967f97215e6068326645ee76624
Edit:
To expand on the comment question, how to get the selected item index:
The wxListCtrl is a little weird when it comes to selected items. I'm sure it has to do with needing to support report, icon, etc. different views. When dealing with a multi-column report mode, you might find that you can only select items in the first column. If you are on Windows, it should automatically be set to "Full Row Select" but I don't know about other OSs.
Anyway, here is a utility method that returns the first selected item (note that you can support multi-selection if you want to).
//Get the item currently selected
int ListView::GetItemSelected() const
{
for(int i=0; i<GetItemCount(); ++i)
if (GetItemState(i, wxLIST_STATE_SELECTED) == wxLIST_STATE_SELECTED)
return i;
return -1;
}
If you want (and it makes sense), you can connect the list item selected event.
this->Connect(wxEVT_COMMAND_LIST_ITEM_SELECTED, wxCommandEventHandler(ListView::selected_Changed), NULL, this);
and within that event handler, get the selected item and do what needs doing (depending entirely on your application).
You will note that I'm using a derived class here which just makes things a lot easier but you don't have to. You can connect to something like MyMainForm::sqlResults_selectedChanged or whatever.
There is more than one way to accomplish all this and you can also find some good suggestions and help here: https://wiki.wxwidgets.org/WxListCtrl

Which component of MV should transform raw data into user friendly format?

There is a structure that holds raw data (consider int) and a gui that should visualize this data. Gui part consists of a few models and and a few standard views (table and tree). The value should be presented either as a string or as a picture depend on user choice. There are some additional difficulties: in some cases the text is different for different elements - i.e., for the same column 0 normally means "Ok", but for a particular element it should be printed as "Yes", also depend on a user choice some columns should not print text for 0 values at all, living a cell empty and don't clutter the table; next, sometimes cell should show two values - e.g., current value and requested - something like "Yes -> No". This also applies to the case when user wants to see icons.
I see two options:
The model forms string text itself and store it somewhere (in the model item may be). The model also forms composite icon from base icons if it is needed. In that scenario I can use standard delegate and just use standard roles reimplementing QAbstractItemModel::data() method.
Reimplement delegates' paint() method (actually I need to reimplement delegates anyway because I need custom editors like QComboBox). The delegate can get any information it needs through the model and custom roles in paint() method and form the string or complex icon itself. As a suboption, I suppose, it's possible for a model to compose needed information itself, like returning std::tuple<int /*value*/, int /*requested value*/, int /*source element type*/, bool /*text or icon*/> in data() method for Qt::DisplayRole.
Update: I want add one thought about second option. Presumably delegates were introduced to handle the work of representing data. One of the consequences of that is the flexibility delegates can give. If you want to see a raw number in some column, you can just leave the default delegate, if you want some additional info presented in a cell you can set a special delegate for that specific column. And it seems you don't need even touch the model or something else.

QListWidget performance with many custom items

I have a list with about 2500 custom items. I set them with:
const std::vector<const Items::AbstractItem *> results = _engine.request(text);
if (!results.empty())
{
for (auto i : results){
QListWidgetItem *lwi = new QListWidgetItem;
_results->addItem(lwi);
ListItemWidget *w = new ListItemWidget;
w->setName(i->name());
w->setTooltip(i->path());
_results->setItemWidget(lwi, w);
}
_results->setFixedHeight(std::min(5,_results->count()) * 48); // TODO
_results->show();
}
This takes about 5 seconds on an i5-4590. Hiding the widget is twice as fast. Is this normal or do I have look for errors?
A few ideas:
Try assigning proper parents to your QWidgets, thats way the layout doesn't have to do this
mapping for you. This should help performance.
Call setUpdatesEnable(false) before starting the insert, and to true after it's done
As for hiding the widget while adding large amounts of items, this will help to alleviate extraneous update calls. The second suggestion above should mitigate that.
I think this is fully expected behavior for controls like Lists or Trees that are not based on any data model. And I believe that the data model was invented mainly to fix this issue.
In your situation you have a ListWidget control that stores its data on its own. You need to pass all 2500 items before your app can go on, and you need to do this even if your list shows only 10 items at a time. Even if you just run and close your app, the user won't see all the items but you still need to pass them to your ListWidget. Some GUI frameworks use internal allocation of items and in such case they can optimize things a bit, you could do the same if you allocated your Items in chunks but it's still not a good solution.
Now let's say you introduce some object that could be asked about item properties. The Control will ask about some item and your object will respond with the contents. Your object don't even need to know about all your items, it will just learn when needed.
You Control can ask about few first items and stop when it realize it can fill up its entire height. This way you can avoid work that is not needed for now. The Control can also ask about the item count, so it can set-up its vertical slider.
It needs to be said that the model will not solve your problem automatically, it's just a programming paradigm that allows you to do it better.
So the solution for you would be to replace your QListWidget with a QListView and implement you own data model inheriting QAbstractListModel. You could pass the results to the model and it will pass the items data when needed.
If your QListWidgetItem's always has fixed size, call setUniformItemSizes on your QListWidget, pass true.

Change QComboBox item text in delegate

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.

QTableWidget - combobox delegate how do I allow different options per cell

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.