How to cycle through all widget in a QVBoxLayout - c++

I have a QVBoxLayout created through the drag and drop section.
Inside of it AT RUN TIME I insert some widgets with the command
ui->verticalLayout->insertWidget() //using appropriate options.
All widgets I insert are of the same type/class.
I would like to cycle through the inserted widgets in order to perform some actions over them.
I suppose it is really simple but can't seem to find out how...
thank you all!

You can use QLayout::itemAt() to loop on the items of the layout. Then use QLayoutItem::widget() to get the widget:
for(int i = 0; i < layout->count(); ++i)
{
do_something(
layout->itemAt(i)->widget()
);
}
Note that widget() may return a null pointer.

Related

Why cannot addItem into QGraphicsScene from a QVector<QGraphicsItem>?

I want to undo or redo actions in a QGraphicsScene that is linked to a QGraphicsView: For that, I use QGraphicsEllipse (to draw points while moving and clicking on the QGraphicsView) in my mouseMoveEvent method, I push every QGraphicsEllipse in a QVector<QGraphicsItem>, and when the QAction "undo" is triggered the program must delete the last ellipses (determined number of ellipses) drawn in my QGraphicsView thanks to my QGraphicsScene.
When I clear my QGraphicsScene and try to add all QGraphicsItems that were pushed in my QVector<QGraphicsItem>, I got an error: my app goes down!
if(index < historyIndex.size()){
for (int i = 0; i < scHistory.size() - historyIndex[index]; i++){
scene->addItem((QGraphicsItem*)scHistory[i]);
}
index++;
}
QVector<QGraphicsItem *> scHistory;
QGraphicsScene::addItem takes the ownership of the element added, check the doc. It means that it is now in charge of destructing the element, which happens when you clear the scene with QGraphicsScene::clear. From that point on, your vector is full of dangling pointers.
One quick fix is to replace the call to QGraphicsScene::clear with a manual removal of items through QGraphicsScene::removeItem, which doesn't destroy the item (it returns the ownership to the caller). Then, destroy only those elements that are actually out of the scene, and add back the rest. Another option, more efficient, is to remove only the elements you need, keeping the rest, so you also increase performance by avoiding add back a large number of items from the history.
Without a full knowledge of your code, the second options may be something like:
if(_indexHistoryRoam < _indexHistory.size()){
// Remove only items beyond history
const int range = _sceneHistory.size() - _indexHistory[_indexHistoryRoam];
for (int i = range; i < _sceneHistory.size(); i++){
scene->removeItem((QGraphicsItem*)_sceneHistory[i]);
delete _sceneHistory[i]; // item must be destroyed to avoid a leak
}
_indexHistoryRoam++;
} else { // force removal of all elements
scene->clear();
}
scene->update();

I'm using qt to design a program and need help because my code is not DRY [duplicate]

I have a gui form, where multiple text boxes are present. I want to put their values inside an array. One way of doing it is by writing something like this
{array element } = ui->text_1->text();
and repeat it for text_2,text_3 upto n.
What I want is to run a loop and replace number portion of text box name in each cycle.
something like this {array element } = ui->text_{This number getting changed }->text();
How can it be done in qt?
There are two ways of doing this.
When you create the UI, instead of using text1, text2, etc. you create an array of QLineEdits (eg. std::vector<QLineEdit>) and then when you want to retrieve their values then simply iterate over this array
Iterate over the children of the container widget. You can get the list of the children using the following (documentation):
QList<QObject *> list = parentWidget->children();
Another option to those listed would be to create an array using an initializer list. Depending on how big the array is (and how often it changes), this might be workable.
QLineEdit* entries[] = { ui->text_0, ui->text_1, ui=>text_2 };
QStringList answers;
for ( int i = 0; i < 3; ++i )
{
answers += entries[i]->text();
}
here is an expanded version of Matyas' solution:
class MyClass : public QWidget
{
QStringList answers;
void FillAnswersList(QObject *base)
{
QLineEdit *lineEdit = qobject_cast<QLineEdit>(base);
if(lineEdit)answers.append(lineEdit->text());
else
{
foreach(QObject *child, base->children())
FillAnswersList(child);
}
}
};
If it is just the number changing, and always incrementing, there is another possible solution using QObject::findChild, which takes a name as a parameter.
QString name_template("text_%1");
QStringList answers;
for(int i = 0; i < MAX_TEXTS; ++i)
{
QLineEdit *edit = ui->findChild<QLineEdit *>(name_template.arg(i));
answers += edit->text();
}

Qt : How to cast QTableRowItem to Object?

