How to add items to a QTreeView? - c++

What's wrong with the code below? It's creating the QTreeView, however, the view doesn't display anything, just a thin border.
I was also trying to find a way to add a widget into a specific row. I tried with a QPushButton.
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
ui.setupUi(this);
QGridLayout* layout = new QGridLayout();
ui.centralWidget->setLayout(layout);
QTreeView* treeView = new QTreeView(this);
treeView->setStyleSheet(R"(
QTreeView {
background-color: transparent;
}
)");
// Create a model to hold the data
QStandardItemModel model;
// Set the model to the tree view
treeView->setModel(&model);
// Add columns to the model
QStandardItem* column1 = new QStandardItem("Column 1");
column1->setBackground(QBrush(Qt::red));
model.setHorizontalHeaderItem(0, column1);
model.setHorizontalHeaderItem(1, new QStandardItem("Column 2"));
// Add data to the model
for (int i = 0; i < 5; i++) {
// Create a new row
QList<QStandardItem*> row;
// Create items for the row
QStandardItem* item1 = new QStandardItem("Data " + QString::number(i));
QStandardItem* item2 = new QStandardItem("More data " + QString::number(i));
// Add the items to the row
row << item1 << item2;
// Create a push button
QPushButton* button = new QPushButton("Button " + QString::number(i));
// Add the button to the first column
item1->setData(QVariant::fromValue(static_cast<void*>(button)));
// Add the row to the model
model.appendRow(row);
}
layout->addWidget(treeView);
treeView->show();
return;
}

The problem is your model destroyed after MainWindow ctor's scope finishes.
// Create a model to hold the data
QStandardItemModel model;
You can define this model as member variable (recommended) or create on heap like this auto model = new QStandardItemModel;.

Related

How to add items in 2nd column of QTreeView

Code explain:
First I create the items, next I define the 1st column structure and finally I try to define the 2nd column structure.
For the first column structure I use appendRow() method to QStandardItems.
For the second column structure I use setItem() method to the QStandardItemModel.
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
// QStandardItemModel
treeModel = new QStandardItemModel(this);
// Create Items
QStandardItem *item_0 = new QStandardItem("Item 0");
QStandardItem *item_0_0 = new QStandardItem("Item 0_0");
QStandardItem *item_1 = new QStandardItem("Item 1");
QStandardItem *item_1_0 = new QStandardItem("Item 1_0");
QStandardItem *item_1_0_0 = new QStandardItem("Item 1_0_0");
QStandardItem *item_2 = new QStandardItem("Item 2");
QStandardItem *item_3 = new QStandardItem("Item 3");
// Root Item
QStandardItem * rootItem = treeModel->invisibleRootItem();
//Define the tree structure
rootItem->appendRow(item_0);
rootItem->appendRow(item_1);
item_0->appendRow(item_0_0);
item_1->appendRow(item_1_0);
item_1_0->appendRow(item_1_0_0);
//Define 2nd column structure
treeModel->setItem(0,1,item_2);
treeModel->setItem(1,1,item_3);
// QTreeView
ui->treeView->setModel(treeModel);
}
This code result in the image below. But what I want is to have Item3 just below Item2.
There are many ways to achieve your desired behavior, but the following is maybe the one being most straightforward. I suggest, that you study carefully the Qt docs. A good idea is to take the QTreeView instead of the QTreeWidget, which is kind of less flexible.
It might take some time to fully grasp the MVC concept in Qt, but it is worth the effort. Here goes my solution.
#include <QApplication>
#include <QStandardItemModel>
#include <QTreeView>
int main(int argc, char** args) {
QApplication app(argc, args);
auto model=new QStandardItemModel;
// Create Items
QStandardItem *item_0 = new QStandardItem("Item 0");
QStandardItem *item_0_0 = new QStandardItem("Item 0_0");
QStandardItem *item_1 = new QStandardItem("Item 1");
QStandardItem *item_1_0 = new QStandardItem("Item 1_0");
QStandardItem *item_1_0_0 = new QStandardItem("Item 1_0_0");
QStandardItem *item_2 = new QStandardItem("Item 2");
QStandardItem *item_3 = new QStandardItem("Item 3");
// Root Item
QStandardItem * rootItem = model->invisibleRootItem();
//Define the tree structure
rootItem->appendRow(item_0);
rootItem->appendRow(item_1);
item_0->appendRow(QList<QStandardItem*>{item_0_0,item_2});
item_1->appendRow(item_1_0);
item_1_0->appendRow(item_1_0_0);
model->setItem(1,1,item_3);
auto view=new QTreeView;
view->setModel(model);
view->show();
app.exec();
}

