I'm still struggling with using QListView, I'm trying to select one particular row in the view and I cannot figure out how to do this.
I found a similar question on StackOverflow which recommends using the createIndex() method of the model, however this method is protected (perhaps it used to be public but is not anymore) so that doesn't work for me. Any suggestion?
You can get the index of anything by just calling
QModelIndex indexOfTheCellIWant = model->index(row, column, parentIndex);
Then you can call setCurrentIndex(indexOfTheCellIWant) as bruno said in his answer.
If model contains just a standard list of items as opposed to a tree structure, then it's even easier. Because we can assume that the item is a root item - no parent.
QModelIndex indexOfTheCellIWant = model->index(row, column);
With a tree structure it is a little trickier, because we can't just specify a row and a column, we need to specify these with respect to a parent. If you need to know about this part let me know and I'll explain more.
Only one more thing to note. Selection is based on cells, not really rows. So if you want to ensure that when the user selects a cell (or you do through code) that the whole row is selected you can do that by setting the "selectionBehavior" on the itself.
list->setSelectionBehavior(QAbstractItemView::SelectRows);
You can use QAbstractItemView::setCurrentIndex ( const QModelIndex & index )
Fetch an instance of QModelIndex from the QListView's model and select it:
void selectRowInQListView(int row, QListView *listView) {
QModelIndex index = listView->model()->index(row, 0);
if (index.isValid()) {
//listView->selectionModel()->select(index, QItemSelectionModel::Select);
//listView->selectionModel()->select(index, QItemSelectionModel::Current);
listView->setCurrentIndex(index);
}
}
Related
I am creating a completer myself, using ComboBox and QTreeView (for the proposal list).
MyComboBox::MyComboBox( QWidget *p_parent ) : QComboBox( p_parent )
{
setEditable(true);
m_view = new QTreeView();
m_view->expandAll(); // this command does not work!!!
m_view->setItemDelegate( new CompleterDelegate(m_view));
CompleterSourceModel *m_sourceModel = new CompleterSourceModel(this);
CompleterProxyModel *m_proxyModel = new CompleterProxyModel(this);
m_proxyModel->setSourceModel(m_sourceModel);
setView(m_view);
setModel(m_proxyModel);
connect(this, &QComboBox::currentTextChanged, this, &MyComboBox::showProposalList);
}
The structure of my data for the tree model here is parent-child. With the constructor above, after I put my data into the model, the children are hidden, only the parents can be seen.
In order to see all the items (children) I have to use m_view->expandAll() after I put the data into the model. Is there any way that we can do it in constructor, so each time i put data into the model (whatever my data is), all the items (parents and children) are automatically expanded ?
Your best bet is probably to connect to the QAbstractItemModel::rowsInserted signal to make sure items are expanded on a just-in-time basis. So, immediately after setting the view's model use something like...
connect(m_view->model(), &QAbstractItemModel::rowsInserted,
[this](const QModelIndex &parent, int first, int last)
{
/*
* New rows have been added to parent. Make sure parent
* is fully expanded.
*/
m_view->expandRecursively(parent);
});
Edit: It was noted in the comments (#Patrick Parker) that simply calling m_view->expand(parent) will not work if the row inserted has, itself, one or more levels of descendants. Have changed the code to use m_view->expandRecursively(parent) (as suggested by #m7913d) to take care of that.
I want to use a QTableView to show different roles of an item, e.g. column 1 shows the DisplayRole data, column 2 shows UserRole, column 3 shows UserRole+1, etc
I've created this by making my own item delegate and assigning it to the appropriate column. However, to get access to the same item the delegates have to access their siblings. For example, here's my setEditorData function:
void UserRoleDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
{
QModelIndex baseIndex = index.sibling(index.row(),0);
QVariant v = baseIndex.data(Qt::UserRole);
if(v.isValid())
static_cast<QLineEdit*>(editor)->setText(v.toString());
}
Right now, it's hardcoded that column 0 contains the real object and other columns just access that via the sibling function. I'm worried, though, that it's fragile in that changing the column order will break it.
There are obvious ways to manage that, but I'm just wondering if I'm taking the right approach. Is there a better option to display different aspects of the same item?
To avoid possible code rewrite you simple could use the enumeration for your column numbers. For example:
enum ColumnNumber {
Base,
Sub1,
Sub2
}
And in your delegate's code:
[..]
QModelIndex baseIndex = index.sibling(index.row(), Base);
Thus, if you need to change the column order, you simply need to change the order of your enum values. The rest of code will work correctly as it is.
WRT to item delgate usage - it looks rather strange. Such things used to made in the model class, especially in QAbstractItemModel::data(...) function, using it with Qt::EditRole role. For example:
QVariant Model::data(const QModelIndex &index, int role) const
{
if (role == Qt::EditRole) {
// Get the data that stored in the first column
// ..
return data;
}
[..]
}
I know you can do it when you have access to the QStandardItemModel but using combobox->model() returns a QAbstractItemModel which doesn't have the item(int row, int col) accessor. I've tried working with QAbstractItemModel::itemData(QModelIndex) but can't get it to work as I require.
I just need to get the CheckState of the items, if(item.checkState() == Qt::Checked) etc...
Edit: I have this code, can I cast it to a QStandardItem?
QModelIndex index(1, 0);
QVariant item = ui->SearchAssessmentCombo->model()->data(index, Qt::CheckStateRole);
You can't declare an index yourself, all indices are tied to a model. Internally, the data() function will determine that the index you gave in the parameter does not belong to the model and will return null values for everything.
You need to ask your model to give you a valid index before you can use it.
QModelIndex index = ui->SearchAssessmentCombo->model()->index(1,0);
I have implemented my QTreeView & QAbstractItemModel according to editabletreemodel example plus the advice here: QTreeView & QAbstractItemModel & insertRow.
It works, ok.
But there is a difference I do not understand.
To make my application work properly when inserting child rows I had to emit layoutChanged(). I had to. If I don't emit - inserting a child works not good: a new child correctly appears in model, it is correctly saved (when I save), but it is NOT shown in view.
The editabletreemodel example does not contain "emit layoutChanged()", and it works fine with inserting children.
Here is my insertRows method of QAbstractItemModel sibling class:
bool ExTree::insertRows(int position, int rows, const QModelIndex &parent)
{
ExObject *parentItem = getItem(parent);
bool success;
beginInsertRows(parent, position, position + rows - 1);
success = parentItem->insertChildren(position, rows, rootItem->columnCount());
endInsertRows();
if(success) emit layoutChanged();
return success;
}
It is almost like those in editabletreeview example, the only difference is the emit.
Why, please?
I want to understand, WHY exactly I needed to emit layoutChanged(), while editabletreeview does not do this. Will appreciate greatly if anyone can explain this point.
I do not need understanding why emit. I need understanging why I need to emit and editabletreeview does not need it.
In QAbstractItemModel, some functions, such as beginInsertRows, beginRemoveRows, can be used to insert and remove rows. But how to implement replacing a row item by another one?
If I understand you right, you need to notify subcribed views about data changing (one row replaced by new, for views it means that data has been changed) for the specified model index:
// let's the row is index that we want to invalidate
QVector<int> roles;
roles << Qt::DisplayRole;
emit dataChanged(index(row, 0), index(row, columnCount()-1), roles);
If you want to change a lot of data, you should do something like this:
beginResetModel();
// change data
endResetModel();
If you change just one row, emitting dataChanged() should do the trick.