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();
}
Related
I have a QStandardItemModel with a single column (represents a list). Each item in the list has a unique integer ID stored as the QStandardItem's data (via QStandardItem::setData which I guess is into Qt::UserRole+1 by default).
Given one of these IDs, I'd like to find and remove the corresponding row from the model. Right now I'm doing this:
void NetworkManager::removeSessionFromModel (QStandardItemModel *model, int sessionId) {
foreach (const QStandardItem *item, model->findItems("*", Qt::MatchWildcard)) {
if (item->data() == sessionId) {
model->removeRow(item->index().row());
break;
}
}
}
It works fine, but every single line of that function makes me cringe. Is there a cleaner way to do any of this?
How about traversing the QStandardItemModel directly? Something like this:
void NetworkManager::removeSessionFromModel (QStandardItemModel *model, int sessionId)
{
for (int i = 0; i < model->rowCount(); ++i)
{
if (model->item(i)->data() == sessionId)
{
model->removeRow(i);
break;
}
}
}
Not sure how QStandardItemModel behaves with random access, maybe your method is more efficient.
Edit:
Actually, there is a function to do what you want: QAbstractItemModel::match
It returns a QModelIndexList with all entries that have matching data in a given role.
void NetworkManager::removeSessionFromModel (QStandardItemModel *model, int sessionId)
{
QModelIndexList list = model->match(model->index(0, 0), Qt::UserRole + 1, sessionId);
if (!list.empty())
model->removeRow(list .first().row());
}
Setting data to a specific role can be done as follows:
model->setData(model->index(row, col), QVariant(13), Qt::UserRole + 1);
You need to get the row index from your item id.
A more effective way could be to use a QMap with the row index as value and the item id as a key.
In this case, you also need to maintain the map values every time you add/remove a row.
If you don't have 3 millions items in your list, just keep it simple and use your code.
By optimize this code, you probably also add complexity and reduce maintainability, and you get is 0,05 ms instead of 0,06 ms.
In GUI code, I often have code like this : it's simple, everyone get it immediatly and it does the job. It' also fast enough.
You're using findItems wrong, it can already return the item you want just by passing the value you're searching for. If you call it like you're doing right now you're looping through your items at least two times, since findItems must iterate through all the items to find those that match your pattern, in your case all items match, then you iterate the returned items again to find the sessionId.
void NetworkManager::removeSessionFromModel (QStandardItemModel *model, int sessionId) {
auto items = model->findItems(QString::number(sessionId));
if (!items.empty()) {
auto row = items.first()->index().row();
model->removeRow(row);
}
}
Alternatively you can use the match method since findItems uses that internally, so you avoid allocating the StandardItem just to get its index. Also match returns right after the number of items matching the pattern, in this case the value of sessionId, are found so it doesn't always iterate all the items; that's more efficient. Obviously if the value is not found after iterating all the items it returns an empty list.
auto start = model->index(0, 0);
auto indexes = model->match(start, Qt::UserRole + 1, QString::number(sessionId), 1, Qt::MatchExactly);
if (!indexes.empty()) {
auto row = indexes.first().row();
model->removeRow(row);
}
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();
}
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.
A friend of mine and I are trying to make a game in C++ using Qt. We want to store a few QGraphicsTextItem in a QMap to access them during runtime. I've pasted the relevant parts of our code here, and our problem is that the program stops responding.
Game.cpp
int players = 6;
QGraphicsRectItem * overviewBox = new QGraphicsRectItem();
overviewBox->setRect(0, 0, 782, 686);
scene->addItem(overviewBox);
for(int i = 1; i <= players; i++) {
Container * ovContainer = new Container(overviewBox);
ovContainer->Overview(i, faceNo);
ovContainer->setPos(0, 0 + 110 * (i - 1));
info->textBoxMap[i-1] = ovContainer->textBox->playerText; // Program stops responding here
}
GameInfo.h
#ifndef GAMEINFO_H
#define GAMEINFO_H
#include "TextBox.h"
#include <QMap>
class GameInfo {
public:
GameInfo();
QMap<int, QGraphicsTextItem *> textBoxMap;
};
#endif // GAMEINFO_H
None of us have much experience using C++ or Qt, and we would appreciate any help.
Unless you are missing some code in your code snippet, then your QMap is not being used correctly. I think you have not allocated (inserted) any QMap items yet? - therefore you are accessing an element that is out of range (i.e. does not exist yet).
To add items into the QMap you can use insert(), like this (taken from Qt page):
QMap<int, QString> map;
map.insert(1, "one");
map.insert(5, "five");
map.insert(10, "ten");
Then to read your values back out:
QString str = map[1];
//or
QString str2 = map.value(5);
You don't need to iterate using a for loop but for your code you could do:
for(int i = 1; i <= players; i++)
{
:
:
info->textBoxMap.insert(i, ovContainer->textBox->playerText);
}
note
If you want to insert items with the same key you will need to use insertMulti(...), otherwise you will just overwrite the value of the key, e.g.:
QMap<int, QString> map;
map.insert(1, "test1");
map.insert(1, "test2");
Here, map[1] will return "test2". But I don't think this is what you want since your players are all going to be unique indexes I assume... but its worth pointing out that insert() with the same index just over-writes the value.
I create a QLineEdit,set a validator and put it on the table with this code:
ui->moneyTableWidget->setCellWidget(rowsNum, 1, newQLineEdit);
Then I've got another class for manipulating the table's data doing the sum of every value of a column. Here's the code:
int Calculator::calculatePricesSum(QTableWidget &moneyTableWidget){
double total = 0;
QWidget *tmpLineEdit;
QString *tmpString;
for(int row=0; row<moneyTableWidget.rowCount(); row++){
tmpLineEdit = (QLineEdit*)moneyTableWidget.cellWidget(row,1);
tmpString = tmpLineEdit.text();
total += tmpString->toDouble();
}
return total;
}
But the building fails with this error:
/home/testpec/src/nokia
QT/MoneyTracker-build-simulator/../MoneyTracker/calculator.cpp:11:
error: cannot convert ‘QLineEdit*’ to
‘QWidget*’ in assignment
Why this convertion error?
Another subquestion: passing the table as reference saves memory right? Could this be the problem? Im developing for a Nokia smartphone and I think passing the object by value is a waste of memory...(sorry if is a dumb question but I'm a little rusty with C++ and all the pointers stuff...)
When you declare your tmpLineEdit, you should be declaring it as a QLineEdit* instead of a QWidget*. Your loop grabs the widget, casts it to a QLineEdit* and then tries to put it back into a QWidget*. Also, I'd recommend using qobject_cast<QLineEdit*> (or dynamic_cast) so that you can ensure the cast succeeded.
int Calculator::calculatePricesSum(QTableWidget &moneyTableWidget){
double total = 0;
QLineEdit* tmpLineEdit;
QString tmpString;
for(int row=0; row < moneyTableWidget.rowCount(); row++)
{
tmpLineEdit = qobject_cast<QLineEdit*>(moneyTableWidget.cellWidget(row,1));
if(NULL == tmpLineEdit)
{
// Do something to indicate failure.
}
tmpString = tmpLineEdit->text();
total += tmpString.toDouble();
}
return total;
}
As for your second question, passing by reference is probably a good idea - I know some of the classes in Qt (QImage in particular) use reference counting and implicit sharing so that you can pass around by value without worrying about the implications of large copy operations, but I'm not sure if a QTableWidget is in that category as well.