I have a problem where i did not find a solution so far, actually the question is when and for what QModelIndex we have to call beginInsertColumns and similar functions.
My example is the standard Qt example from here:
http://doc.qt.io/qt-5/qtwidgets-itemviews-editabletreemodel-example.html
It is also delivered with every qt installation, e.g. C:\Qt\Qt5.12.0\Examples\Qt-5.12.0\widgets\itemviews\editabletreemodel
When just executed, it works fine, but now lets insert a QSortFilterProxyModel, just to make it sortable. We change the MainWindow c'tor to
...
QFile file(":/default.txt");
file.open(QIODevice::ReadOnly);
TreeModel *model = new TreeModel(headers, file.readAll());
file.close();
QSortFilterProxyModel* sort = new QSortFilterProxyModel( this );
sort->setSourceModel( model );
view->setModel( sort );
...
Now execute the example, expand one or two of the root nodes and select the menu entry "insert column" you will see, that only the root nodes have a new column, all their children do not have the new column. It works without the proxy. Now my question, do i have to emit beginInsertColumns for every QModelIndex that has children?
How can i fix this?
Note, this is not only a problem with qt 5.12, but also with many earlier releases.
I've always had problems using QSortFilterProxyModel, and as far as I understood they are often related to the index mapping between with the source model.
I managed to solve it using the QSortFilterProxyModel::invalidate() method that will reset the mapping, without spoiling the current state of the view.
Modify the MainWindow::insertColumn as follows:
bool MainWindow::insertColumn()
{
QAbstractItemModel *model = view->model();
int column = view->selectionModel()->currentIndex().column();
bool changed = false;
// Insert a column in the parent item.
QSortFilterProxyModel* sfpm = qobject_cast<QSortFilterProxyModel*>(model);
if (sfpm)
{
changed = sfpm->insertColumn(column + 1);
if (changed)
sfpm->setHeaderData(column + 1, Qt::Horizontal, QVariant("[No header]"), Qt::EditRole);
sfpm->invalidate();
}
updateActions();
return changed;
}
It is not an optimal solution, and I don't know if there is a better way, but works for me.
Related
Initially, I used qsqlrelationtablemodel in my database project. And the values were entered into the database using the combobox inside the tableView. And it looked something like this:
enter image description here
And the code looked very simple:
model->setTable(sql_query);
model->setRelation(1, QSqlRelation("my_schema.aircrafts","id_aircraft","registration_number"));
model->setRelation(3, QSqlRelation("my_schema.airports","id_airport","name_airport"));
model->setRelation(4, QSqlRelation("my_schema.airports","id_airport","name_airport"));
ui->tableView->setModel(model);
ui->tableView->setItemDelegateForColumn(1, new QSqlRelationalDelegate(this));
ui->tableView->setItemDelegateForColumn(3, new QSqlRelationalDelegate(this));
ui->tableView->setItemDelegateForColumn(4, new QSqlRelationalDelegate(this));
But now I have redone the data entry on the forms and there are no problems with transferring information to qlineedit. There are difficulties with transferring data from foreign key's to an external combobox.
Now it looks like this (the value of the combobox does not change when you click on another row of the tableView):
enter image description here
Now I'm using this code, but I don't understand how to pass the value of the selected index in the tableView to the combobox.
QString query = QString("SELECT * FROM my_schema.route WHERE id_route = '%1'").arg(id);
QSqlQuery qw;
qw.exec(query);
while(qw.next())
{
ui->lineEdit->setText(qw.value(1).toString());
ui->lineEdit_2->setText(qw.value(2).toString());
ui->lineEdit_3->setText(qw.value(3).toString());
ui->comboBox_2->setModel(model);
ui->comboBox_2->setModelColumn(4);
}
I would like to see something like this result:
enter image description here
Please tell me in the most accessible form how to achieve this
You have model and tableview for fetching all columns for clicked row by using QAbstractItemView::clicked(const QModelIndex &index) signal for receive event. And implement the slot that loops all columns in selected row like this example snippet:
// create query with join for combobox model only containing desired values not foreign keys
// after that create QStringList and fill it with query result
// set stringlistmodel to combobox
ui->comboBox_2->setModel(stringlistmodel); // this is different from tableview's model
connect(ui->tableView, &QTableView::clicked, [this](const QModelIndex &index)
{
auto row{index.row()};
ui->lineEdit->setText(model.data(model.index(row, 0).toString());
//...
ui->comboBox_2->setCurrentIndex(model.data(model.index(row, 4).toInt());
//...
});
I have already coded this for QTableWidget:
void ReadOnlyWindow::addReportIconToRow(const int rowIndex){
QIcon icon;
QSize sz(16, 16);
icon.addPixmap(style()->standardIcon(QStyle::SP_FileDialogEnd).pixmap(sz), QIcon::Normal);
QTableWidgetItem *iconItem = new QTableWidgetItem();
iconItem->setText("report");
iconItem->setIcon(icon);
iconItem->setFlags(iconItem->flags() & (~Qt::ItemIsEditable));
ui->homeWorksTable->setItem(rowIndex, REPORT_COLUMN_INDEX, icon);
}
REPORT_COLUMN_INDEX is const int from class and it has value 4.
I am trying to found out how to rewrite code if table is `` QTableView`.
I was trying to use setItemData() and setData() but I think I used it in bad way because it didn't work.
P.S.: Now I want to do it for QTableView because its easy to load SQLite table there. This part works. I also added one more column. Now I need add to all rows of this column icon with text (how is in my code for QTableWidget). Function up there should be for one cell and will be implemented in loop.
to add an icon to a tableWidget you set the item like specified here :
and when defining the QTableWidgetItem, use the constructor taking an Icon to be displayed.
here is a short example:
this->ui->myTable->setItem(row, col, new QTableWidgetItem(QIcon(":/resources_to_icon_.png"),"SomeText"));
in your code:
ui->homeWorksTable->setItem(rowIndex, REPORT_COLUMN_INDEX, QTableWidgetItem(icon,"some text");
I have a Qt project where I have a QStandardItemModel called stockModel. This model is linked to a QTableView called tvStock.
I have a button called btnDelete which has a clicked event set up as follows:
void StockItems::on_btnDelete_clicked()
{
//delete
}
How do I delete the selected row of tvStock from stockModel?
I assume I can start with this (but I'm not sure how to complete it):
stockModel->removeRow(/* What goes here? */);
Otherwise let me know if I'm completely on the wrong track.
Update:
I found and modified some code that allows to me to delete the row if the entire row's contents are selected:
QModelIndexList indexes = ui->tvStock->selectionModel()->selectedRows();
while (!indexes.isEmpty()) {
stockModel->removeRows(indexes.last().row(), 1);
indexes.removeLast();
}
Is there a way this code could be modified to delete the entire row if only one of the row items is selected?
Thanks!
I'm currently working on a customization of QAbstractItemModel an I encountered a problem. The model itself works fine so far, but I have problems, if i try to display it with QTreeView.
The Model itself is able to change its column number on its own, depending on it's data. But the view will never update the number of columns shown, only their content.
I did overload insertColumns:
bool MyModel::insertColumns(int column, int count, const QModelIndex &parent)
{
bool success;
beginInsertColumns(parent, column, column + count - 1);
success = this->getItem(parent)->insertColumns(column, count);
endInsertColumns();
return success;
}
I experimented a little bit and found out, that if I reset and set the View each time, it will display the right number of columns:
connect(this->model, SIGNAL(columnsChanged()), this->ui->treeView, SLOT(reset()));
But there has to be another way to do this. I'm looking for a function, that will just tell the View that the column-count has changed. But the only one i found (QTreeView::columnCountChanged(int oldCount, int newCount)) is protected...
here are some other signals that treeview's mode can give out these should all be triggered if your inserting a column so just use the appropriate one and connect to update on your table view, although i would have thought that if your changing the underlying model the view should update, and if it doesnt reset the model
ui->treeView->model()->layoutChanged();
ui->treeView->model()->dataChanged();
ui->treeView->model()->columnsInserted();
ui->treeView->model()->columnsMoved();
I have a piece of code in my application as follows :
....
QStandardItemModel* authorModel = getAuthorModel(author);
// Create result tab
QTableView* tblView = new QTableView();
tblView->setModel(authorModel);
controller.queryAuthor(author, authorModel);
qDebug() << authorModel->setHeaderData(0, Qt::Horizontal, QVariant("Author Name"), Qt::DisplayRole);
qDebug() << authorModel->setHeaderData(1, Qt::Horizontal, QVariant("Author Id"), Qt::DisplayRole);
int tabIdx = ui->mainTabWidget->addTab(tblView, author);
ui->mainTabWidget->setCurrentIndex(tabIdx);
tblView->setColumnHidden(1, true);
This code is called multiple times creating different tableviews. When authorModel is empty, then setting headerdata fails, also, setColumnHidden fails and once the data is populated default numeric headers are shown and column 1 is visible. Both qDebug statements return false.
However, when the same populated model is used to create a new table view, in the new view column 1 is hidden without issues, and headers are set as they should be. Both qDebug statements return true.
What is the problem and how can it be alleviated ?
Stepping into QStandardItemmodel implementation shows that for these functions unless the column exists to begin with updating header data has no effect.
This can thus be worked around by setting the number of columns your model is designed to use before by using
authorModel->setColumnCount(2);
This way even if the model data is empty column count will return 2 and the calls to set header data should be fine in your case