Using QTreeWidgetItems setData to store a QStackedWidget or QVariant

I am trying to make a QTreeWidget such that each row contains a series of comboboxes. Depending on how the user interacts with the comboboxes I would like certain comboboxes to becomes line edits and some to become buttons.
It was suggested here that a QStackedWidget would serve my needs and its done a pretty good job except now I need a way to alter the QStackedWidget that is next to the one that contains the combobox sending me an indexChanged signal. (Basically I want to change the neighboring QStackWidgets index)
I thought that I would be able to simply store the QStackWidget in the childItem using setData and then retrieve it inside the indexChanged slot but for some reason it appears the QStackWidget is not set to the childItems data.
Any help is appreciated.
This is where I orginally setup my QTreeWidget and its Items
QTreeWidgetItem *childItem = new QTreeWidgetItem(itemParent);
QVariant itemParentVariant,widgetParentVarient;
widgetParentVarient.setValue(widgetParent);
itemParentVariant.setValue(itemParent);
QList<QVariant> stackWidgetList;
uint cycleSetup;
for(cycleSetup = 0;cycleSetup < methodBlocks.at(rowType).size()+2;cycleSetup++)
{
QComboBox *itemComboBox = new QComboBox;
itemComboBox->setProperty("rowType", rowType);
itemComboBox->setProperty("row", 0);
itemComboBox->setProperty("column",cycleSetup);
itemComboBox->setProperty("widgetParent",widgetParentVarient);
itemComboBox->setProperty("itemParent",itemParentVariant);
itemComboBox->addItems(methodBlocks.at(0).at(cycleSetup));
QObject::connect(itemComboBox, SIGNAL(currentIndexChanged(const QString&)), this, SLOT(OnComboIndexChanged(const QString&)));
QLineEdit *itemLineEdit = new QLineEdit;
QPushButton *itemButton = new QPushButton;
itemButton->setText("Reset");
QComboBox *timeComboBox = new QComboBox;
timeComboBox->setProperty("rowType", rowType);
timeComboBox->setProperty("row", 0);
timeComboBox->setProperty("column",cycleSetup);
timeComboBox->setProperty("widgetParent",widgetParentVarient);
timeComboBox->setProperty("itemParent",itemParentVariant);
timeComboBox->addItems(QString("Seconds;MilliSeconds;Reset").split(";"));
QStackedWidget *masterItemWidget = new QStackedWidget;
masterItemWidget->addWidget(itemLineEdit);
masterItemWidget->addWidget(itemComboBox);
masterItemWidget->addWidget(itemButton);
masterItemWidget->addWidget(timeComboBox);
masterItemWidget->setCurrentIndex(1);
QVariant stackParent;
stackParent.setValue(masterItemWidget);
itemComboBox->setProperty("stackParent",stackParent);
childItem->setData(0,Qt::UserRole,stackParent);
stackWidgetList.push_back(stackParent);
widgetParent->setItemWidget(childItem,cycleSetup,masterItemWidget);
itemParent->addChild(childItem);
}
And this is inside the slot where I am trying to retrieve the data (The QStackWidget)
QStackedWidget *itemMaster = combo->property("stackParent").value<QStackedWidget*>(); //this works
itemMaster->setCurrentIndex(0);
QTreeWidget *widgetParent = combo->property("widgetParent").value<QTreeWidget*>();
QTreeWidgetItem *parentItem = combo->property("itemParent").value<QTreeWidgetItem*>();
QTreeWidgetItem *childItem = new QTreeWidgetItem(parentItem);
QList<QVariant> stackList = childItem->data(0,Qt::UserRole).value<QList<QVariant>>(); //this doesnt
QStackedWidget *itemsibMaster = childItem->data(0,Qt::UserRole).value<QStackedWidget*>(); //neither does this
itemsibMaster->setCurrentIndex(2);
EDIT:
I've tried to set the data like this
QFrame *stackFrame = new QFrame;
QStackedWidget *masterItemWidget = new QStackedWidget(stackFrame);
masterItemWidget->addWidget(itemLineEdit);
masterItemWidget->addWidget(itemComboBox);
masterItemWidget->addWidget(itemButton);
masterItemWidget->addWidget(timeComboBox);
masterItemWidget->setCurrentIndex(1);
QVariant stackParent;
stackParent.setValue(masterItemWidget);
itemComboBox->setProperty("stackParent",stackParent);
QVariant frameVariant;
frameVariant.setValue(stackFrame);
childItem->setData(0,Qt::UserRole,frameVariant);
stackWidgetList.push_back(stackParent);
widgetParent->setItemWidget(childItem,cycleSetup,masterItemWidget);
itemParent->addChild(childItem);
And retrieve it like this
QStackedWidget *itemMaster = combo->property("stackParent").value<QStackedWidget*>();
itemMaster->setCurrentIndex(0);
QTreeWidget *widgetParent = combo->property("widgetParent").value<QTreeWidget*>();
QTreeWidgetItem *parentItem = combo->property("itemParent").value<QTreeWidgetItem*>();
QTreeWidgetItem *childItem = new QTreeWidgetItem(parentItem);
QFrame *frameObject = childItem->data(0,Qt::UserRole).value<QFrame*>();
//QList<QVariant> stackList = childItem->data(0,Qt::UserRole).value<QList<QVariant>>();
QStackedWidget *itemFrameMaster = frameObject->findChild<QStackedWidget*>();
if(itemFrameMaster)
{
qDebug() << "itemFrame Exists";
}
else
{
qDebug() << "itemFrame is NULL";//It goes to here
}
So I'm still unable to get the desired functionality.
Check if it works.
As stacked widget QStackedWidget derived from QFrame, Create a Frame object and add your stacked widget to it. Set the frame to your child item.
QFrame *stackFrame = new QFrame(Parent);
QStackedWidget *masterItemWidget = new QStackedWidget(stackFrame);
While querying first query for the frame and get the stacked widget child from it.
QStackedWidget *stackedWidget = FrameObject->findChild<QStackedWidget *>();
Alright so I managed to get the functionality that I was after, so in case anyone else is trying to do this here's the fix.
Essentially everything I had done before was correct except for one error.
When retrieving the data stored in the comboboxes child I had originally gotten that child here.
QTreeWidgetItem *parentItem = combo->property("itemParent").value<QTreeWidgetItem*>();
QTreeWidgetItem *childItem = new QTreeWidgetItem(parentItem);
The issue was that since the parentItem had more than one child (I suppose in this case the minimum is two children) I was not referencing the correct child. This issue was solved by creating a custom combobox property which held the child rather than the parent (parent can be derived from the parent more accurately than visa versa).
Now where I populate the QTreeWidget with combobox items looks like this.
QTreeWidgetItem *childItem = new QTreeWidgetItem(itemParent);
QVariant childItemVariant,widgetParentVarient;
widgetParentVarient.setValue(widgetParent);
childItemVariant.setValue(childItem);
uint cycleSetup;
for(cycleSetup = 0;cycleSetup < methodBlocks.at(rowType).size();cycleSetup++)
{
if(cycleSetup < methodBlocks.at(rowType).size())
{
QComboBox *itemComboBox = new QComboBox;
itemComboBox->setProperty("rowType", rowType);
itemComboBox->setProperty("row", 0);
itemComboBox->setProperty("column",cycleSetup);
itemComboBox->setProperty("widgetParent",widgetParentVarient);
itemComboBox->setProperty("childItem",childItemVariant);
itemComboBox->addItems(methodBlocks.at(0).at(cycleSetup));
QObject::connect(itemComboBox, SIGNAL(currentIndexChanged(const QString&)), this, SLOT(OnComboIndexChanged(const QString&)));
QLineEdit *itemLineEdit = new QLineEdit;
QFrame *stackFrame = new QFrame;
QStackedWidget *masterItemWidget = new QStackedWidget(stackFrame);
masterItemWidget->addWidget(itemLineEdit);
masterItemWidget->addWidget(itemComboBox);
//masterItemWidget->addWidget(timeComboBox);
masterItemWidget->setCurrentIndex(1);
QVariant stackParent;
stackParent.setValue(masterItemWidget);
itemComboBox->setProperty("stackParent",stackParent);
itemComboBox->setProperty("cycleSetupIT",cycleSetup);
QVariant frameVariant;
frameVariant.setValue(stackFrame);
childItem->setData(cycleSetup,Qt::UserRole,stackParent);
widgetParent->setItemWidget(childItem,cycleSetup,masterItemWidget);
itemParent->addChild(childItem);
}
}
}
And the code in the slot where I get the data from the childItem (which holds the combobox) looks like this.
QTreeWidget *widgetParent = combo->property("widgetParent").value<QTreeWidget*>();
widgetParent->setColumnCount(6);
QTreeWidgetItem *childItem = combo->property("childItem").value<QTreeWidgetItem*>();
QTreeWidgetItem *parentItem = childItem->parent();
int rowType = combo->property("rowType").toInt();
int cycleIT = combo->property("cycleSetupIT").toInt();
int offset = 0;
QStackedWidget *itemFrameMaster = childItem->data(cycleIT+1,Qt::UserRole).value<QStackedWidget*>();
itemFrameMaster->setCurrentIndex(0);
Hope this helps. Also if anyone knows how to get the number of data items in a QStandardItem::data() that'd be great albeit not super critical.
Cheers!

