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:
- Below the small user interface that I have so far which is incorrect:

- Below the interface I am trying to achieve:

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

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.
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:
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)