What Is a Best WAY popuplate QTreeWidget & Database?



  • alt text
    alt text

    This is My ALgorithm:
    I have only ONE ROOT And ONE COLUMN.

    Wooden arrangement:

       QSqlQuery qry;
        qry.prepare("select * from tree");
        if(qry.exec()){
            while(qry.next()){
                QTreeWidgetItem *item = new QTreeWidgetItem(QStringList() << qry.value("name").toString());
                item->setWhatsThis(0, qry.value("parent").toString());
                mp[qry.value("UID").toInt()] = item;
    
                if(qry.value("parent").toInt() == 0){
                    top = item;
                }
    
                mpuid[item] = qry.value("uid").toInt();
            }
        }
    
        ui->treeWidget->addTopLevelItem(top);
    
        for(it = mp.begin(); it != mp.end(); it++){
            if(it.key() == mpuid[top]) continue;
            QTreeWidgetItem *item = mp[it.value()->whatsThis(0).toInt()];
            item->addChild(it.value());
        }
    

    Insert Child:
    void MainWindow::on_btnInsertChild_clicked()
    {
    QTreeWidgetItem *last = ui->treeWidget->currentItem();

    if(db.open()){
        QSqlQuery qry;
                  qry.prepare("insert into tree(name, parent) values (:nm, :pr)");
                  qry.bindValue(":nm", QString::number(++cnt));
                  qry.bindValue(":pr", mpuid[last]);
    
        QTreeWidgetItem * newitem = new QTreeWidgetItem(QStringList() << QString::number(cnt));
    
        if(qry.exec()){
            last->addChild(newitem);
        }
    
        //Get Last Item UID
        
        QSqlQuery sel;
                  sel.prepare("select seq from sqlite_sequence where name = 'Tree'");
                  sel.exec();
                  sel.next();
    
        mpuid[newitem] = sel.value(0).toInt();
    }
    

    }

    Remove Node:

    void MainWindow::DFS(QTreeWidgetItem *v, QTreeWidgetItem *pr)
    {
        used[v] = true;
    
        for(int i = 0; i < v->childCount(); i++){
            if(v == pr) continue;
            QTreeWidgetItem * go = v->child(i);
            if(!used[go]){
                DFS(go, v);
            }
        }
    
        if(v != top){
            removeitems.push_back({pr, v});
        }
    }
    void MainWindow::on_btnRemoveNode_clicked()
    {
        DFS(ui->treeWidget->currentItem(), ui->treeWidget->currentItem()->parent());
        
        for(unsigned int i = 0; i < removeitems.size(); i++){
            std::pair<QTreeWidgetItem*, QTreeWidgetItem*> now = removeitems[i];
                now.first->removeChild(now.second);
        }
    }
    

    H.file

    #ifndef MAINWINDOW_H
    #define MAINWINDOW_H
    
    #include <QMainWindow>
    #include "form.h"
    #include "QAction"
    #include "QTreeWidget"
    #include "QDebug"
    #include "QSqlDatabase"
    #include "QSqlQuery"
    #include "QSqlError"
    #include "queue"
    
    namespace Ui {
    class MainWindow;
    }
    
    class MainWindow : public QMainWindow
    {
        Q_OBJECT
    
    public:
        explicit MainWindow(QWidget *parent = 0);
        ~MainWindow();
    
        QTreeWidgetItem * top;
    
        std::vector<std::pair<QTreeWidgetItem*, QTreeWidgetItem*> > removeitems;
    
        QMap<int, QTreeWidgetItem*> mp;
        QMap<int, QTreeWidgetItem*> ::iterator it;
    
        QMap<QTreeWidgetItem*, int> mpuid;
        QMap<QTreeWidgetItem*, bool> used;
    
        void DFS(QTreeWidgetItem * v, QTreeWidgetItem * pr);
    
        int cnt;
    
        QSqlDatabase db;
    
    signals:
    
    public slots:
    
    private slots:
        void on_treeWidget_itemDoubleClicked(QTreeWidgetItem *item, int column);
    
        void on_btnRemoveNode_clicked();
    
        void on_treeWidget_doubleClicked(const QModelIndex &index);
    
        void on_btnInsertChild_clicked();
    
        void on_treeWidget_itemClicked(QTreeWidgetItem *item, int column);
    
    private:
        Ui::MainWindow *ui;
    };
    
    #endif // MAINWINDOW_H
    
    


  • IM sorry. I forgot to remove it from the base. After deleting the Node.

        std::vector<int> del;
    
        for(unsigned int i = 0; i < removeitems.size(); i++){
            std::pair<QTreeWidgetItem*, QTreeWidgetItem*> now = removeitems[i];
                now.first->removeChild(now.second);
                del.push_back(mpuid[now.second]);
        }
    
        if (db.open()) {
            for(unsigned int i = 0; i < del.size(); i++){
                QSqlQuery qry;
                          qry.prepare("delete from tree where uid = :id or parent = :id");
                          qry.bindValue(":id", del.at(i));
                          qry.exec();
            }
    
            db.close();
        }
    


  • It is My Second WAY

    void MainWindow::on_btnRemoveNode_Clicked()
    {
        QTableWidgetItem * item = ui->tablewidget->currentItem();
    
        std::vector<int> del;
        std::vector<QTreeWidgetItem*> delitems;
    
        std::queue<QTreeWidgetItem*> Q;
                                     Q.push(item);
    
        QTreeWidgetItem *last = item;
    
        while (!Q.empty()) {
            QTreeWidgetItem *now = Q.front(); Q.pop(); delitems.clear();
    
            for(int i = 0; i < now->childCount(); i++){
                del.push_back(mpuid[now->child(i)]);
                Q.push(now->child(i));
                delitems.push_back(item->child(i));
            }
    
            for(unsigned int i = 0; i < delitems.size(); i++){
                now->removeChild(delitems[i]);
            }
    
            if(now == last){
                now->parent()->removeChild(now);
                del.push_back(mpuid[now]);
            }
        }
    
        if (db.open()) {
            for(unsigned int i = 0; i < del.size(); i++){
                QSqlQuery qry;
                          qry.prepare("delete from tree where uid = :id or parent = :id");
                          qry.bindValue(":id", del.at(i));
                          qry.exec();
            }
    
            db.close();
        }
    }```


  • Hi @Taz742

    The usual way is to fill a model and let the model fill the view (visualization widget). You may think it's not worth the time as the sql query result is nothing permanent enough. But there is already a class that helps to build the model. You may want to take a look at QSqlQueryModel: http://doc.qt.io/qt-5/qsqlquerymodel.html#details

    -Michael.



  • @m.sue
    Yes I know about QsqlQueryModel.
    But I can not understand how to use this particular case.
    That's why I started with my knowledge for this problem.
    Can you explain how to use this case QsqlQueryModel?



  • You can't use QSqlQueryModel here.

    enum { UidRole = Qt::UserRole + 123 };
    QModelIndex findParent(const QAbstractItemModel* model, const QVariant& val, int colIdx = 0, const QModelIndex& parent = QModelIndex())
    {
        Q_ASSERT(model);
        Q_ASSERT(colIdx >= 0 && colIdx < model->columnCount(parent));
        const int rowCnt = model->rowCount(parent);
        for (int i = 0; i < rowCnt; ++i) {
            QModelIndex currIdx = model->index(i, colIdx, parent);
            if (currIdx.data(UidRole) == val)
                return currIdx;
            currIdx = findParent(model, val, colIdx, currIdx);
            if (currIdx.isValid())
                return currIdx;
        }
        return QModelIndex();
    }
    
    void populateModel(QAbstractItemModel* model)
    {
        Q_ASSERT(model);
        model->removeRows(0, model->rowCount());
        model->removeColumns(0, model->columnCount());
        QSqlQuery qry;
        qry.prepare(QStringLiteral("select * from tree"));
        if (qry.exec()) {
            while (qry.next()) {
                const QSqlRecord recrd = qry.record();
                const QModelIndex parentIdx = findParent(model, recrd.value(QStringLiteral("Parent")));
                const int rowCnt = model->rowCount(parentIdx);
                model->insertRow(rowCnt, parentIdx);
                if (model->columnCount(parentIdx) <= 0) model->insertColumn(0, parentIdx);
                const QModelIndex currIdx = model->index(rowCnt, 0, parentIdx);
                model->setData(currIdx, recrd.value(QStringLiteral("Name")));
                model->setData(currIdx, recrd.value(QStringLiteral("UID")), UidRole);
            }
        }
    }
    

    This assumes that in the query the parent will never come after the child in order. As a model you can use anything that supports trees, QStandardItemModel is more than fine but even passing treeWidget->model() should work

    The code is untested but should work


    To update the database with the changes you made just connect to the relevant signals of the model and split adding the row/updating the data functionality from the DB update itself



  • @VRonin
    Thank VRonin for answer.
    I only had one question. My algorithm is bad?



  • @Taz742 said in What Is a Best WAY popuplate QTreeWidget & Database?:

    My algorithm is bad?

    Honestly I hate the QStandardItem interface so much I really can't judge an algorithm using it with a clear mind. the only "design flaw" is that you are doing too many things in the same function but, once again, it's not a break of functionality



  • @VRonin
    Thank!
    I will choose your algorithm and I think I will make a good decision.


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.