How to show the row where QPushButton is clicked in QTableWidget

I would like to delete row where QPushButton is clicked how it is possible to I think it is reasonable to use slots but how to do it don't know , if you have any ideas how to get a row of selected button please share, thanks.
It is my table
It is a code where i add rows to my QTableWidget
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
for(int i = 0; i<20;i++)
ui->tableWidget->insertRow(ui->tableWidget->rowCount());
QVector<QString>vec;
vec<<"first"<<"sec"<<"third"<<"for"<<"fif"<<"first"<<"sec"
<<"third"<<"for"<<"fif";
vec<<"first"<<"sec"<<"third"<<"for"<<"fif"<<"first"<<"sec"
<<"third"<<"for"<<"fif";
for(int i = 0; i<ui->tableWidget->rowCount();i++)
{
for(int j = 0; j<ui->tableWidget->columnCount();j++)
{
if(j == 0)
{
QWidget* pWidget = new QWidget();
QPushButton* btn_edit = new QPushButton();
btn_edit->setText("Remove");
QHBoxLayout* pLayout = new QHBoxLayout(pWidget);
pLayout->addWidget(btn_edit);
pLayout->setAlignment(Qt::AlignCenter);
pLayout->setContentsMargins(0, 0, 0, 0);
pWidget->setLayout(pLayout);
ui->tableWidget->setCellWidget(i, j, pWidget);
continue;
}
QTableWidgetItem*item = new QTableWidgetItem(vec[i]);
item->setFlags(item->flags() ^ Qt::ItemIsEditable);
ui->tableWidget->setItem(i, j, item);
}
}
}
This task can be solved using the removeRow() method but before we must get the row. First of all we will connect all the buttons to a slot inside the loop as shown below:
*.h
private slots:
void onClicked();
*.cpp
[...]
QPushButton* btn_edit = new QPushButton();
btn_edit->setText("Remove");
connect(btn_edit, &QPushButton::clicked, this, &MainWindow::onClicked);
[...]
In the slot we can get the button that emits the signal through the sender() method, then we get QWidget parent (created with the name pWidget), this is the widget that is added to the QTableWidget and its position is relative to it, then we use the method indexAt() to get the QModelIndex associated with the cell, and this has the information of the row through the method row(). All of the above is implemented in the following lines:
void MainWindow::onClicked()
{
QWidget *w = qobject_cast<QWidget *>(sender()->parent());
if(w){
int row = ui->tableWidget->indexAt(w->pos()).row();
ui->tableWidget->removeRow(row);
ui->tableWidget->setCurrentCell(0, 0);
}
}
Note: The setCurrentCell() method is used to set the focus since the last cell that has it has been deleted.
The complete example can be found in the following link.
When you are creating QPushButton just add :
btn_delete = new QPushButton("Remove", ui->tableWidget);
btn_delete->setObjectName(QString("%1").arg(ui->tableWidget->rowCount()));
connect(btn_delete, SIGNAL(clicked()), this, SLOT(CellButtonDeleteClicked()));
And create function CellButtonDeleteClicked()
void CellButtonDeleteClicked()
{
// by this line I can get the sender of signal
QPushButton *pb = qobject_cast<QPushButton *>(QObject::sender());
int row = pb->objectName().toInt();
ui->tableWidget->removeRow(row);
}

