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();
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 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.
Assume we have a XamDataGrid with 10 columns.
Column 1 is a XamComboEditor bound to a collection in the model
This can't be changed, the data is coming from a server and the combo's collection is based on different selections within the model so it's very dynamic.
Columns 2 - 10 are just normal alpha numeric fields
The problem:
When you enter a alpha numeric and start typing the model is initialized and everything is fine. However, if you go to the very last row, the non-initialized empty one, and click on the combo editor before entering any data into any of the other fields, the combo editor is empty.
Now I am well aware of why this is happening, it's clear that this is due to the model not being initialized yet. I'm just not sure the best method to fix this.
I would hope there is a property on the XamDataGrid that adjusts when the record is initialized but I've searched the Infragistics documentation and examples and I can't find anything.
There are events for:
EditModeStarting
EditModeStarted
EditModeEnding
EditModeEnded
private void OnCellEditModeStarting(object sender, EditModeStartingEventArgs args)
{
if (args.Cell.Field.Name == "TotalQuantity")
{
DataRecord record = args.Cell.Record;
if (record == null)
return;
MyGridEntry item = record.DataItem as MyGridEntry;
// Do a thing
}
}
You can also respond to the InitializeRecord event. It can fire for multiple reasons, such as cell editing, so check the state of your row model when responding to it. All these events are on the parent grid, not any FieldLayouts or Fields.
<i:XamDataGrid x:Name="myGrid"
InitializeRecord ="OnInitializeRecord"
EditModeStarting ="OnEditModeStarting">
I have a QSqlTableModel and I want to insert and update records in it with a special form in a child-window. It's a design choice not to allow "inline editing" which I disabled on purpose.
When the user selects an entry (which can be sorted and filtered through a QSortFilterProxyModel and is presented through a QTableView), he has three options (represented by buttons): delete, edit and add.
My problem is with editing:
The parent-widget emits a signal with a given record and executes a model child-view
The child-widget prepares a form based on the record, waits for user input, validates it, creates a record and sends it back to the parent-widget.
The parent-widget takes the record and puts it into the database.
And right here is the problem! One can get the right record by row quite easily, like so:
void Parent::on_button_edit_record_clicked()
{
// Table could be sorted/filtered!
row = proxyModel->mapToSource(ui->tableView->currentIndex()).row();
QSqlRecord r = model->record(row);
emit editRecordSignal(record);
child->exec();
}
void Parent::editRecord(const QSqlRecord &record)
{
model->setRecord(row, record);
}
As you can see, I manually save the row of the record I want to update. I don't think this is a nice way to handle this. Actually it seems rather hacky to me.
What I missed was an easy way to translate a primary key to a row and vice versa. Like:
void Parent::editRecord(const QSqlRecord &record)
{
model->setRecord(model->primaryKeyToRow(record->value("id")), record);
}
Is there any way to easily do this (without having to extend the QSqlTableModel), so did I miss something or do I really need to save the row manually to achieve what I want?
I need a piece of code to check that the user has selected at least one row in a QTableWidget
The QTableWidget can be referenced using ui->tableWidget.
I'm trying to check there are selecting, if not, display a message box, if so, moving on to code I have written.
Thanks.
You can obtain the selected rows from the selection model like:
QItemSelectionModel *selectionModel = ui->tableWidget->selectionModel();
QModelIndexList *selectedRows = selectionModel->selectedRows();
if (selectedRows.size() > 0) {
// There is at lease one selected row.
}