Storing Directory hierarchy in Datastructure Qt - c++

How should i store data which is going into a TreeView? A Dictionary (QHash)? Plain Text? JSON?
My Hierarchy would be something like:
{
'Cloth': {
'Tissue':None,
'Leather': {
'Bandage': None
}
},
'Smoke': {
'White':{
'Smallscale': None,
'Largescale':None
}
}
}
Actions:
When I click a leaf-Element it will retrieve the Fullpath, like "Smoke/White/Smallscale" and this will be used as a key to place a SQL-Query.
I would use QStandardItem for every entry and when clicked, I would recursively call their parents, till I hit root.
Any thoughts?

Do you know QJsonTreeWidget?
Of course you don't need to use that library, but I do think you should use JSON in any case. It's almost a standard nowadays and very useful when we're working with trees.
Boost also has a wonderful library to work with JSON.

You can use one of the json library (like cajun) to parse json file.
This is the Qt part:
#include <QtGui>
#include <QTreeView>
class SimpleTreeView :public QTreeView
{
Q_OBJECT
public:
SimpleTreeView(QWidget *parent = 0);
public slots:
void slot_item_clicked(const QModelIndex &idx);
private:
QStandardItemModel *model;
};
#include <simpletreeview.h>
#include <qmessagebox.h>
#include <qobject.h>
SimpleTreeView::SimpleTreeView(QWidget *parent) : QTreeView(parent)
{
model = new QStandardItemModel(2,1);
QStandardItem *item1 = new QStandardItem("Cloth");
QStandardItem *item2 = new QStandardItem("Smoke");
model->setItem(0, 0, item1);
model->setItem(1, 0, item2);
QStandardItem *item3 = new QStandardItem("White");
item2->appendRow(item3);
QStandardItem *leaf = new QStandardItem("Smallscale");
leaf->setData("Smoke/White/Smallscale");
item3->appendRow(leaf);
setModel(model);
connect(this, SIGNAL(clicked(const QModelIndex &)), this, SLOT(slot_item_clicked(const QModelIndex &)));
}
void SimpleTreeView::slot_item_clicked(const QModelIndex & idx)
{
QString strData = model->itemFromIndex(idx)->data().toString();
QMessageBox::information(NULL, "Title", strData, QMessageBox::Yes, QMessageBox::Yes);
}
// main.cpp
#include <QApplication>
#include <simpletreeview.h>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
SimpleTreeView view;
view.show();
return app.exec();
}

Related

QListView set Custom Editor via QStyledItemDelegate::createEditor

