QTableView::scrollTo() (finding the right QModelIndex) - qt

I'm trying to get PgDown clicks on a QTableView to scroll down a variable number of rows. I talk to my subclassed QSortFilterProxyModel which talks to the subclassed QAbstractTableModel to figure out what the next row is. That's all fine and dandy but I believe I'm faced with two caveats:
1: The row number inside the view doesn't do much. I need a QPoint on the screen to scroll to, and I'm not sure how to derive that from a cell.
2: I can create an index in the QSortFilterProxyModel but this generally causes crashes, as the parent is different... or I'm missing something.
int nextRow = getModel()->nextRow( indexAt( rect().topLeft() ) );
QModelIndex nextIndex = getModel()->index( nextRow, 0 );
scrollTo( nextIndex, QAbstractItemView::PositionAtTop );

Okay, I figured this out:
QModelIndex nextIndex = getModel()->index( nextRow, 0 );
scrollTo( nextIndex, QAbstractItemView::PositionAtTop );
I was having the QSortFilterProxyModel create and index which was a big no-no. I have issues when I have hidden rows, but should hopefully be able to figure that out.

Related

Can a QComboBox display a different value than whats in it's list?

Using Qt 5.9 on Linux, I have a QComboBox with several labels.
qc = new QComboBox;
qc->addItem(tr("Red"));
qc->addItem(tr("Green"));
qc->addItem(tr("Blue"));
Lets say a user activates the QComboBox and the 3 color labels are shown in the drop down list. The user then selects the 1st item (red).
What I want to do is have the QComboBox display a different value than what was selected. I.e., if red is selected, then a number is shown, possibly 1 for the first item (or it could be an R for Red), and if green is selected, then display 2 (or G) for the second item.
My goal in doing this is to use less display space (less wide) than is actually necessary to show the complete text of the selection because some of my item strings are quite long and a much shorter label is desired when the QComboBox is not activated in it's drop down state. Besides, the item strings are descriptive and abbreviations would work better for displaying.
Edit:
Using Marek's example, thought this might help. Here's what I have. I'm expecting if the user selects from the list, then an R, G, or a B should be displayed after.
QStandardItem *red = new QStandardItem();
red->setData(tr("Red"), Qt::DisplayRole);
red->setData("R", Qt::UserRole);
QStandardItem *green = new QStandardItem();
green->setData(tr("Green"), Qt::DisplayRole);
green->setData("G", Qt::UserRole);
QStandardItem *blue = new QStandardItem();
blue->setData(tr("Blue"), Qt::DisplayRole);
blue->setData("B", Qt::UserRole);
QStandardItemModel *rgb_model = new QStandardItemModel(this);
rgb_model->setItem(0, red);
rgb_model->setItem(1, green);
rgb_model->setItem(2, blue);
QComboBox *rgb_cb = new QComboBox();
rgb_cb->setModel(rgb_model);
I get the feeling it's because I don't quite understand how to use Qt::UserRole.
Yes it is possible. QComboBox uses data model to manage items.
You have to provide own data model, with items with respective data values.
QStandardItem *itme1 = new QStandardItem();
item1->setData(tr("Red"), Qt::DisplayRole);
item1->setData("1", Qt::UserRole); // note doesn't have to be a string.
QStandardItem *itme2 = new QStandardItem();
item2->setData(tr("Green"), Qt::DisplayRole);
item2->setData("2", Qt::UserRole);
QStandardItemModel *model = new QStandardItemModel(this);
mode->setItem(1, item1);
mode->setItem(2, item2);
qc->setModel(model);
It should work, but I didn't test it. At least this should be some clue.
Please review QComboBox documentation, especially about roles.
Another solution is use translations with multiple lengths. You can provide couple translation for a single string. Each translation should be graphically shorter than earlier one.
In such situation QString contains all possibilities separated by spatial character. When such string is rendered first substring (between separators) which will fit available space will be used.
Now I do not remember what is the separator value. I've used this very long time ago (with Qt 4.8) and now can't find reference for it.
In your example for make it short just make:
qc->setWidth( 20 );
But if you really want user choose something, then:
connect( qc, SIGNAL( onCurrentIndexChanged( int ) ), SLOT( changeComboText() ) );
[...]
void changeComboText()
{
QString shortText;
//Determine short value for shortText
qc->setCurrentText( shortText );
}

QTreeview iterate through list

I need to iterate through the QTreeview list with buttons the way it natively does via the keyboard arrow keys. I can get it to jump to the last item with this code, but it won't iterate through the list, it just jumps to the last item.
for( int i = 0; i < ui->TList_Tree->topLevelItemCount(); ++i )
{
ui->TList_Tree->setCurrentIndex(ui->TList_Tree->currentIndex().sibling(i,0));
}
I'm sure I'm missing something simple here.
My comments above missed the rather obvious QTreeView::indexAbove and QTreeView::indexBelow. So your button that that moves the cursor down should connect to code that does something along the lines of...
QModelIndex index = ui->TList_Tree->indexBelow(ui->TList_Tree->currentIndex());
if (index.isValid())
ui->TList_Tree->setCurrentIndex(index);
Did a quick check and this appears to do what you want.

QTreeView with QAbstractItemModel: tree items collapse and expend when adding/updating new children

