QStandardItemModel::findItems does not search in QModelIndex(row, col, QModel(row,col)) - c++

I'm trying to create hierarchical tree from sql database.
Tree nodes can have the same name, but unique ID.
Therefore I search for a node parent by its ID, recognize its index and insert a new child row.
Also I know how QModelIndexes are represented in QTreeView.
Example pic
But I'm stuck in QStandardItemModel::findItems. It searches only in QModelIndex(row, col), but never in QModelIndex(row, col, QModel(row,col)).
I would appreciate any advice how to find second column in hierarchical tree model.
Here is my minimum code: main.cpp
#include <QApplication>
#include <QSqlDatabase>
#include <QSqlQuery>
#include <QTreeView>
#include <QStandardItemModel>
void createDB()
{
QSqlDatabase m_db = QSqlDatabase::addDatabase("QSQLITE");
m_db.setDatabaseName(":memory:");
if (!m_db.open()) {
return;
}
QSqlQuery query;
query.exec("CREATE TABLE IF NOT EXISTS tags ("
"id INTEGER PRIMARY KEY AUTOINCREMENT,"
"parent_id INTEGER DEFAULT -1 REFERENCES tags(id),"
"title TEXT NOT NULL);");
query.exec("INSERT INTO tags(title) VALUES('item 1');");
query.exec("INSERT INTO tags(title) VALUES('item 2');");
query.exec("INSERT INTO tags(parent_id, title) VALUES(1, 'sub 1');");
query.exec("INSERT INTO tags(parent_id, title) VALUES(2, 'sub 1');");
query.exec("INSERT INTO tags(parent_id, title) VALUES(2, 'sub 2');");
query.exec("INSERT INTO tags(parent_id, title) VALUES(3, 'sub sub 1');");
query.exec("INSERT INTO tags(parent_id, title) VALUES(3, 'sub sub 2');");
}
void createTreeView() {
QTreeView *m_view = new QTreeView;
QStandardItemModel *m_model = new QStandardItemModel;
m_model->setColumnCount(2);
m_view->setModel(m_model);
QSqlQuery query("SELECT id, parent_id, title FROM tags;");
while (query.next()) {
QList<QStandardItem *> node;
node << new QStandardItem(query.value("title").toString());
node << new QStandardItem(query.value("id").toString());
if (query.value("parent_id").toInt() == -1) {
m_model->appendRow(node);
} else {
QList<QStandardItem *> items = m_model->findItems(query.value("parent_id").toString(), Qt::MatchExactly | Qt::MatchRecursive, 1);
for (QStandardItem *item : items) {
QModelIndex index = item->index().siblingAtColumn(0);
item = m_model->itemFromIndex(index);
item->appendRow(node);
}
}
}
m_view->expandAll();
m_view->resizeColumnToContents(0);
//m_view->hideColumn(1);
m_model->setHeaderData(0, Qt::Horizontal, QVariant());
m_view->show();
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
createDB();
createTreeView();
return a.exec();
}
What I want to get:
> item 1
> sub 1
sub sub 1 // does not appear, because `findItems` returns an empty list.
sub sub 2 // same
> item 2
sub 1
sub 2
I have Qt 5.12.1, MSVC 2017 32bit.

You didn't use the correct "second" parameter of the function "StandardItemModel::findItems". The default scan depth of the function is 1 layer. So, if you want to seek recusively, you should use the parameter "Qt::MatchRecursive".It like:
modelname.findItems("xxxx", Qt::MatchRecursive);

Related

How to select next row automatically in Qt tableView whenever a pushbutton is pressed?

