3

I am designing a major interface but had a problem on how to add a QTableWidget into a QTreeWidget but as a QTreeWidgetItem. In order to save everyone's time and provide the best information, below an example that replicate exactly the problem I have. I tried to be as concise as possible:

  1. Below the small user interface that I have so far which is incorrect: cor
  1. Below the interface I am trying to achieve: ok

As you can notice I am trying to insert an additional widget in the tree. See below the final look of the interface: final

It was really hard to prepare the example and really replicate the problem on a small scale, so please if there are any doubts, let me know:

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QSettings>
#include <QTreeWidget>
#include <QComboBox>
#include <QGraphicsView>
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QWidget>
#include <QGraphicsView>
#include <QGraphicsScene>
#include <QObject>

#include "gaussianfilter.h"

class ImgPreProc;
class PreProcessing;
class MyMainWindow;

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class ImgPreProc : public QObject {
    Q_OBJECT
public:
    explicit ImgPreProc(QSettings *Settings = new QSettings("default", QSettings::IniFormat), QMainWindow *UserParent = nullptr);
    MyMainWindow *MainWindow;
    PreProcessing *PreProc;

public slots:
    void OpenWindow();
private:
    QWidget *parent;
    bool mainWinOpen;
    bool loadProp;
};

class MyMainWindow : public QMainWindow {
    Q_OBJECT
public:
    explicit MyMainWindow(QWidget *parent = nullptr);
    ~MyMainWindow();
};

class MainWindow : public QMainWindow {
    Q_OBJECT
public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private slots:
    void OpenPreProcessingWindow(void) {
        mImgPreProc->OpenWindow();
    }

private:
    Ui::MainWindow *ui;
    QSettings *Settings;
    ImgPreProc *mImgPreProc;
};

class PreProcessing : public QObject {
    Q_OBJECT
public:
    PreProcessing(QSettings *Settings = new QSettings(QString("default"), QSettings::IniFormat));
    ~PreProcessing(void);
    void CreateWidgets(QWidget *UserParent = nullptr);
    QWidget *MainWidget;
    Filter *filter;

private slots:
    void addFilter();
    int addFilterItem(std::string FilterType = "?");

private:
    QWidget *parent;
    QTreeWidget *mainTree;
    QComboBox *filterChoose;
    QTreeWidget* getTree(void){
        return mainTree;
    }
};

#endif // MAINWINDOW_H

mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "gaussianfilter.h"
#include <QTabWidget>
#include <QStatusBar>
#include <QLabel>
#include <QString>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    mImgPreProc = new ImgPreProc(Settings);
    QAction *showPreProc = new QAction(this);
    showPreProc->setIcon(QIcon(QPixmap(QString((":/images/gauss.png")))));
    ui->mainToolBar->addAction(showPreProc);
    connect(showPreProc,SIGNAL(triggered()),this,SLOT(OpenPreProcessingWindow()));
}

MainWindow::~MainWindow(){delete ui;}

ImgPreProc::ImgPreProc(QSettings *set, QMainWindow *UserParent) {
    mainWinOpen = false;
    loadProp = false;
    parent = UserParent;
    PreProc = new PreProcessing(set);
}

void ImgPreProc::OpenWindow() {
    loadProp = true;
    MainWindow = new MyMainWindow(parent);
    QWidget *centralWidget = new QWidget(MainWindow);
    MainWindow->setCentralWidget(centralWidget);
    QVBoxLayout *MainLayout = new QVBoxLayout();
    centralWidget->setLayout(MainLayout);

    QStatusBar *statusbar = new QStatusBar(MainWindow);
    MainWindow->setStatusBar(statusbar);
    QTabWidget *mainTab = new QTabWidget(MainWindow);
    MainLayout->addWidget(mainTab);
    PreProc->CreateWidgets(MainWindow);
    mainTab->addTab(PreProc->MainWidget, QString("PreProcessing"));
    MainWindow->show();
}

MyMainWindow::MyMainWindow(QWidget *parent){(void) parent;}
MyMainWindow::~MyMainWindow(){}

PreProcessing::PreProcessing(QSettings *Settings) : QObject() {
    mainTree = nullptr;
    filter = new Filter("Filter Node");
    (void) Settings;
}