I have a QTreeView with my own model. When adding new items into the tree, some items expand or collapse. How can I preserve the expand state when modifying the tree?
Thank you, Martin.
It's quite late for the question's author but I had a similar problem and ended up here, so maybe it is worth posting a solution I came up with.
My understanding is that updating nodes is not a problem - indices are not invalidated and expansion is conserved. When you add a new node, however, the default seems to be to make the node collapsed. The fallowing small hack changes the default to expand all newly added indices:
// This is done at the point where the model is set to be used by the view
connect(&model, &QAbstractItemModel::rowsInserted,
[&](const QModelIndex &parent, int first, int last) {
for (; first <= last; ++first) {
tree_view->expand(
model.index(first, 0, parent));
}
});
In case you want to replace a node with a new version (remove it and add a new one in its place) you can use a similar approach: remember expansion by connecting to QAbstractItemModel::rowsAboutToBeRemoved and using QTreeView::isExpanded(). The state can be resored in a function/slot connected to QAbstractItemModel::rowsInserted.
I would like to share some code, but it is too long. I will explain where my problem was instead.
This is my tree structure
It is necessary to use following functions when inserting/deleting rows.
void QAbstractItemModel::beginInsertRows(const QModelIndex & parent, int first, int last);
void QAbstractItemModel::endInsertRows()
void QAbstractItemModel::beginRemoveRows(const QModelIndex & parent, int first, int last)
void QAbstractItemModel::endRemoveRows()
I found out that when inserting/deleting items A and C, it is required to use invalid model index as a parent index. An invalid model index is QModelIndex() without any parameters. At least it is what help in my case.
A simple tree model example is available here:
http://doc.qt.io/qt-5/qtwidgets-itemviews-simpletreemodel-example.html

QStandardItemModel inside Qtableview

I am using QStandardItemModel inside QTableView. Here i have two button & Qtableview inside my mainwindow.
I need only 4 columns inside this. And rows will vary. The two Buttons will be used to add/delete a row (test case).
setHorizontalHeaderItem is not showing all the text(means all text is not visible). Example if i put 'Text for the Employee Name' it is not fully visible ?
How to make QStandardItemModel occupy full QTableview (width). At present it is showing at top left corner ?
How to achieve it?
Code :
model= new QStandardItemModel(4, 4);
for (int row = 0; row < 4; ++row) {
for (int column = 0; column < 4; ++column) {
QStandardItem *item = new QStandardItem(QString("row %0, column %1").arg(row).arg(column));
model->setItem(row, column, item);
}
}
model->setHorizontalHeaderItem(0, new QStandardItem(tr("Time")));
model->setHorizontalHeaderItem(1, new QStandardItem(tr("Text for the Employee Name")));
model->setHorizontalHeaderItem(2, new QStandardItem(tr("Text for the Employee Address")));
model->setHorizontalHeaderItem(3, new QStandardItem(tr("Text for the Employee Date of Birth")));
model->setVerticalHeaderItem(0, new QStandardItem(tr("Test-Case-----1")));
tableView->horizontalHeader()->setStretchLastSection(true);
or
tableView->horizontalHeader()->setResizeMode(QHeaderView::Stretch);
As the question is already accepted for the answer, my answer may help someone, as the above accepted answer didn't help me.
QStandardItemModel *model = new QStandardItemModel(2,3,this);
//----------
ui->tableView->setModel(model);
ui->tableView->resizeColumnsToContents();
I know the answer comes a bit late, but I was just needing to do the same, and figured out a different solution.
To achieve your goal in Qt 4, you need to adjust the settings of the header of your QTableView. To do so, first retrieve the header:
QHeaderView *header = ui->tableView->horizontalHeader();
Next, adjust the resize mode of the individual columns by calling QHeaderView::setResizeMode (the second flavor, which accepts logicalIndex):
header->setResizeMode(0, QHeaderView::ResizeToContents);
header->setResizeMode(1, QHeaderView::ResizeToContents);
header->setResizeMode(2, QHeaderView::ResizeToContents);
header->setResizeMode(3, QHeaderView::Stretch);
In the above example, I chose to stretch column 3, but you may choose any of the columns to be in "stretch" mode.
In Qt 5, the call you want is QHeaderView::setSectionResizeMode().
Hope this helps you or anybody else seeking a solution to this problem.

How to maintain vertical scroll position in a QTableWidget

This a very simple problem to which I can find no solution:
This is my code:
qint32 pos = ui->twShow->verticalScrollBar()->value();
ui->twShow->blockSignals(true);
//Code for updating the contents QTableWidget twShow, this is done by erasing all cells and adding them again, in case it matters.
ui->twShow->blockSignals(false);
if (pos > 0){
ui->twShow->verticalScrollBar()->setValue(pos);
}
What I want to accomplish is simply to maintain the vertical scroll position. However the setValue function ignores the value pos (I've checked by printing the value before and after the instruction and both times its cero).
I have also tried:
QScrollBar *bar = ui->twShow->verticalScrollBar();
// Same code as before
ui->twShow->setVerticalScrollBar(bar); //This line crashes de program
However the last line crashes the program (which I've checked by commenting it, and it works fine).
Any advice would be greatly appreciated...
Thank you very much
QTableWidget * tw;
int desiredRow;
// before update
desiredRow = tw->row(tw->itemAt(1,1));
...
// update code
...
tw->scrollToItem( tw->item( desiredRow, 0),
QAbstractItemView::EnsureVisible | QAbstractItemView::PositionAtTop );
QAbstractItemView::EnsureVisible = 0.
The 'or' flag converts the result to an integer which is not allowed as parameter of the scrollToItem method. On the other hand enums are not intended to be used as combined flags.

Resources