How to make a column of a table editable? - c++

I'm trying to make a table that will allow a user to enter a mark out of 10 given some criteria. I'm connected to my SQLite database and retrieving a column on the left for the description of the criteria, and a column on the right where I want to be able to enter a grade (an editable column). Here is my code:
QSqlQueryModel *model = new QSqlQueryModel();
QSqlQuery* qry = new QSqlQuery(conn.mydb);
qry->prepare("select I.itemDescription, S.grade from Items I, ItemsToStudent S where I.itemID = S.itemID and I.rubricID = ? and S.courseID = ? and S.studentID = ?");
qry->addBindValue(actid);
qry->addBindValue(courseid);
qry->addBindValue(studentid);
qry->exec();
model->setQuery(*qry);
ui->rubricTable->setModel(model);
However, I can't make the second column editable. How would I go about accomplishing this?

The QSqlQueryModel class provides a read-only data model for SQL result sets. You can use QSqlRelationalTableModel like:
QSqlRelationalTableModel *model = new QSqlRelationalTableModel(this,conn.mydb);
model->setEditStrategy(QSqlTableModel::OnFieldChange);
model->setJoinMode(QSqlRelationalTableModel::LeftJoin);
model->setTable( "Items" );
model->setRelation( 0, QSqlRelation( "ItemsToStudent", "itemID", "grade" ) );
model->setFilter( QString("rubricID = ? and courseID = ? and studentID = ?") );
model->select();
model->setHeaderData( 1, Qt::Horizontal, tr("Criteria") );
model->setHeaderData( 2, Qt::Horizontal, tr("Grade") );
ui->rubricTable->setModel(model);
You can also hide the columns you don't want to show like:
ui->rubricTable_tbl->hideColumn(3);

Related

Qt: QSqlRelationalTableModel reference to non-existing foreign key

Let's say that I have a table 'person' with the following columns:
id, name, manager_id. Where 'id' is the primary key and 'manager_id' is the foreign key. Since some people might now have a manager this value is allowed to be NULL. However, this seems to create problems with Qt's QSqlRelationalTableModel.
Here is a minimalistic example which replicates the problem:
window.cpp:
Window::Window(QWidget *parent) : QWidget(parent)
{
// setup database
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
db.setDatabaseName(":memory:");
db.open();
// create database
QSqlQuery query;
query.exec("create table 'person' (id INTEGER NOT NULL PRIMARY KEY, "
"name varchar(20), manager_id INTEGER NULL)");
query.exec("insert into person values(1, 'Alice', 2)");
query.exec("insert into person values(2, 'Bob', -1)"); // invalid manager_id
//query.exec("insert into person values(2, 'Bob', 1)"); // valid example
// setup model
model = new QSqlRelationalTableModel(this);
model->setTable("person");
model->setEditStrategy(QSqlTableModel::OnRowChange);
// setup foreign key
int typeIndex = model->fieldIndex("manager_id");
model->setRelation(typeIndex, QSqlRelation("person", "id", "name"));
model->select();
// setup UI
auto nameLabel = new QLabel(tr("Name:")); auto nameEdit = new QLineEdit();
auto typeLabel = new QLabel(tr("Manager:")); auto typeComboBox = new QComboBox();
auto nextButton = new QPushButton(tr("Next"));
auto previousButton = new QPushButton(tr("Previous"));
QSqlTableModel *relModel = model->relationModel(typeIndex);
typeComboBox->setModel(relModel);
typeComboBox->setModelColumn(relModel->fieldIndex("name"));
QGridLayout *layout = new QGridLayout();
layout->addWidget(nameLabel, 0, 0, 1, 1);
layout->addWidget(nameEdit, 0, 1, 1, 1);
layout->addWidget(previousButton, 0, 2, 1, 1);
layout->addWidget(nextButton, 1, 2, 1, 1);
layout->addWidget(typeLabel, 2, 0, 1, 1);
layout->addWidget(typeComboBox, 2, 1, 1, 1);
setLayout(layout);
// setup mapper
mapper = new QDataWidgetMapper(this);
mapper->setModel(model);
mapper->setItemDelegate(new QSqlRelationalDelegate(this));
mapper->addMapping(nameEdit, model->fieldIndex("name"));
mapper->addMapping(typeComboBox, typeIndex);
mapper->toFirst();
connect(previousButton, SIGNAL(clicked()), mapper, SLOT(toPrevious()));
connect(nextButton, SIGNAL(clicked()), mapper, SLOT(toNext()));
}
window.h:
#include <QWidget>
class QDataWidgetMapper;
class QSqlRelationalTableModel;
class Window : public QWidget
{
Q_OBJECT
public:
Window(QWidget *parent = 0);
private slots:
private:
QDataWidgetMapper *mapper;
QSqlRelationalTableModel *model;
};
The problem is that the second record (i.e., "Bob") will not be displayed because his manager's id is invalid (-1).
The documentation of the QSqlRelationalTableModel states:
"If a relational table contains keys that refer to non-existent rows in the referenced table, the rows containing the invalid keys will not be exposed through the model. The user or the database is responsible for keeping referential integrity."
However, is there no way around this? It seems to me that this is a common problem. Thanks.
According to your description of the data, "no manager" is an allowable value, so it makes no sense to refer to it as invalid.
The real problem in your example is that the name column has several overlapping puposes. It really should be a separate names table, which can then have a row with an empty string to indicate "no manager".
The person table would then just contain ids from the names table.

How to fill database sqlite tablewidget