Adjust widget's position and width in qtreewidget

I created a Qtreewidget which has a Qtreewidgetitem is a widget (combobox, edit box and so on).
For example, this is one part of my code.cpp:
m_pPropertyTree = new QTreeWidget();
m_pPropertyTree->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
m_pPropertyTree->setColumnCount(2);
m_pPropertyTree->setColumnWidth(0, 155);
m_pPropertyTree->setStyleSheet("QTreeView::item { height: 20px;}");
m_pPropertyTree->setHeaderLabels(QStringList() << "Property" << "Value");
...
QTreeWidgetItem *pButtonItem = new QTreeWidgetItem(m_pPropertyTree);
pButtonItem->setText(0, "Button");
//caption
QTreeWidgetItem *pCaptionItem = new QTreeWidgetItem();
pCaptionItem->setText(0, "caption");
pCaptionItem->setText(1, "Button");
pButtonItem->addChild(pCaptionItem);
//style
QTreeWidgetItem *pStyleItem = new QTreeWidgetItem();
pStyleItem->setText(0, "style");
QComboBox *pCombobox = new QComboBox();
pCombobox->setFixedHeight(20);
pCombobox->addItem("normal");
pCombobox->addItem("bold");
pButtonItem->addChild(pStyleItem);
m_pPropertyTree->setItemWidget(pStyleItem, 1, pCombobox);
And this is what i get:
The combobox has original size which is the red rectangle. I want to adjust it's size or position to the new one, which is the black rectangle size. How can i do that? Thanks.