PreProcessing::~PreProcessing(){}
void PreProcessing::CreateWidgets(QWidget *UserParent)
{
    parent = UserParent;
    MainWidget = new QWidget(parent);
    QVBoxLayout *MainLayout = new QVBoxLayout(MainWidget);
    MainWidget->setLayout(MainLayout);

        QHBoxLayout *filterLayout = new QHBoxLayout();
        MainLayout->addLayout(filterLayout);
        filterLayout->setAlignment(Qt::AlignLeft);

            filterChoose = new QComboBox(MainWidget);
            for(int x = 0; x < (int)filter->NameOfFilter.size(); x++){
                filterChoose->addItem(filter->NameOfFilter.at(x).c_str());
            }
            filterLayout->addWidget(new QLabel("Filter"));
            filterLayout->addWidget(filterChoose);

        QToolBar *toolbar = new QToolBar(MainWidget);
        MainLayout->addWidget(toolbar);
        toolbar->addAction("Insert",this,SLOT(addFilter()));

        mainTree = new QTreeWidget(MainWidget);
        QStringList headerList;
        headerList.append("Type Of Filter");
        headerList.append("Iteration");
        headerList.append("Save Configuration?");

        mainTree->setColumnCount(headerList.size());
        mainTree->setHeaderLabels(headerList);
        MainLayout->addWidget(mainTree);
        filter->setTree(mainTree);
        for(int x = 0; x < (int)filter->ItemFilter.size(); x++){
            filter->ItemFilter.at(x).CreateWidgets(parent);
        }
}

void PreProcessing::addFilter() {
    int index = addFilterItem(filterChoose->currentText().toStdString());
    filter->ItemFilter.at(index).CreateWidgets(parent);
}

int PreProcessing::addFilterItem(std::string FilterType) {
    if(FilterType == filter->NameOfFilter.at(Filter::GAUSSIAN)){
        filter->addFilterGauss();
    }
    return ((int)filter->ItemFilter.size() - 1);
}

gaussianfilter.h

#ifndef GAUSSIANFILTER_H
#define GAUSSIANFILTER_H
#include <QObject>
#include <QTreeWidget>
#include <QTreeWidgetItem>

class FilterItem;
class Filter : public QObject{
    Q_OBJECT

public:
    enum{ GAUSSIAN, OTHER };
    Filter(std::string Name, QTreeWidget *userTree = new QTreeWidget());
    ~Filter();
    std::vector<std::string>NameOfFilter;
    std::vector<FilterItem>ItemFilter;

    void setTree(QTreeWidget *userTree){
        tree = userTree;
    }

    QTreeWidget* getTree(void){
        return tree;
    }

    int getItemCount(void){
        return ((int)ItemFilter.size());
    }

    QTreeWidget *tree;

private:
    std::string filterName;

public slots:
    void addFilterGauss(int width = 3, int height = 3, int iter = 1, bool ok = true);
};

class GaussianFilter : public QObject{
    Q_OBJECT

public:
    GaussianFilter(Filter *UserFilter, int width = 3, int height = 3, int iter = 1, bool ok = true);
    void CreateFilterWidgets(QWidget *parent);
    void CreateAdvancedFilterWidgets(QWidget *parent);

    ~GaussianFilter();

    QWidget *type,*iteration,*execute, *type2;
    void setRectHeight(int x){rectH = x;}
    int getRectHeight(void){return rectH;}
    void setRectWidth(int x){rectW = x;}
    int getRectWidth(void){return rectW;}
    void setIteration(int x){gaussItera = x;}
    int getIteration(void){return gaussItera;}
    void setExecute(bool x){gaussOk = x;}
    bool getExecute(void){return gaussOk;}

private:
    Filter *filter;
    int rectH, rectW, gaussItera;
    bool gaussOk;
    QTreeWidgetItem *otherItem;

private slots:
    void changeRectHeight(int x){setRectHeight(x);}
    void changeIteration(int x){setIteration(x);}
    void changeExecute(bool x){setExecute(x);}
};

class FilterItem{

public:
    explicit FilterItem(Filter *userFilter);
    void CreateWidgets(QWidget *parent);
    void CreateAdvancedWidgets(QWidget *parent);
    GaussianFilter *gaussian;

    int getFilterType(void){return filterType;}
    void determFilterType(int x){ filterType = x;}

    std::string getFilterTypeStr(void){
        if(filterType != (-1)){
            return filter->NameOfFilter.at(filterType);
        }
        else{
            return "\0";
        }
    }

    ~FilterItem();

private:
    Filter *filter;
    QWidget *parent;
    QTreeWidget *tree;
    QTreeWidgetItem *item;
    QTreeWidgetItem *advancedChildItem;
    QList<QTreeWidgetItem *> options;
    int filterType;
    QString iconPath;
    QTreeWidgetItem *childItem;
    QTreeWidgetItem *otherItem;


};
#endif // GAUSSIANFILTER_H

gaussianfilter.cpp

#include "gaussianfilter.h"
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QToolBar>
#include <QLabel>
#include <QSpinBox>
#include <QCheckBox>
#include <QComboBox>
#include <QPainter>
#include <QTableWidget>
#include <QPushButton>