I want to show custom widget in each QListView cells (3 labels width different fonts and 2 tool buttons). The widget must handle mouse events for correct handling of the hover events and button clicks. (Therefore I cannot just draw it in QStyledItemDelegate::paint()).
Here is what I want each row in a list view looks like:
The main idea: QAbstractItemView::openPersistentEditor().
#include <QApplication>
#include <QWidget>
#include <QHBoxLayout>
#include <QLabel>
#include <QToolButton>
#include <QVBoxLayout>
#include <QDateTime>
#include <QListView>
#include <QStringListModel>
#include <QStyledItemDelegate>
class Form : public QWidget
{
//Q_OBJECT
public:
explicit Form(QWidget *parent = nullptr)
:QWidget(parent)
{
verticalLayout = new QVBoxLayout(this);
horizontalLayout = new QHBoxLayout();
labelTitle = new QLabel(this);
labelTitle->setFont(QFont("Calibri", 12, QFont::Bold));
horizontalLayout->addWidget(labelTitle);
toolButtonEdit = new QToolButton(this);
toolButtonEdit->setText("E");
horizontalLayout->addWidget(toolButtonEdit);
toolButtonRemove = new QToolButton(this);
toolButtonRemove->setText("R");
horizontalLayout->addWidget(toolButtonRemove);
verticalLayout->addLayout(horizontalLayout);
labelDate = new QLabel(this);
labelDate->setFont(QFont("Calibri", 8));
verticalLayout->addWidget(labelDate);
labelText = new QLabel(this);
labelText->setFont(QFont("Calibri", 10));
verticalLayout->addWidget(labelText);
verticalLayout->setStretch(2, 1);
setMinimumSize(QSize(300, 50));
}
public:
QVBoxLayout *verticalLayout;
QHBoxLayout *horizontalLayout;
QLabel *labelTitle;
QToolButton *toolButtonEdit;
QToolButton *toolButtonRemove;
QLabel *labelDate;
QLabel *labelText;
};
class MyDelegate : public QStyledItemDelegate
{
public:
QWidget* createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override
{
auto editor = new Form(parent);
return editor;
}
void setEditorData(QWidget *ed, const QModelIndex &index) const override
{
QVariant var = index.model()->data(index, Qt::DisplayRole);
if (Form *editor = dynamic_cast<Form*>(ed))
{
editor->labelTitle->setText("SYMBOL");
editor->labelDate->setText("date-time");
editor->labelText->setText(var.toString());
}
}
void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem& option, const QModelIndex &)const override
{
editor->setGeometry(option.rect);
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Form form(nullptr);
form.labelTitle->setText("TITLE");
form.labelDate->setText(QDateTime::currentDateTime().toString());
form.labelText->setText("text body");
form.show();
auto model = new QStringListModel;
model->setStringList(QStringList()
<< "text body 1"
<< "text body 2"
<< "text body 3");
auto view = new QListView(nullptr);
view->setModel(model);
view->setItemDelegate(new MyDelegate);
int rowCount = model->rowCount();
for (int row = 0; row < rowCount; ++row)
{
QModelIndex index = model->index(row, 0);
view->openPersistentEditor(index);
}
view->show();
return a.exec();
}
Here is how the list view actually looks:
What how can one set such a custom widget to show view cells?
Note that while you are defining your own delegate MyDelegate you never actually use it (i.e. by calling QAbstractItemView::setItemDelegate(). Therefore you see the default delegate (a simple QLineEdit for data of type QString) when calling openPersistentEditor().

How to make a QStandardItemModel from QStringList of file paths

i'm new to C++ and Qt and can't seem to find the right solution for this. I want to create a QTreeView using a QStandardItemModel. I have a list of file/folder paths (within a QStringList) structured in a format like this:
Arena/Main/00078/a.txt
Arena/Main/00080/b.txt
Arena/Main/00080/collision/c.txt
Arena/Main/00080/light/d.txt
Arena/Main/00081/e.txt
Characters/f.txt
Characters/Main/g.txt
Characters/Main/someFolder/h.txt
I previously used a QFileSystemModel and made actual temporary directories, iterating through the list as a workaround:
QDir dir(temp_path);
if (!dir.exists(dir_string)){
dir.mkpath(dir_string);
}
QFile file(filepath_str);
file.open(QIODevice::WriteOnly);
Which gave me a working result like this: https://i.stack.imgur.com/ITzJz.png
However, the list can reach up to 10,000+ files making it a longer backwards workaround. I want to populate a QTreeView in a similar way using a QStandardItemModel created from the path list.
You can store the created paths in a map like this:
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QMap>
#include <QStandardItemModel>
class QStandardItem;
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private:
void addPathRecusively(const QStringList &path_parts, QStandardItem *parent, int layer = 1);
QMap<QString, QStandardItem*> path_item_map_;
QStandardItemModel model_;
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include <QStringList>
#include <QTreeView>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
QStringList filenames =
{
"Arena/Main/00078/a1.txt",
"Arena/Main/00078/a2.txt",
"Arena/Main/00080/b.txt",
"Arena/Main/00080/collision/c.txt",
"Arena/Main/00080/light/d.txt",
"Arena/Main/00081/e1.txt",
"Arena/Main/00081/e2.txt",
"Arena/Main/00081/e3.txt",
"Characters/f.txt",
"Characters/Main/g.txt",
"Characters/Main/someFolder/h.txt",
};
for (auto &filename : filenames)
{
QStringList parts = filename.split("/");
parts.pop_back();
addPathRecusively(parts, model_.invisibleRootItem());
}
QTreeView *view = new QTreeView{this};
view->setModel(&model_);
this->setCentralWidget(view);
this->resize(500, 500);
}
MainWindow::~MainWindow()
{
}
void MainWindow::addPathRecusively(const QStringList &path_parts, QStandardItem *parent, int layer)
{
QStringList path_parts_to_layer;
std::copy(path_parts.begin(), path_parts.begin() + layer, std::back_inserter(path_parts_to_layer));
QString path = path_parts_to_layer.join("/");
auto item = path_item_map_.value(path);
if (item == nullptr)
{
item = new QStandardItem{path_parts_to_layer.last()};
path_item_map_.insert(path, item);
parent->appendRow({item, new QStandardItem});
}
if (path_parts.length() == layer)
{
auto file_count_index = model_.indexFromItem(item).siblingAtColumn(1);
model_.setData(file_count_index, file_count_index.data().toInt() + 1, Qt::DisplayRole);
}
else
addPathRecusively(path_parts, item, layer + 1);
}

How to get the selected item in a QTreeView

