How to add items in 2nd column of QTreeView - c++

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();
}

Related

How to add items to a QTreeView?

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;.

How to recover item from a QPushButton?

I made a code in c ++ with the Qt framework.
The goal is to add lines that contain a QLabel "url", a QLineEdit "name" and QPushButton "remove".
I add the line with a QPushButton named "Add".
The part to add the line works.
The code is the following :
name=new QLineEdit("",list);
url=new QLabel("",list);
removeLine=new QPushButton("remove",list);
//list is a QListWidget
hbox=new QHBoxLayout;
hbox->addWidget(name);
hbox->addWidget(url);
hbox->addWidget(removeLine);
widget = new QWidget;
widget->setLayout(hbox);
item=new QListWidgetItem;
item->setSizeHint(QSize(0,50));
list->addItem(item);
list->setItemWidget(item,widget);
When I press the remove button, I would like the corresponding line to be deleted.
The problem is that I can not find the item of each remove button.
I tried a method with QSignalMapper but it does not work.
Here is the code:
i = list->currentRow();
signalMappper=new QSignalMapper(this);
connect(signalMappper,SIGNAL(mapped(int)),this,SLOT(removeLineEditLabelAndButton(int)));
connect(removeLine,SIGNAL(clicked()),signalMappper,SLOT(map()));
signalMappper->setMapping(removeLine,i);
//SLOT
void PanoramaWidget::removeLineEditLabelAndButton(int row){
item= list->takeItem(row);
list->removeItemWidget(item);
delete item;
}
How would you solve this problem?
Can use lambda function to connect clicked signal of QPushButton
list = new QListWidget(this);
QLineEdit * name = new QLineEdit("", list);
QLabel * url = new QLabel("", list);
QPushButton * removeLine = new QPushButton("remove", list);
//list is a QListWidget
QHBoxLayout * hbox = new QHBoxLayout;
hbox->addWidget(name);
hbox->addWidget(url);
hbox->addWidget(removeLine);
auto widget = new QWidget;
widget->setLayout(hbox);
auto item = new QListWidgetItem;
item->setSizeHint(QSize(0, 50));
list->addItem(item);
list->setItemWidget(item, widget);
connect(removeLine, &QPushButton::clicked,
[this, item]() {list->takeItem(list->row(item)); });

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!

QT dynamically generate label, LineEdit, Button

I want to do serial communication in the QT inside, according to the number of serial ports to dynamically generate label, LineEdit, Button, and these three buttons can pull down the scroll bar when the size of the interface, how to do well, I write this below is dead of.
The effect of encapsulation into a method
The interface was washed last
Define QGridLayout insude QScrollArea. And add your new widgets into
that layout in code. QGridLayout::addWidget
Define table where you will show several widgets in table cells. That real complicated way.
void BaseUi::BaseScrollArea()
{
QScrollArea *pArea = new QScrollArea();
QWidget *pWidget = new QWidget();
pWidget->setStyleSheet("QWidget" "{background:white;}");
m_vbox_layout = new QVBoxLayout();
m_vbox_layout->addSpacerItem(new QSpacerItem(100, 30,
QSizePolicy::Expanding, QSizePolicy::Expanding));
pWidget->setLayout(m_vbox_layout);
pArea->setWidget(pWidget);
pArea->setWidgetResizable(true);
m_main_layout = new QVBoxLayout();
m_main_layout->addWidget(pArea);
}
void BaseUi::addAutoRecordUi(QString lab_neme, QString ledit_name)
{
QWidget *page = new QWidget;
QGridLayout *layout = new QGridLayout(page);
QLabel *label = new QLabel;
label->setText(lab_neme);
label->setFont(font());
QLineEdit *ledit = new QLineEdit;
ledit->setText(ledit_name);
ledit->setFont(font());
layout->addWidget(label, 0, 1);
layout->addWidget(ledit, 0, 2);
page->setLayout(layout);
m_vbox_layout->insertWidget(m_vbox_layout->count()-1, page);
}
void BaseUi::addMulRecordUi(QString lab_neme, QString ledit_name, QString
but_name)
{
QWidget *page = new QWidget;
QGridLayout *layout = new QGridLayout(page);
QLabel *label = new QLabel;
label->setText(lab_neme);
label->setFont(font());
QLineEdit *ledit = new QLineEdit;
ledit->setText(ledit_name);
ledit->setFont(font());
QPushButton *but = new QPushButton(but_name);
but->setFont(font());
layout->addWidget(label, 0, 1);
layout->addWidget(ledit, 0, 2);
layout->addWidget(but, 0, 3);
page->setLayout(layout);
m_vbox_layout->insertWidget(m_vbox_layout->count()-1, page);
}

Qt. QStackedLayout does not work