I'm new to Qt. i want to cast a row item in the QTableWidget as object.
So far, i've manage to populate the QTableWidget with QList:
header.h
QList<Inventory> inventories;
int row = 0;
int rowCount = ui->tableItems->rowCount();
ui->tableItems->insertRow(rowCount);
foreach(Inventory inventory, this->inventories)
{
QTableWidgetItem *code = new QTableWidgetItem(inventory.getName());
QTableWidgetItem *name = new QTableWidgetItem(inventory.getCode());
QTableWidgetItem *price = new QTableWidgetItem(GlobalFunctions::doubleToMoney(this, inventory.getPrice()));
ui->tableItems->setItem(row,0,code);
ui->tableItems->setItem(row,1,name);
ui->tableItems->setItem(row,2,price);
row++;
}
In my table, i will select the row using this.
void CreateSalesWindow::removeItem()
{
qDebug() << "Remove Item" << ui->tableItems->currentIndex().column();
this->salesdetails.removeAt(ui->tableItems->currentIndex().column() - 1);
this->refreshItemList();
}
I've manage to get the selected row, is there a straightforward way to cast my row back into object. I've come from a C# .Net Background where i could easily cast it back into something like this (just an example). I could not found any good solutions in SO and Documentation.
Inventory selectedInventory = (Inventory) ui->tableItems->selectedItem().getValue();
qDebug() << selectedInventory.getPrice();
// 1699.75
PS. I also want to remove an item from the QList<> from the selected row in the table.
Thanks! I'm new to Qt, i'm open to much better approach in handling this. if something is unclear please raise a comment so i could correct it.
I'm not familiar with the QTableWidget itself, but in general you should use the row method with Qt's model/view classes to access the underlying data row index of the model and then just access your original data from your custom model (depending on the implementation of your model).
In your case something like this:
int rowIndex = ui->tableItems->selectedItems().first().row();
// or this should also work to get the current index directly
int rowIndex = ui->tableItems->currentIndex().row();
Inventory *selectedInventory = ui->tableItems->model()->getInventory(rowIndex);
where the getInventory(int index) method is your custom method to access your object via its index (I guess you have a derived model from QAbstractItemModel or something so save your data and view it in the QTableWidget).
That is at least what I would do, you can read more about general model/view programming with Qt at Introduction to Model/View Programming.

Efficient way to make an array of labels

I'm making a board game and I need to display a 15 x 15 array in my gui. I decided to go with individual labels which contains one element of the array. This means I do have quite a lot of labels. I gave each label the name "tile_0_0" with the first 0 being the row and the second 0 being the column. This was quite easy to do in qt.
The problem however is that I ofcourse can't simply use 2 forloops to access each tile since you can't use variables in names. ("tile_i_j" does not exist.) To solve this I decided to make an array which contains each and every label. I however cannot initialise the array efficient because of the earlier mentioned problem.
So the question is: How can I avoid having to write a giant block of code?
A small piece of the current code:
Ui::BoardView *ui = new UI::BoardView; // BoardView is my class
QLabel* m_boardLbArray[8][8];
m_boardLbArray[0][0] = ui->tile_0_0;
m_boardLbArray[0][1] = ui->tile_0_1;
m_boardLbArray[0][2] = ui->tile_0_2;
m_boardLbArray[0][3] = ui->tile_0_3;
m_boardLbArray[0][4] = ui->tile_0_4;
// ...
Note: Sorry that I didn't post a part of code you could simply copy and run, but I do not know how since it's gui related.
It sounds like you are creating your tiles(QLabels) in Qt Designer; A cleaner way to achieve this is to create them programatically. You can do something like add a Grid Layout to your form in Designer in the location you want them, and then do:
QGridLayout *layout = ui->gridlayout;
QLabel* m_boardLbArray[8][8];
for(int row=0; row<8; row++)
for(int col=0; col<8; col++)
{
m_boardLbArray[row][col] = new QLabel(this);
m_boardLbArray[row][col]->setText(tr("This is row %1, col %2")
.arg(row).arg(col));
layout->addWidget(m_boardLbArray[row][col], row, col);
}

How to run a loop using gui object names in qt?

I have a gui form, where multiple text boxes are present. I want to put their values inside an array. One way of doing it is by writing something like this
{array element } = ui->text_1->text();
and repeat it for text_2,text_3 upto n.
What I want is to run a loop and replace number portion of text box name in each cycle.
something like this {array element } = ui->text_{This number getting changed }->text();
How can it be done in qt?
There are two ways of doing this.
When you create the UI, instead of using text1, text2, etc. you create an array of QLineEdits (eg. std::vector<QLineEdit>) and then when you want to retrieve their values then simply iterate over this array
Iterate over the children of the container widget. You can get the list of the children using the following (documentation):
QList<QObject *> list = parentWidget->children();
Another option to those listed would be to create an array using an initializer list. Depending on how big the array is (and how often it changes), this might be workable.
QLineEdit* entries[] = { ui->text_0, ui->text_1, ui=>text_2 };
QStringList answers;
for ( int i = 0; i < 3; ++i )
{
answers += entries[i]->text();
}
here is an expanded version of Matyas' solution:
class MyClass : public QWidget
{
QStringList answers;
void FillAnswersList(QObject *base)
{
QLineEdit *lineEdit = qobject_cast<QLineEdit>(base);
if(lineEdit)answers.append(lineEdit->text());
else
{
foreach(QObject *child, base->children())
FillAnswersList(child);
}
}
};
If it is just the number changing, and always incrementing, there is another possible solution using QObject::findChild, which takes a name as a parameter.
QString name_template("text_%1");
QStringList answers;
for(int i = 0; i < MAX_TEXTS; ++i)
{
QLineEdit *edit = ui->findChild<QLineEdit *>(name_template.arg(i));
answers += edit->text();
}