I have a tree like this:
|-Parent
| |-Child-Child
|-Parent
| |-Child-Child
...
Only the Parents are selectable. How can I get the data from the selected Parent?
I tried
ui->treeView->selectedIndexes()[0];
but it says that selectedIndexes() is protected.
You need to call QItemSelectionModel::selectedIndexes() instead, i.e.:
QModelIndexList indexes = ui->treeView->selectionModel()->selectedIndexes();
if (indexes.size() > 0) {
QModelIndex selectedIndex = indexes.at(0);
[..]
}
How to get the selected item in a QTreeView?
The question is simple and the answers are terrible, and the tutorial worse.
Below is a fully functional example which shows how to get the selected item. Specifically selected_item()
#include <QApplication>
#include <QTreeView>
#include <QStandardItemModel>
#include <QItemSelectionModel>
#include <QGridLayout>
#include <iostream>
struct Node:public QStandardItem {
Node(std::string name):QStandardItem(name.c_str()){}
virtual void operator()(){
std::cout<<"selected node named: "<<text().toStdString()<<std::endl;
}
};
class TreeView :public QWidget{
Q_OBJECT
public:
QTreeView tree;
using Model=QStandardItemModel;
Model* item_model(){ return (Model*)tree.model(); }
Node* selected_item() {
QModelIndex index = tree.currentIndex();
if(!index.isValid()) return nullptr; // if the user has selected nothing
return (Node*)(item_model()->itemFromIndex(index));
}
TreeView() {
// automatically sets to parent
auto layout=new QGridLayout(this);
layout->addWidget(&tree,0,0);
// set the item model, there is no sane choice but StandardItemModel
tree.setModel(new Model());
connect(tree.selectionModel(),
&QItemSelectionModel::selectionChanged,
this,
&TreeView::selected);
// create a small tree
auto top=new Node("top");
auto a=new Node("a");
a->appendRow(new Node("a0"));
a->appendRow(new Node("a1"));
auto b=new Node("b");
top->appendRow(a);
top->appendRow(b);
// add it to the treeview root
item_model()->invisibleRootItem()->appendRow(top);
}
private slots:
void selected(
const QItemSelection &news, // not used
const QItemSelection &olds)
{
auto* node=selected_item();
if(node) (*node)();
}
};
int main(int argc, char** argv){
QApplication a(argc, argv);
TreeView w;
w.show();
return a.exec();
}

Qt send data from form1 into form2 treeview component