I had a console-game that was written on c++ and since I am currently trying to learn Qt I decided to add GUI to this program as an exercise.
So there's main window named "gui" that inherits Qwidget. It has layout QHBoxLayout* main_h_lo. Which has 2 added layouts: 1. QStackedLayout* leftpart, 2. QGridLayout* deck. The first is some sort of menu-part. It has 4 different widgets with their layouts. For example choosing game mode or printing game score. And second layout - deck - is game table, similar to chessboard.
There's constructor code which I suppose contains the problem:
gui::gui(QWidget *parent) :
QWidget(parent), pgame(nullptr)
{
QHBoxLayout* main_h_lo = new QHBoxLayout;
main_h_lo->setMargin(0);
main_h_lo->setSpacing(0);
setLayout(main_h_lo);
//leftpart-widgets initialization:
bot_or_playerW = new QWidget;
QVBoxLayout* bot_or_playerL = new QVBoxLayout;
bot_or_playerL->addWidget(new QLabel("Choose game mode"));
QPushButton* qpb1 = new QPushButton("vs Human");
QPushButton* qpb2 = new QPushButton("vs Bot");
QObject::connect(qpb1, SIGNAL(clicked()), SLOT(pvp()));
QObject::connect(qpb2, SIGNAL(clicked()), SLOT(pvb()));
bot_or_playerL->addWidget(qpb1);
bot_or_playerL->addWidget(qpb2);
bot_or_playerW->setLayout(bot_or_playerL);
choosing_colourW = new QWidget;
QVBoxLayout* choosing_colourL = new QVBoxLayout;
choosing_colourL->addWidget(new QLabel("Choose your colour"));
QPushButton* qpb3 = new QPushButton("white(2nd turn)");
QPushButton* qpb4 = new QPushButton("black(1st turn)");
QObject::connect(qpb3, SIGNAL(clicked()), SLOT(chwh()));
QObject::connect(qpb4, SIGNAL(clicked()), SLOT(chbl()));
choosing_colourL->addWidget(qpb3);
choosing_colourL->addWidget(qpb4);
choosing_colourW->setLayout(bot_or_playerL);
score_lturnW = new QWidget;
QVBoxLayout* score_lturnL = new QVBoxLayout;
lturn = new QLabel;
pturn = new QLabel;
score = new QLabel;
score_lturnL->addWidget(lturn);
score_lturnL->addWidget(pturn);
score_lturnL->addWidget(score);
score_lturnW->setLayout(score_lturnL);
after_gameW = new QWidget;
QVBoxLayout* after_gameL = new QVBoxLayout;
winner = new QLabel;
offer_to_play_again = new QLabel("Wanna play again?");
QPushButton* qpb5 = new QPushButton("yes");
QObject::connect(qpb5, SIGNAL(clicked()), SLOT(restart()));
QPushButton* qpb6 = new QPushButton("no");
QObject::connect(qpb6, SIGNAL(clicked()), qApp, SLOT(quit()));
after_gameW->setLayout(after_gameL);
leftpart = new QStackedLayout;
leftpart->addWidget(bot_or_playerW);
leftpart->addWidget(choosing_colourW);
leftpart->addWidget(score_lturnW);
leftpart->addWidget(after_gameW);
//"rightpart" init:
deck = new QGridLayout;
deck->setMargin(0);
deck->setSpacing(0);
e_pic = QPixmap("empty.png");
b_pic = QPixmap("black.png");
w_pic = QPixmap("white.png");
pic_sz = e_pic.size();
for (int i = 0; i < 8; ++i)
for (int j = 0; j < 8; ++j)
{
QPushButton* tqpb = new QPushButton;
tqpb->setIcon(e_pic);
tqpb->setIconSize(pic_sz);
std::stringstream ss;
std::string s;
ss << i << j;
ss >> s;
tqpb->setObjectName(s.c_str());
deck->addWidget(tqpb, i, j);
connect(tqpb, SIGNAL(clicked()), SLOT(turn_try()));
}
main_h_lo->addLayout(leftpart);
main_h_lo->addLayout(deck);
leftpart->setCurrentWidget(bot_or_playerW);
}
I get no error or warning. The deck part is scary and ugly but it is as expected :D. The "menu" part does not show up - that is the problem. Screen: http://i.imgur.com/Sh9PU9N.jpg .
Some comments about you code -
A layout can be given the parent on construction and it will automatically become the default layout. This saves you one command, but makes it more implicit. It all depends on what you prefer - implicit or explicit.
Loose the QObject::connect. A simple 'connect' will do.
Are you sure "black(1st turn)" is correct? In a conventional chess game, white generally goes first.
You can avoid std::stringstream and use QString::number instead.
The problem has already been mentioned by 'hyde'.