PDA

View Full Version : Refer to an interface object name with a variable (string)



guiismiti
27th January 2015, 15:03
Hello,

I'm developing an app that has 100 line edits.
I named them t001, t002, t003, ..., t100
I want to convert the value of each text in the line edits to double (no problem there) and save it in a vector, called t[].
I don't want to repeat the following code a hundred times over:



double t[100];
int count1 = 0;
if(ui->t001->text() != "")
{
t[count1] = ui->t001->text().toDouble();
count1++;
}


So, is there anyway I can use the name of the object as a string variable, so I can use a loop? Like:



double t[100];
int count1 = 0;
QString current_element;


for(count1 = 0; count1 < 100; count1++)
{
current_element = "t" + count1+1;

if(ui->current_element->text() != "")
t[count1] = ui->current_element->text().toDouble();
}


edited: I know this code wouldn't work and needs adapting in at least 2 parts, but still, what I need answered is the main question in the thread.


Thanks in advance.

anda_skoa
27th January 2015, 15:28
Why not simply put the line edits in a list as well?
Or even create them programatically in the first place into a list.

Cheers,
_

guiismiti
27th January 2015, 16:05
I don't understand how putting the objects in a list would help me, I'm a newbie, that's why I posted it in this board.
I tried googling it, the key words are too common.

Anyway, this should make my problem very clear for those who did not understand:



double t[100];
int count1 = 0;
QString objectname;

for(count1 = 0; count1 < 100; count1++)
{
if(count1 < 9)
objectname = "t0";
else
objectname = "t";

objectname += QString::number(count1+1);
t[count1] = ui->objectname->text().toDouble();
}


And, when I try to build this, I get:
'objectname': is not a member of 'Ui::MainWindow'

since I'm trying to use ui->objectname instead of ui->t01 or ui->t02 or ui->t03, and all the way to t100, one by one.

wysota
27th January 2015, 16:33
I don't understand how putting the objects in a list would help me,

You would iterate over the list the same way you are iterating over 't' in your code snippet.

Lesiok
27th January 2015, 17:23
Read about QObject::findChild

guiismiti
27th January 2015, 18:23
thank you all, i'll look it up and test.

d_stranz
27th January 2015, 18:29
I don't understand how putting the objects in a list would help me

Instead of creating your QLineEdit objects in Qt Designer, create them in code and insert them into a vector:



// in the constructor for your widget
// assumption is that there is a layout to handle the 100 line edits
// also assume there is a QVector< QLineEdit * > * pEdits member variable

pEdits = new QVector< QLineEdit * >( 100, 0 );
for ( int i = 0; i < 100; i++ )
{
QLineEdit * pEdit = new QLineEdit( this );
pLayout->addWidget( pEdit );
pEdits[ i ] = pEdit;
}


Now if you want to retrieve the text from the "nth" line edit, you simply do this:



t[ n ] = pEdits[n]->text().toDouble();

guiismiti
27th January 2015, 19:09
Creating the line edits by code would get too complicated for me, I'm using a few stacked widgets.

wysota
27th January 2015, 21:13
Creating the line edits by code would get too complicated for me, I'm using a few stacked widgets.

That's really not a problem at all. Even if you were to copy and paste the same code 100 times, it would still be beneficial. And you don't have to do that, you can create your widgets based on some array or whatever can hold the ui definition and is iterable.

anda_skoa
28th January 2015, 09:18
I don't understand how putting the objects in a list would help me

A list is a sequential container, its element can be accessed by numerical index, like the array.
If you have the elements in a list, you can access the result value array index and the source element with the same integer value.

You can even use an array of pointers instead of the list, or a vector.

If you can't create the elements in code, you still need to initialize this container once, while every other access can then be based on numerical index.

Cheers,
_

guiismiti
28th January 2015, 16:04
A list is a sequential container

Thank you once again.

Is there anything about lists in the Qt docs? I would like to explore syntax and read about it.

anda_skoa
28th January 2015, 16:07
See documentation for QList and QVector.

Cheers,
_

d_stranz
28th January 2015, 17:33
Creating the line edits by code would get too complicated for me, I'm using a few stacked widgets.

As wysota and others have said, if you don't want to create the widgets in code, you can still retrieve their pointers and store them in a list or vector:



// In the constructor for each widget in the stack:

ui->setupUi( this );

pVector = new QVector< QLineEdit * >( 100 );

pVector[ 0 ] = ui->t000;
pVector[ 1 ] = ui->t001;
// etc. for the next 98 line edits

// Then, you still retrieve the text for each widget using the vector

t[n] = pVector[n]->text().toDouble();


Edit: You could avoid the cut-and-paste if you are consistent in your names for the line edits. You can then use findChild, as Lesiok suggests:



pVector = new QVector< QLineEdit * >( 100 );

for ( int i = 0; i < 100; i++ )
{
QString name = QString( "t%1" ).arg( i, 3, '0' );
pVector[ i ] = findChild< QLineEdit * >( name );
}



This assumes the line edits are named "t000", "t001", ... "t099".

wysota
28th January 2015, 23:32
Just remember, despite having to write less lines of code to use findChild this is actually slower to execute than 100 copy&paste of the manual code that retrieves a single pointer. So all in all it is a matter of balance between the number of lines of code you have to write (once) and the amount of code that needs to be executed (every time you run your app).