I am pretty new to Qt, but I guess that I need to use signals and slots to send/receive data, at least in tutorials and other posts it's always like that.
This should be working:
Push Import button, select xls file -> right after we've selected it. We are running through the file and get the same sheet name and sheet table column headers for each sheet (then the user selects the column and all what that column contains; we are inserting into a database, etc)
I want to make a GUI application like this in my "excel-2-some-db" module:
I got two classes. First one - import_module:
import_module.h:
#ifndef IMPORTDB_MODULE_H
#define IMPORTDB_MODULE_H
#include <QtGui/QMainWindow>
#include <QAxObject>
#include <QAxWidget>
#include "ui_importdb_module.h"
#include "headers_selection.h"//select form
class importdb_module : public QMainWindow
{
Q_OBJECT
public:
importdb_module(QWidget *parent = 0, Qt::WFlags flags = 0);
~importdb_module();
//...
private:
Ui::importdb_moduleClass ui;
//...
headers_selection* select_form;
public slots:
void on_getExcelPath_clicked();
void on_pushButton1_clicked();
//signal we will send after read some sheets columns
signals:
void sendTreeViewData(QString &sheet_name, QStringList &sheet_headers);
};
#endif // IMPORTDB_MODULE_H
import_module.cpp
#include <QtGui>
#include <QApplication>
#include "importdb_module.h"
importdb_module::importdb_module(QWidget *parent, Qt::WFlags flags)
: QMainWindow(parent, flags)
{
ui.setupUi(this);
select_form = new headers_selection();
//to show form on click
connect(ui.pushButton1, SIGNAL(clicked()), select_form, SLOT(show()));
connect(this,
SIGNAL(sendTreeViewData(QString &sheet_name, QStringList &sheet_headers)),
select_form,
SLOT(recieveTreeViewData(QString &sheet_name, QStringList &sheet_headers)));
}
importdb_module::~importdb_module()
{
}
//....
//all file headers
void importdb_module::readSheetsHeaders(QAxObject* &_workbook, QAxObject* _worksheets, QAxObject* &_excel){
QAxObject* sheet_i;
int sheets_count = _worksheets->property("Count").toInt(); //get how much lists there, gonna choose one to import data
QString sheet_name;
QStringList sheet_headers;//sheet headers will store here
//sheets num starts from 1
for(int i=1; i<= sheets_count; i++){
sheet_i = _workbook->querySubObject("Worksheets(int)", i);//get teh list
sheet_name = sheet_i->property("Name").toString();//get teh name
//...
getTableHeaders(sheet_i, sheet_headers);
//says that we've send tree data into the form
emit sendTreeViewData(sheet_name, sheet_headers);
sheet_i->clear();
sheet_headers.clear();
}
delete sheet_i;
};
//...
headers_selection.h
#ifndef HEADERS_SELECTION_H
#define HEADERS_SELECTION_H
#include <QWidget>
#include "ui_headers_selection.h"
class headers_selection : public QWidget
{
Q_OBJECT
public:
headers_selection(QWidget *parent = 0);
~headers_selection();
private:
Ui::headers_selection ui;
public slots:
void recieveTreeViewData(QString &sheet_name, QStringList &sheet_headers);
};
#endif // HEADERS_SELECTION_H
headers_selection.cpp
#include "headers_selection.h"
headers_selection::headers_selection(QWidget *parent)
: QWidget(parent)
{
ui.setupUi(this);
QStandardItemModel *model = new QStandardItemModel;
}
headers_selection::~headers_selection()
{
}
void headers_selection::recieveTreeViewData(QString &sheet_name, QStringList &sheet_headers)
{
//just test
QStandardItemModel *model = new QStandardItemModel;
model->setColumnCount(1);
model->setRowCount(5);
model->setData(model->index(0, 0), "some data0");
model->setData(model->index(1, 0), "some data1");
model->setData(model->index(2, 0), "some data2");
model->setData(model->index(3, 0), "some data3");
model->setData(model->index(4, 0), "some data4");
ui.treeView->setModel(model);
}
And after I import .xls and opening treeview selection windows - it's empty!
So I got two possible problems here
maybe sendTreeViewData does not invoke recieveTreeViewData or maybe I do something wrong with tree view inside recieveTreeViewData method.
Could somebody help me to fix it, please?
UPDATE
Well, I made a little changes (thanks to thomas_b, his answer shown me the way) in code and now it works!
//impordb_module.h
signals:
void sendTreeViewData(QString &sheet_name, QStringList &sheet_headers);
//heaqders_selection.h
public slots:
void recieveTreeViewData(QString &sheet_name, QStringList &sheet_headers);
//heaqders_selection.cpp
void headers_selection::recieveTreeViewData(QString &sheet_name, QStringList &sheet_headers)
{
qDebug()<<sheet_name<<" gotcha! ";
}
//in impordb_module.cpp
importdb_module::importdb_module(QWidget *parent, Qt::WFlags flags): QMainWindow(parent, flags){
connect(this, SIGNAL(sendTreeViewData(QString &,QStringList &)), select_form, SLOT(recieveTreeViewData(QString &,QStringList &)));
}
//...
void importdb_module::readSheetsHeaders(QAxObject* &_workbook, QAxObject* _worksheets, QAxObject* &_excel){
//...
getTableHeaders(sheet_i, sheet_headers);
//says that we've send tree data into the form
emit sendTreeViewData(sheet_name, sheet_headers);
//...
};
Maybe your connection failed because of the parameter names in your connect statement. Try change it to:
connect(this, SIGNAL(sendTreeViewData(QString,QStringList)),
select_form, SLOT(recieveTreeViewData(QString,QStringList)));

How to merge two cells of a qtable row?

