In my app I'd like to insert an item at the top of a QTreeView.
What I have so far will insert an item just above the currently selected item. The code (nicked, I think, from the EditableTreeviewDemo):
QModelIndex index = this->selectionModel()->currentIndex();
QAbstractItemModel *model = this->model();
if (!model->insertRow(index.row(), index.parent()))
return;
I guess what I need is the index to the current first row? How do I get this?
As a side question, what happens to the current index when a row is inserted? Does it continue to point to the same item, or the same row?
Well first you have to know that insertRow is a function from QAbstractItemModel and it will call insertRows (with an s). This function must be redefined in your model subclass if you want to allow insertion of data in your model.
http://doc.qt.io/qt-5/qabstractitemmodel.html#insertRows
Also consider that any parent of a topmost index is a invalid QModelIndex. Then the call to do would be :
model->insertRow(0, QModelIndex());
And because this is the default value for the second parameter, simply call :
model->insertRow(0);
Then in your redefinition of insertRows simply check the validity of you parent index to ensure you news underlying data is created where you want it to be.
For you question, inserting data in the model won't affect the current and selected items.
Related
I want to know how to change the order of QTableView rows by dragging, and store the order changes to the model?
I use QTableView as view and QSqlTableModel as model. I am using Qt 5.15.
I set:
ui->table_view->setSelectionMode(QAbstractItemView::SingleSelection);
ui->table_view->setSelectionBehavior(QAbstractItemView::SelectRows);
ui->table_view->setEditTriggers(QAbstractItemView::NoEditTriggers);
Methods I tried:
1.
ui->table_view->setSectionsMovable(true);
ui->table_view->setDragEnabled(true);
ui->table_view->setDragDropMode(QAbstractItemView::InternalMove);
ui->table_view->setAcceptDrops(true);
It doesn't take effect. The row cannot be dragged.
2.
ui->table_view->verticalHeader()->setSectionsMovable(true);
ui->table_view->verticalHeader()->setDragEnabled(true);
ui->table_view->verticalHeader()->setDragDropMode(QAbstractItemView::InternalMove);
ui->table_view->verticalHeader()->setAcceptDrops(true);
The row can be dragged by vertical header. But the order of changes will not affect the model.
As far as I know the QTableView does not call moveRows method of the model by itself. Instead drag and drop actions call *mimeData methods.
So, one option would be the following:
In your model you need to implement mimeData and dropMimeData functions. When you drag and drop a row the view will ask for mimeData() of the given row. If you are sure that you need only internal moves you can encode the selected row indices to QMimeData. Then in the dropMimeData() you decode the indices that were selected and use them to call your moveRows() implementation. Return false from this function to prevent removing of the moved out rows.
Another option can be to override the QTableView methods such as dropEvent() in a way, that it calls the model moveRows() method directly.
I have custom QTableView class that shows content of custom model based on QAbstractItemModel. In the model I've implemented all needed methods to support changing rows order by DragAndDrop ( using dropMimeData()).
But I do not know how to update selection in the view after model (and view) changed.
For example:
user clicks on the row, it becomes 'selected';
user drags this row to other place;
rows are swaps in the model and view;
BUT selection stays on the first selected row.
How model can notify view to change selection?
NOTE: I cant to create additional signals and slots because don't use MOC.
The solution is:
In function dropMimeData() need to use beginMoveRows() and andMoveRows() around place where data changed.
Need to process signal QAbstractItemModel::rowsMoved of the model, where we can retrieve index of the target row.
There is a method
void QStandardItemModel::appendRow(QStandardItem * item)
in class QStandardItemModel. I cannot figure out where the item is appended, or who the item's parent is after appending?
And another method
bool QStandardItemModel::insertRow(int row, const QModelIndex & parent = QModelIndex())
shows the parent but which item will be appended to the parent?
The first question is easy. As the name says: Append. It is appended to a list or a tree, which has only one column.
The second one is difficult. This function is not directly about inserting items, but about the algorithm how to insert items. In a complicated tree this is not a necessarily trivial task. Adding nodes, changing the dimension of a tree. You have to implement this virtual method, when you have to create an editable tree. In that case making room for a new item and actually inserting the item are two different tasks.
I'm using PyQt to create a GUI application. In a view inherited from QTableView, need to detect the row the user has selected when they double click a row. The table has sorting, but no editing.
How do I do it?
Note - tried the doubleClicked(int) signal. It is emitted by mouse buttons, not by data cells, so it was never fired. :(
Ian
I dont understand.
The doubleClicked signal of the QTableView has the signature
void doubleClicked ( const QModelIndex & index )
If you connect that signal you should obtain the correct QModelIndex.
No need to use SIGNALs anymore:
self.your_table.doubleClicked.connect(your_function)
"doubleClicked" being inherited from QAbstractItemView.
Once you have the modelIndex, (from Frank's comment above) you can use it to find which cell was double clicked.
def slotDoubleClicked(self, mi):
row = mi.row()
column = mi.column()
You then can use these row and col values to access the table with table.setItem(row, column, newdata) or other table method
Like #regomodo said, you can simply connect your function to the double click via:
self.your_table.doubleClicked.connect(your_function)
Then, if you want to know on which row the user double clicked, you can use the following code:
for idx in self.your_table.selectionModel().selectedIndexes():
row_number = idx.row()
column_number = idx.column()
It will return an integer corresponding to the row or the column number.
There will always only be a single value as the double click remove the previous selection.
If you link your function to a push button or another signal, you can receive a list containing multiple elements selected by the user.
For example, you can easily retrieve a list of all selected rows using this code:
rows = []
for idx in self.your_table.selectionModel().selectedIndexes():
rows.append(idx.row())
rows = list(set(rows))
This will return a list of all selected rows (The set function will also remove any duplicates).
Cheers!
I have a QTreeView which has the ExtendedSelection attribute (users may select more than one non-contiguous range of cells).
I would like my app to allow them to select these multiple cells and then enter a value and have all the cells take that value.
My issue is that I don't know how to get the full range of cells passed to my model. Right now the index that is passed to the setData method is only the active cell, not the full range of selected cells.
In the past, I have had my view store the currently selected range in the model every time it changes, and then use that to control which cells to modify. This seems kind of hacky and I wonder whether anyone has a better and more elegant solution.
I am using PyQt by the way, though I suspect this applies to QT by itself.
Thanks!
I believe you can use selectionModel method of the QAbstractItemView class to iterate through selected indexes and change values of the corresponding cells. Below is a small example:
foreach (QModelIndex index, ui->treeView->selectionModel()->selectedIndexes())
{
qDebug() << "Changing index " << index.row();
ui->treeView->model()->setData(index, "new data");
}
hope this helps, regards