guiismiti
29th January 2015, 00:29
Just remember, despite having to write less lines of code to use findChild this is actually slower to execute than 100 copy&paste of the manual code that retrieves a single pointer. So all in all it is a matter of balance between the number of lines of code you have to write (once) and the amount of code that needs to be executed (every time you run your app).

Yea, I thought about that.

I had the code written and ready just before I started the thread, but in my honest opinion I don't really like the idea of doing this kind of manual job when it can be avoided.

I said I have 100 elements but I actually have 3 stacked widgets with 100 each - but that doesn't affect anything, so I decided to make it simple to present the problem. All elements are essential to the app - they are results of a laboratory experiment that must be informed as float (or double, I don't really know the difference).

I haven't got much time right now (couldn't even open Qt today :( ), but I think that the list is the best option for me.
But still, does anybody know what's wrong here? I'm trying to use findChild. I have tried to use parentWidget instead of centralWidget, but no difference.


QLineEdit *lineedit = centralWidget->findChild<QLineEdit *>("t01");

The error message I get is this

'->QObject::findChild' : left operand has " type, use '.'


Edited: This may be helpful

10913

d_stranz
29th January 2015, 00:53
Perhaps you need ui->centralWidget->findChild< QLineEdit * >( "t01" ) ? The compiler is probably complaining about the dereferencing because it can't find a variable named "centralWidget" in the current scope.

And you probably don't want to use centralWidget in any case - findChild will get only the first of the QLineEdit widgets it finds with a matching name in one of the stacked widgets, probably the first one in the stack. (Assuming that the line edits on each page have the same names - eg. "t01" is the first line edit on each page). You will want to retrieve each set of 100 from each stacked widget separately and store them in three separate vectors / lists.

guiismiti
29th January 2015, 01:57
Assuming that the line edits on each page have the same names - eg. "t01" is the first line edit on each page

Not the case - it's tn for time, vn for volume and cn for concentration.

d_stranz
29th January 2015, 02:19
Not the case - it's tn for time, vn for volume and cn for concentration.

Then you're lucky :) Even so, I would still retrieve the line edit pointers from their most immediate parent (eg. the individual page widgets inside the stack widget).

It's an encapsulation thing. The main window (and indeed, the rest of your app) doesn't need to know that there are 100 line edits buried down on a page somewhere in a stack widget. What it needs to know is when the array of time values has been updated. What I would do in that case is to keep all the code that retrieves the line edit pointers and keeps track of when the user changes things encapsulated within the parent widget that contains the line edits. When the user changes a value, you update it in the vector or list (which is held by the parent widget containing the line edits), and then emit a custom signal ("timeValuesChanged( const QVector< double > & )" ) with the new array of time values. Likewise volume and concentration.

This way, you can do whatever you want to change the widgets inside the stack, the only thing the main window will know is that it should expect a signal like that when the values change. You could even substitute an object that reads time, volume, and concentration values from a file (or sensor array), and as long as it emits the same signal, the app won't know the difference.

wysota
29th January 2015, 06:31
I said I have 100 elements but I actually have 3 stacked widgets with 100 each - but that doesn't affect anything, so I decided to make it simple to present the problem. All elements are essential to the app - they are results of a laboratory experiment that must be informed as float (or double, I don't really know the difference).


I would have a configuration file (e.g. xml) that would list all the parameters and at runtime I would read the file, create the widgets based on it and so on. Should be really easy. If at any time you decide to modify the widgets, it is enough to modify the configuration file and the program will follow.

guiismiti
31st January 2015, 03:43
Things seem to be working fine right now.

Although, I've got this little problem going on with the interface - the first page of the stacked widget is always fully displayed, but all the others are not (like in the images). It doesn't matter which is the first page, all the other ones will be like in the second image, with a huge gray square on the right side.

Here is the first page:
10925

Here is the second page:
10926

Please let me know if you need any more info.


Again, thank you very much, I really appreciate it.

anda_skoa
31st January 2015, 05:21
Does each page widget have a proper layout?
Is the stack widget part of a layout?

Cheers,
_

guiismiti
31st January 2015, 15:20
The size of each page is the same, 711x366.

stackedWidget is part of centralWidget.

I just measured the app window in pixels, and it looks that something is reducing the pages (except the first page, like I said) to have 640 pixels of width.
Not sure if I was clear, but the first page is always the selected one when compiling the program - so, it is gonna be the only page that is fully displayed.

Here is centralWidget configuration:
10928

And here is MainWindow (centralWidget is under MainWindow):
10929


Edited: I just tried bringing stackedWidget to front, didn't work.

Edited #2: I also tried to change the app width to 1000 px, it is still cutting everything inside the window to 640.

wysota
31st January 2015, 16:00
You were asked a simple question -- whether your pages have a layout set on them. If you do not know what a layout is then just say so or look it up in the docs.

Layout Management (http://doc.qt.io/qt-5/layout.html)

guiismiti
31st January 2015, 16:06
no
10char10char

wysota
31st January 2015, 16:52
no

Now you know your problem and you can fix it easily by applying a layout to each page and also putting your stacked widget in one.