Filter::Filter(std::string Name, QTreeWidget *userTree) {
    filterName = Name;
    ItemFilter.clear();

    for(int x = 0; x < OTHER; x++){
        NameOfFilter.push_back(std::string());
    }
    // Here we will add the filter inside the QComboBox
    NameOfFilter.at(GAUSSIAN) = "Gaussian";
    tree = userTree;
}

Filter::~Filter(){}

void Filter::addFilterGauss(int width, int height, int iter, bool ok) {
    ItemFilter.push_back(FilterItem(this));
    ItemFilter.at(((int)ItemFilter.size() - 1)).gaussian = new GaussianFilter(this,width,height,iter,ok);
    ItemFilter.at(((int)ItemFilter.size() - 1)).determFilterType(GAUSSIAN);
}

GaussianFilter::GaussianFilter(Filter *UserFilter, int width, int height, int iter, bool ok) {
    filter = UserFilter;
    setRectWidth(width);
    setRectHeight(height);
    setIteration(iter);
    setExecute(ok);
}

void GaussianFilter::CreateFilterWidgets(QWidget *parent) {
    type = new QWidget(parent);
    iteration = new QWidget(parent);
    execute = new QWidget(parent);

    QGridLayout *group = new QGridLayout(type);
    type->setLayout(group);
    QSpinBox *width = new QSpinBox(parent);
    width->setSingleStep(2);
    width->setValue(getRectWidth());
//    connect(width,SIGNAL(valueChanged(int)),this,SLOT(changeRectWidth(int)));
    width->setRange(3,33);
    group->addWidget(new QLabel("Search"),0,0,1,1,Qt::AlignLeft);
    group->addWidget(width,0,1,1,1,Qt::AlignLeft);
    group->addWidget(new QLabel("pix"),0,2,1,1,Qt::AlignLeft);
    QHBoxLayout *iter = new QHBoxLayout(iteration);
    iteration->setLayout(iter);

    QSpinBox *It = new QSpinBox(parent);
    iter->addWidget(It);
    iter->addWidget(new QLabel("Time(s)"));
    It->setRange(1,10);
    It->setValue(getIteration());
    connect(It,SIGNAL(valueChanged(int)),this,SLOT(changeIteration(int)));

    QHBoxLayout *executeHBox = new QHBoxLayout(execute);
    execute->setLayout(executeHBox);
    QCheckBox *Execute = new QCheckBox(parent);
    executeHBox->addWidget(Execute);
    Execute->setChecked(getExecute());
    connect(Execute,SIGNAL(clicked(bool)),this,SLOT(changeExecute(bool)));
}

void GaussianFilter::CreateAdvancedFilterWidgets(QWidget *parent) {
    // This is the part that is giving me doubts
}

GaussianFilter::~GaussianFilter(){}

FilterItem::FilterItem(Filter *userFilter)
{
    filter = userFilter;
}

void FilterItem::CreateWidgets(QWidget *parent)
{
    item = new QTreeWidgetItem();
    QPushButton *childButton = new QPushButton("Child Button 0");
    QTreeWidgetItem *childItemButton = new QTreeWidgetItem();
    if(filterType == Filter::GAUSSIAN){
        item->setText(0,QString("Gaussian"));
        filter->tree->addTopLevelItem(item);

        childItem = new QTreeWidgetItem();
        item->addChild(childItem);
        gaussian->CreateFilterWidgets(parent);
        filter->tree->setItemWidget(childItem,0,gaussian->type);
        filter->tree->setItemWidget(childItem,1,gaussian->iteration);
        filter->tree->setItemWidget(childItem,2,gaussian->execute);
    }
}

void FilterItem::CreateAdvancedWidgets(QWidget *parent)
{
    otherItem->setText(0,QString("Advanced Gaussian"));
    filter->tree->addTopLevelItem(otherItem);

    QTreeWidgetItem *childItem1 = new QTreeWidgetItem();
    QTableWidget *tw = new QTableWidget();
    tw->setColumnCount(3);
    tw->setRowCount(3);
}

FilterItem::~FilterItem()
{

}

finally the main.cpp

#include "mainwindow.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();
    return a.exec();
}