I have created a table using qtablewidget. I would like to selectively merge cells of some rows. Is there any built-in function for that(sorry, I couldn't find one)? Otherwise, please specify how it can be done. Thanks
You could use the setSpan function of QTableView (which QTableWidget inherits) to achieve something like that for rows and columns.
That is, unless you're looking to really just merge individual cells. I'm not sure that can be achieved with setSpan, but have a look at it anyway.
Try QTableWidget::setSpan(int row, int column, int rowSpan, int columnSpan);
But in some versions it doesn't work.
Sample program
#include <QApplication>
#include <QVBoxLayout>
#include <QPushButton>
#include <QLabel>
#include <QTableWidget>
#include <QHeaderView>
class TestWidget: public QWidget
{
Q_OBJECT
private:
QTableWidget *testTable;
QVBoxLayout *mainLayout;
QPushButton *button;
public:
TestWidget(QWidget *parent=nullptr) : QWidget(parent)
{
mainLayout = new QVBoxLayout(this);
testTable = new QTableWidget;
testTable = new QTableWidget(this);
testTable->setObjectName("testTable");
testTable->setFixedWidth(400);
testTable->setSizePolicy(QSizePolicy::Fixed,QSizePolicy::Minimum);
testTable->setColumnCount(4);
testTable->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
testTable->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
QTableWidgetItem *itemC1 = new QTableWidgetItem("Column 1");
testTable->setHorizontalHeaderItem(0, itemC1);
QTableWidgetItem *itemC2 = new QTableWidgetItem("Column 2");
testTable->setHorizontalHeaderItem(1, itemC2);
QTableWidgetItem *itemC3 = new QTableWidgetItem("Column 3");
testTable->setHorizontalHeaderItem(2, itemC3);
QTableWidgetItem *itemC4 = new QTableWidgetItem("Column 4");
testTable->setHorizontalHeaderItem(3, itemC4);
testTable->setColumnWidth(0,100);
testTable->setColumnWidth(1,100);
testTable->setColumnWidth(2,100);
testTable->setColumnWidth(3,100);
testTable->horizontalHeader()->setSectionResizeMode(QHeaderView::Fixed);
testTable->setEditTriggers(QAbstractItemView::DoubleClicked | QAbstractItemView::AnyKeyPressed);
testTable->setProperty("showDropIndicator", QVariant(false));
testTable->setDragDropOverwriteMode(false);
testTable->setSelectionMode(QAbstractItemView::SingleSelection);
testTable->setSelectionBehavior(QAbstractItemView::SelectRows);
testTable->setShowGrid(true);
testTable->setGridStyle(Qt::SolidLine);
testTable->setSortingEnabled(false);
testTable->setCornerButtonEnabled(false);
testTable->horizontalHeader()->setHighlightSections(false);
testTable->horizontalHeader()->setProperty("showSortIndicator", QVariant(false));
testTable->verticalHeader()->setVisible(false);
testTable->setAlternatingRowColors(true);
button = new QPushButton("Group");
connect(button, SIGNAL(clicked(bool)), this, SLOT(buttonClicked(bool)));
mainLayout->addWidget(testTable);
mainLayout->addWidget(button);
this->setLayout(mainLayout);
}
void addRow(QString c1, QString c2, QString c3, QString c4)
{
int row = testTable->rowCount();
testTable->insertRow(row);
QTableWidgetItem *c1Item = new QTableWidgetItem(c1);
QTableWidgetItem *c2Item = new QTableWidgetItem(c2);
QTableWidgetItem *c3Item = new QTableWidgetItem(c3);
QTableWidgetItem *c4Item = new QTableWidgetItem(c4);
c1Item->setTextAlignment(Qt::AlignLeft | Qt::AlignVCenter);
c2Item->setTextAlignment(Qt::AlignLeft | Qt::AlignVCenter);
c3Item->setTextAlignment(Qt::AlignLeft | Qt::AlignVCenter);
c4Item->setTextAlignment(Qt::AlignLeft | Qt::AlignVCenter);
testTable->setItem(row, 0, c1Item);
testTable->setItem(row, 1, c2Item);
testTable->setItem(row, 2, c3Item);
testTable->setItem(row, 3, c4Item);
}
void updateCell(int rowNumber, int columnNumber, QString textString)
{
if(rowNumber>=testTable->rowCount() || columnNumber>=testTable->columnCount()) return;
testTable->item(rowNumber, columnNumber)->setText(textString);
}
void groupCells(int rowNumber, int columnNumber, int rowSpan, int columnSpan)
{
if(rowNumber>=testTable->rowCount() || columnNumber>=testTable->columnCount()) return;
testTable->setSpan(rowNumber,columnNumber,rowSpan,columnSpan);
}
void ungroupCells(int rowNumber, int columnNumber)
{
if(rowNumber>=testTable->rowCount() || columnNumber>=testTable->columnCount()) return;
testTable->setSpan(rowNumber,columnNumber,1,1);
}
public slots:
void buttonClicked(bool event)
{
Q_UNUSED(event)
if(button->text()=="Group")
{
groupCells(1,1,2,1);
button->setText("Ungroup");
}
else
{
ungroupCells(1,1);
button->setText("Group");
}
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
TestWidget wid;
wid.addRow("0,0", "0,1", "0,2", "0,3");
wid.addRow("1,0", "1,1", "1,2", "1,3");
wid.addRow("2,0", "2,1", "2,2", "2,3");
wid.addRow("3,0", "3,1", "3,2", "3,3");
wid.show();
return a.exec();
}
#include "main.moc"
Project file shall be downloaded from github