I have a Qt tableView that loads the data from the SQLite database and I have configured it in such a way that in the default view, the first row is automatically selected and I can execute a query on that row by pressing a button-'Present'. Now, I want my program to automatically select the next row after I pressed the 'button' for the first time so that the when I press 'Present' for the second time, the query is executed on the second row. So, basically, I want to change the selection of row whenever a button is pressed until the end of row numbers is reached.
I have searched quite a few sites for the solution, but I could not get the one for my problem.
Code for viewing table s_info and selecting the first row as default.
void table::on_view_clicked()
{
MainWindow conn;
QSqlQueryModel * modal = new QSqlQueryModel();
conn.connOpen();
QSqlQuery* qry= new QSqlQuery(conn.info);
qry->prepare("Select Name,Roll_No from s_info order by Roll_no");
qry->exec();
modal->setQuery(*qry);
ui-> tableView ->setModel(modal);
ui->tableView-> setSelectionBehavior(QAbstractItemView::SelectRows);
ui->tableView->selectRow(0);
ui->tableView->setFocus();
conn.connClose();
qDebug()<< (modal->rowCount());
}
Code for the execution of a query when a button name 'Present' is clicked.
Note that I have performed query execution based on Column Roll_No in my s_info table and index of Roll No for the 1st row is(0,1)
void table::on_present_clicked()
{
QAbstractItemModel *model = ui->tableView->model();
QModelIndex index = model->index(0,1);
QString roll= (index.data().toString());
MainWindow conn;
conn.connOpen();
QSqlQuery qry;
QSqlTableModel modal;
qry.prepare("Update s_info set Present_Days=Present_Days + 1 where
Roll_No='"+roll+"'");
qry.exec();
conn.connClose();
}
I expect that when I click a Present for the second time, the row selection is shifted to the second row and the query is executed on that row. I want this to occur until I reached the end of the number of rows.
The following example illustrates what you are going to achieve. The key-point is that you are going to need an QItemSelectionModel, which manages you selection. Very often one forgets to explicitly set the model of the QItemSelectionModel being the model of the view.
Now, if you will select one row in an table view the next button will select the next row. Selecting the next row basically means to select all columns in the next row.
It shouldn't matter if you are using something like an QSqlTableModel the usage should be simply the same.
#include <QApplication>
#include <QTableView>
#include <QPushButton>
#include <QHBoxLayout>
#include <QStandardItemModel>
#include <QItemSelectionModel>
int main(int argc, char** args) {
QApplication app(argc, args);
auto frame = new QFrame;
frame->setLayout(new QHBoxLayout);
auto tableView = new QTableView;
tableView->setSelectionMode(QAbstractItemView::SelectionMode::SingleSelection);
tableView->setSelectionBehavior(QAbstractItemView::SelectionBehavior::SelectRows);
auto model = new QStandardItemModel;
tableView->setModel(model);
auto selectionModel = new QItemSelectionModel;
selectionModel->setModel(model);
tableView->setSelectionModel(selectionModel);
frame->layout()->addWidget(tableView);
auto button = new QPushButton("Next");
frame->layout()->addWidget(button);
model->setRowCount(10);
model->setColumnCount(10);
frame->show();
QObject::connect(button, &QPushButton::clicked, [&]()
{
auto indices = selectionModel->selectedIndexes();
if (indices.isEmpty()) return;
QModelIndexList empty;
selectionModel->select(QModelIndex(), QItemSelectionModel::SelectionFlag::Clear);
for (auto index : indices)
{
auto newIndex=index.sibling(index.row() + 1, index.column());
selectionModel->select(newIndex,QItemSelectionModel::SelectionFlag::Select);
}
});
app.exec();
}

how to display records from database through qqmllistproperty

Displaying records from database through listview using qqmllistproperty
I am trying to display the records from database through qqmllistproperty but it display only the last records
DataManager::DataManager(QObject *parent) : QObject(parent)
{
}
QQmlListProperty<EmployeeDetails> DataManager::employeedetailslist()
{
return QQmlListProperty<EmployeeDetails>(this, m_employeeDetailsList);
}
void DataManager::setEmployeeDetailsList(const QList<EmployeeDetails *> &employeeDetailsList)
{
m_employeeDetailsList = employeeDetailsList;
}
void DataManager::printAllPersons(){
m_employeeDetailsList.clear();
EmployeeDetails *em = new EmployeeDetails();
QSqlQuery query("SELECT * FROM employee");
int idName = query.record().indexOf("name");
while (query.next())
{
em->setName( query.value(idName).toString());
m_employeeDetailsList.append(em);
}
int count = m_employeeDetailsList.count();
for (int i =0;i<count;i++) {
qDebug()<<"name"<<m_employeeDetailsList[i];
}
qDebug() <<"count"<<count;
// qDebug() <<"name"<<m_employeeDetailsList.;
emit listchanged();
}
There are 9 records , I expected all 9 records should come in the listview, but it shows last record 9 times in the listview

How to Get Absolute Path of Currently Selected Item in QTreeWidget on mouse Clicked

I have a simple QTreeWidget pointing to the root directory:
#include <QTreeWidget>
#include <QStringList>
#include <QApplication>
int main(int argc, char **argv)
{
QApplication application(argc, argv);
QStringList fileNames{"TEST/branch", "trunk"};
QTreeWidget treeWidget;
treeWidget.setColumnCount(1);
for (const auto& filename : fileNames)
{
QTreeWidgetItem *parentTreeItem = new QTreeWidgetItem(&treeWidget);
parentTreeItem->setText(0, filename.split('/').first());
QStringList filenameParts = filename.split('/').mid(1);
for(const auto& filenamePart : filenameParts)
{
QTreeWidgetItem *treeItem = new QTreeWidgetItem();
treeItem->setText(0, filenamePart);
parentTreeItem->addChild(treeItem);
parentTreeItem = treeItem;
}
}
treeWidget.show();
return application.exec();
}
Output:
The item I have selected above is /TEST/branches. How can I get the absolute path of the currently selected item?
Well, I don't think there is a built in function does that but you can write a function yourself like
QString treeItemToFullPath(QTreeWidgetItem* treeItem)
{
QString fullPath= treeItem->text(0);
while (treeItem->parent() != NULL)
{
fullPath= treeItem->parent()->text(0) + "/" + fullPath;
treeItem = treeItem->parent();
}
return fullPath;
}
edit:
Input treeItem is the selected tree item that you want to show its path. if you are sure at least one item is selected, you can get it by
treeWidget.selectedItems().first();
Another mehtod is using tooltips. You can add tip for each item, while you are adding them to tree, but you can do this after you add them in their final place.
change this
for(const auto& filenamePart : filenameParts)
{
QTreeWidgetItem *treeItem = new QTreeWidgetItem();
treeItem->setText(0, filenamePart);
parentTreeItem->addChild(treeItem);
parentTreeItem = treeItem;
}
as this
for(const auto& filenamePart : filenameParts)
{
QTreeWidgetItem *treeItem = new QTreeWidgetItem();
treeItem->setText(0, filenamePart);
parentTreeItem->addChild(treeItem);
parentTreeItem = treeItem;
treeItem->setToolTip(0, treeItemToFullPath(treeItem));
}
this way you will see the full path whenever you hover the mouse on the item.
To get notified of the current item change, one can use QTreeWidget::currentItemChanged or QItemSelectionModel::currentChanged.
There are two main approaches to obtaining the full path:
Iterate up the tree from the selected item and reconstruct the path. This keeps the data model normalized - without redundant information.
Store full path to each item.
If the tree is large, storing the model normalized will use less memory. Given that selection of the items is presumably rare because it's done on explicit user input, the cost of iterating the tree to extract the full path is minuscule. Humans aren't all that fast when it comes to mashing the keys or the mouse button.
The example demonstrates both approaches:
// https://github.com/KubaO/stackoverflown/tree/master/questions/tree-path-41037995
#include <QtWidgets>
QTreeWidgetItem *get(QTreeWidgetItem *parent, const QString &text) {
for (int i = 0; i < parent->childCount(); ++i) {
auto child = parent->child(i);
if (child->text(0) == text)
return child;
}
return new QTreeWidgetItem(parent, {text});
}
int main(int argc, char **argv)
{
QApplication app(argc, argv);
QStringList filenames{"TEST/branch", "TEST/foo", "trunk"};
QWidget window;
QVBoxLayout layout(&window);
QTreeWidget treeWidget;
QLabel label1, label2;
for (const auto &filename : filenames) {
QString path;
auto item = treeWidget.invisibleRootItem();
for (auto const &chunk : filename.split('/')) {
item = get(item, chunk);
path.append(QStringLiteral("/%1").arg(chunk));
item->setData(0, Qt::UserRole, path);
}
}
QObject::connect(&treeWidget, &QTreeWidget::currentItemChanged, [&](const QTreeWidgetItem *item){
QString path;
for (; item; item = item->parent())
path.prepend(QStringLiteral("/%1").arg(item->text(0)));
label1.setText(path);
});
QObject::connect(&treeWidget, &QTreeWidget::currentItemChanged, [&](const QTreeWidgetItem *item){
label2.setText(item->data(0, Qt::UserRole).toString());
});
layout.addWidget(&treeWidget);
layout.addWidget(&label1);
layout.addWidget(&label2);
window.show();
return app.exec();
}

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