In case you need to see the components of the ui see below, it is very minimal:

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>MainWindow</class>
 <widget class="QMainWindow" name="MainWindow">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>800</width>
    <height>600</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>MainWindow</string>
  </property>
  <widget class="QWidget" name="centralwidget">
   <layout class="QGridLayout" name="gridLayout">
    <item row="0" column="0">
     <layout class="QHBoxLayout" name="HLayout"/>
    </item>
   </layout>
  </widget>
  <widget class="QMenuBar" name="menubar">
   <property name="geometry">
    <rect>
     <x>0</x>
     <y>0</y>
     <width>800</width>
     <height>22</height>
    </rect>
   </property>
  </widget>
  <widget class="QStatusBar" name="statusbar"/>
  <widget class="QToolBar" name="mainToolBar">
   <property name="windowTitle">
    <string>toolBar</string>
   </property>
   <attribute name="toolBarArea">
    <enum>TopToolBarArea</enum>
   </attribute>
   <attribute name="toolBarBreak">
    <bool>false</bool>
   </attribute>
  </widget>
 </widget>
 <resources/>
 <connections/>
</ui>

I tried to solve the problem myself and consulted first of all the QTreeWidgetItem class to make sure I didn't miss any specifications. In addition I also consulted the QTreeWidget class too to make sure I was correctly using the setItemWidget correctly. It seems to me that it is. However, as soon as I implement the additional function CreateAdvancedWidgets(QWidget *parent) things just stopped working as I planned.

So did more research and came across this useful source. I replicated the example to make sure I wasn't missing anything and applied it to my case but it didn't work. I also went through this, this source and lastly this source too.

Thanks to anybody who would be willing to use a bit of their time to go through it and suggest a potential solution to this problem.

EsoMars
  • 337
  • 3
  • 14
  • Does this link help?https://forum.qt.io/topic/18603/qtablewidget-in-a-qtreewidget He says you can give ```treeWidget->setItemWidget``` a try – kenash0625 Nov 05 '21 at 02:24

1 Answers1

1

Whenever I use Qt and consider using a QTableWidget, I always consider other options first because they are NOT easy to work with. Here's what I'd consider at this point:

  • is there going to be a case where multiple rows are needed?
  • is your layout going to need dynamic properties, like having resizable columns by the user or the ability to sort?

If the answer to these questions is no, then perhaps consider using a QGridLayout and a QSpacerItem or two.

However, if you have to have some of those things (or maybe they're planned for the future), look at QAbstractItemModel and some examples that go along with it (eg: https://doc.qt.io/qt-5/qtwidgets-itemviews-simpletreemodel-example.html)

Dharman
  • 30,962
  • 25
  • 85
  • 135
Catcow
  • 7
  • 2
  • I appreciate you taking the time to read the question. To answer to your questions: Yes, there will be multiple rows because everytime the user changes parameters, I would like those search params be stored in the history. There could be parameters that gave a better result instead of others. There fore, to answer to your second question, yes, the Table needs dynamic properties. for example every time a new search is conducted and the user select to save the params, an additional row shall be created automatically. – EsoMars Nov 04 '21 at 16:34
  • I don't have enough experience with `QAbstractItemModel` concept but I am looking into that now. I was hoping to have some sort of coding solution if possible. I know people are busy and that is why I tried to put together a small working example. If there is anyway you guys could help me move forward with that I would appreciate it. If you can't I understand that too. In any case thanks everyone for taking the time to read the question! – EsoMars Nov 04 '21 at 16:38
  • For widget examples, you can always go to your installation of Qt and go to the Examples folder, eg `Qt5.15.2/Examples/Qt-5.15.2/widgets`. Then `grep -Rni QAbstractItemModel` will list the files that use QAbstractItemModel objects – Catcow Nov 04 '21 at 16:59
  • @EsoMars _Yes, there will be multiple rows because everytime the user changes parameters, I would like those search params be stored in the history._ Do not use the frontend as backend. – scopchanov Nov 06 '21 at 20:30
  • Thanks @scopchanov for reading the question and taking the time. It took me a while to study and organize the example code to exactly replicate the problem. Is there any chance you could please help me out with the code? I know you are busy and I understand if you can't. – EsoMars Nov 07 '21 at 22:07
  • I have been following the advise of a user in this loop and studying `QAbstractItemModel` objects. I don't have enough experience with that tough. I am trying to understand it. – EsoMars Nov 07 '21 at 22:10
  • @EsoMars, it would be a better idea to reconsider the way you use the tree and table widgets. – scopchanov Nov 08 '21 at 00:29
  • @scopchanov, ok, what do you mean exactly though? – EsoMars Nov 08 '21 at 01:10
  • @EsoMars, I mean, that you first need to learn the intended way of using these classes and then design your solution. Doing it the other way round causes a lot of [XY problems](https://en.wikipedia.org/wiki/XY_problem). To be more exact and provide you with a suitable guidance I first need to know what your real task is. I am afraid though, that this would go far beyond the intended purpose of the comments. – scopchanov Nov 08 '21 at 10:24