How can I add a checkbox/radio button to QTableWidget

How can I add a checkbox/radiobutton/combobox to a QTableWidget or a QListWidget?
There are two methods:
void QTableWidget::setCellWidget(int row, int column, QWidget* widget)
and
void QListWidget::setItemWidget(QListWidgetItem* item, QWidget* widget)
They allow to insert any widget and other controls that inherit QWidget. Checkbox/radio button/combobox do inherit from QWidget.
For a checkbox using the item's setCheckState method should do what you need both for list and table widgets. See if code below would work for you:
List widget:
QListWidgetItem *item0 = new QListWidgetItem(tr("First"), listWidget);
QListWidgetItem *item1 = new QListWidgetItem(tr("Second"), listWidget);
item0->setCheckState(Qt::Unchecked);
item1->setCheckState(Qt::Checked);
Table widget:
QTableWidgetItem *item2 = new QTableWidgetItem("Item2");
item2->setCheckState(Qt::Checked);
tableWidget->setItem(0, 0, item2);
You can use delegates (QItemDelegate) for other types of editor's widgets, example is here: Spin Box Delegate Example.
I hope this helps.
you can add checkbox like this too
#include <QCheckBox>
void addCheckBoxAt(int row_number, int column_number,int state)
{
// Create a widget that will contain a checkbox
QWidget *checkBoxWidget = new QWidget();
QCheckBox *checkBox = new QCheckBox(); // We declare and initialize the checkbox
QHBoxLayout *layoutCheckBox = new QHBoxLayout(checkBoxWidget); // create a layer with reference to the widget
layoutCheckBox->addWidget(checkBox); // Set the checkbox in the layer
layoutCheckBox->setAlignment(Qt::AlignCenter); // Center the checkbox
layoutCheckBox->setContentsMargins(0,0,0,0); // Set the zero padding
/* Check on the status of odd if an odd device,
* exhibiting state of the checkbox in the Checked, Unchecked otherwise
* */
if(state == 1){
checkBox->setChecked(true);
} else {
checkBox->setChecked(false);
}
ui->job_table_view->setCellWidget(row_number,column_number, checkBoxWidget);
// Another way to add check box as item
/*
// QTableWidgetItem *checkBoxItem = new QTableWidgetItem("checkbox string ");
QTableWidgetItem *checkBoxItem = new QTableWidgetItem();
checkBoxItem->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled);
checkBoxItem->setCheckState(Qt::Checked);
ui->job_table_view->setItem(row_number,column_number,checkBoxItem);
*/
}
// call it like
addCheckBoxAt(0,0,1); // insert checkbox it 0,0 and check status true