I am writing "SQLite" database and I create the database table like this:
void MainWindow::createdata()
{
QSqlQuery query;
query.exec("DROP TABLE messages");
query.exec("CREATE TABLE messages("
"id INTEGER PRIMARY KEY AUTOINCREMENT,"
"IPAddress VARCHAR(20),"
"date VARCHAR(10),"
"message VARCHAR(30))");
query.prepare("INSERT INTO messages(IPAddress, date, message) values(?,?,?)");
query.addBindValue("192.168.1.1");
query.addBindValue("jun 3 2016");
query.addBindValue("hello");
if (query.exec()) {
qDebug() << "ok!";
}
else
{
qDebug() << query.executedQuery();
qDebug() << query.lastError();
}
}
And i create "qtablewidget" like this:
QTableWidget* table = new QTableWidget();
table->setRowCount(10);
table->setColumnCount(4);
table->setWindowTitle("Received Message");
table->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding);
table->setHorizontalHeaderLabels(QString("ID;HostAddress;Date;Message").split(";"));
table->setStyleSheet("QTableView {selection-background-color: blue;}");
table->setEditTriggers(QAbstractItemView::NoEditTriggers);
table->setSelectionBehavior(QAbstractItemView::SelectRows);
table->setSelectionMode(QAbstractItemView::SingleSelection);
QSqlQuery query("SELECT * FROM messages");
but i don't know how should i fill the "tablewidget" to show with query.
Can anyone please help me?Thanks
You should use QTableView (Model based version of table view) and then using QSqlQueryModel, you can populate your table.
QSqlQueryModel *model = new QSqlQueryModel();
model->setQuery(query);
tableView->setModel(model);
To use QSqlQueryModel with a QTableWidget, you should iterate through QSqlQueryModel row by row and add them to your QTableWidget.
QSqlQueryModel *model = new QSqlQueryModel();
model->setQuery(query);
int i;
QSqlRecord row
for(i = 0, row = model->record(i); !row.isEmpty(); i++, row = model->record(i)){
// Get each field using `value` method of variabale 'row'
// and insert this fields to its corresponding cell in QTableWidget
}
Relevant Question:
Setting the model to a QTableWidget

Empty qsqlrelationaltablemodel

When I put model->select(); after relations, the model is empty, when I put it back before relations, the table displays correctly, but when I edit any cell the whole row becomes empty and gets "!" on the left. No errors.
QSqlRelationalTableModel *model = new QSqlRelationalTableModel(this, db1);
model->setTable("syllabi");
model->setEditStrategy(QSqlTableModel::OnFieldChange);
model->select();
model->setRelation(3, QSqlRelation("activity_types", "activity_type_id", "activity_type_name"));
model->setRelation(0, QSqlRelation("teachers", "teacher_id", "teacher_name"));
//model->select();
qDebug() << model->lastError();
ui->tableView->setModel(model);
ui->tableView->setItemDelegate(new QSqlRelationalDelegate(ui->tableView));
ui->tableView->horizontalHeader()->setStretchLastSection(true);
ui->tableView->setColumnHidden(9, true);

Showing output of sql query inside a QLabel

I am writing a qt gui application where I am planning to show the output of an sql query inside a QLabel.
Now population the output inside a QTableView model is simple enough and that I can do using;
QSqlDatabase dbSqlite = QSqlDatabase::addDatabase("QSQLITE"); //these 2 lines for SQLite DB connection
dbSqlite.setDatabaseName("/home/aj/test.db");
dbSqlite.setUserName("aj");
QString MyQuerySqlite = ui->sqlite_queryEdit->text(); //take the query from a QLineEdit
dbSqlite.open(); //open db connection
QSqlQuery query(MyQuerySqlite,dbSqlite);
if(query.exec()) //populate in table
{
this->model1=new QSqlQueryModel();
model1->setQuery(MyQuerySqlite);
ui->sqlite_tableView->setModel(model1);
qDebug()<<QDateTime::currentDateTime()<<"SQLITE QUERY SUCCESS "<<dbSqlite.lastError().text();
}
Any idea on how to achieve this inside a QLabel ???
The output of the query will be one single record. For example the name of the world's tallest mountain, or the name of the capital of Engalnd. Just single records.
If you want to use QSqlQueryModel for this, you can use QSqlQueryModel::record ( int row ) to retrieve a specific record and then QSqlRecord::value ( int index ) to get a field's value :
QString str = model1->record(0).value(0).toString();
label->setText(str);
You can get result with value method. For example:
QString country;
QSqlQuery query("SELECT country FROM artist");
while (query.next()) {
country.append( query.value(0).toString() + " ");
}
label->setText(country);
Also you can read data from model1. Something like:
label->setText(model1->data(model1->index(0,0),Qt::DisplayRole).toString());

How to show the result of multiple selects in QTableView using QSqlQueryModel

I am using the following approach to show the result of a select statement in the QTableView. How should I modify this code to show the result of two or more different select statements in the same QTableView?
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
QString dbPath = "test1.db";
db.setDatabaseName(dbPath);
QSqlQueryModel *model = new CustomSqlModel();
QSqlQuery* query = new QSqlQuery(db);
query->prepare("SELECT * FROM MyTable");
query->exec();
model->setQuery(*query);
model->setHeaderData(0, Qt::Horizontal, "Col1");
model->setHeaderData(1, Qt::Horizontal, "Col2");
model->setHeaderData(2, Qt::Horizontal, "Col3");
QTableView* tableView = new QTableView();
tableView->setModel(model);
I need to append the data selected from the same table in another database test2.db to the data already shown in the tableView.
To append the result of one query to another, use a compound SELECT statement:
SELECT * FROM MyTable
UNION ALL
SELECT * FROM MyOtherTable
This requires that the subselects have the same number of columns.
If the other table is in another database, you must ATTACH it to the first one:
db.exec("ATTACH '/somewhere/else/test2.db' AS test2");
...
query->prepare("SELECT * FROM MyTable UNION ALL SELECT * FROM test2.MyTable");