PDA

View Full Version : Can someone explain this code from C++ GUI Programming with Qt 4?



deejross
19th January 2011, 23:47
I am reading the C++ GUI Programming with Qt4 2nd Edition book trying to learn Qt. I am completely new to Qt and C++. I have experience with C#, Java, VB, Python, and PHP, but C++ is just so new right now that I'm having trouble figuring out how stuff works. The worst part is I don't know how to Google something like this. Anyways, to my question...

I'm on Chapter 5, Creating Custom Widgets where you subclass QSpinBox in order to create HexSpinBox. This is the code I am referring to:


#ifndef HEXSPINBOX_H
#define HEXSPINBOX_H

#include <QSpinBox>

class QRegExpValidator;

class HexSpinBox : public QSpinBox
{
Q_OBJECT
public:
HexSpinBox(QWidget *parent = 0);

protected:
QValidator::State validate(QString &text, int &pos) const;
int valueFromText(const QString &text) const;
QString textFromValue(int value) const;

private:
QRegExpValidator *validator;
};

#endif


My question is about the line
class QRegExpValidator; What does this line do? Why am I not doing something like a
#include <QRegExpValidator> instead? This is probably a trivial thing for someone with more experience, so I'm sorry if this is a stupid question. I'm sure I'll have several more simple questions as I keep reading.

Zlatomir
20th January 2011, 00:08
My question is about the line
class QRegExpValidator; What does this line do?

That line is declaring a class (it is called a forward declaration), basically you just tell the compiler that you have a class called QRegExpValidator and it (the compiler) will get the definition a little bit later, more exactly in the .cpp file.

You can use that technique, but be careful you can't create objects, you can only declare pointers and initialize the pointers only after the class has been defined by you or you included the framework file which define that class.


Why am I not doing something like a
#include <QRegExpValidator> instead? This is probably a trivial thing for someone with more experience, so I'm sorry if this is a stupid question. I'm sure I'll have several more simple questions as I keep reading.

You will do that in the .cpp file. (If not maybe the author simplified some things and included all QtGui module, not the all the .h files one by one).

deejross
20th January 2011, 00:24
This is the entire .cpp file:

#include "hexspinbox.h"

HexSpinBox::HexSpinBox(QWidget *parent) :
QSpinBox(parent)
{
this->setRange(0, 255);
validator = new QRegExpValidator(QRegExp("[0-9A-Fa-f]{1-8}"), this);
}

QValidator::State HexSpinBox::validate(QString &text, int &pos) const
{
return validator->validate(text, pos);
}

QString HexSpinBox::textFromValue(int val) const
{
return QString::number(val, 16).toUpper();
}

int HexSpinBox::valueFromText(const QString &text) const
{
bool ok;
return text.toInt(&ok, 16);
}


QRegExpValidator is not defined there either, which is why I'm wondering if there's some kind of "magic" going on somewhere. I've been looking at some of Qt's .h files and a lot of things in there are way out of my league right now, but I did notice that QAbstractSpinBox does an #import <QValidator>, so I'm wondering if QRegExpValidator is already declared somewhere by QSpinBox and it's using a forward declaration as a way to keep QRegExpValidator from being imported more than once. Does that make sense, or am I totally misunderstanding this stuff?

Zlatomir
20th January 2011, 00:28
And that code compiles?

Are you sure you don't have something like:


#include <QtGui> //which include all QtGui module (in that is QRegExpValidator defined)
#include "hexspinbox.h"
?

deejross
20th January 2011, 00:48
I am positive. I have posted the full .h and full .cpp that I have and the code does compile without any errors. It took me a few minutes to figure out how to add the widget to a QMainWindow in order to test that it actually works. And it does work. From what I've read so far, this should NOT be working, but it is, so I've somehow missed the reason it works.

Zlatomir
20th January 2011, 01:03
Then it must be the QSpinBox that include the QRegExpValidator (it has by default some validation tools, maybe it uses internally QRegExpValidator).


...and it's using a forward declaration as a way to keep QRegExpValidator from being imported more than once.

No, the include guards are for that:

#ifndef HEXSPINBOX_H
#define HEXSPINBOX_H
//... your class declaration
#endif
The forward declaration are used to decrease the compile time, since the framework headers are not included in more of your files, in case you include your header hexspinbox.h in many other of your files.
And other usage is when you have two classes that each use pointer to the other.
See this FAQ (http://www.parashift.com/c++-faq-lite/misc-technical-issues.html#faq-39.11)

wysota
20th January 2011, 01:36
Suprisingly QSpinBox does include QRegExpValidator through inclusion of qabstractspinbox.h which in turn includes qvalidator.h which defines QRegExpValidator.


#include <QSpinBox>

int main(int argc, char **argv){
QRegExpValidator v;
return 0;
}

This compiles which means the forward declaration from the original snippet is not needed.

deejross
20th January 2011, 01:50
This compiles which means the forward declaration from the original snippet is not needed.
You are correct. I commented out the class QRegExprValidator; line in my .h file and the project still builds and works. Thanks.

portilhe
17th March 2011, 13:39
Hi all,

I am also following this book and I have a question regarding this exact same example.

I was trying to play with it making it display the prefix "0x" by adding the line
setPrefix("0x") to the HexSpinbox default constructor as so:


HexSpinBox::HexSpinBox (QWidget *parent)
: QSpinBox(parent)
{
setPrefix("0x");
setRange(0,255);
validator = new QRegExpValidator(QRegExp("[0-9A-Fa-f]{1,8}"), this);
}

Unfortunately this compiles but immediately gives me a seg fault:


The inferior stopped because it received a signal from the Operating System.

Signal name : SIGSEGV
Signal meaning : Segmentation fault

Does this have anything to do with the validator getting the prefix value? Is it fixable?

Thanks in advance,
Manu

norobro
17th March 2011, 18:00
Try setting the prefix after you initialize *validator.

portilhe
17th March 2011, 18:54
Thanks, it works. Well, almost! I had to change the regular expression to

validator = new QRegExpValidator(QRegExp("0x[0-9A-Fa-f]{1,8}"), this);
otherwise, although it compiled and worked fined with the arrows, it didn't accept correctly any keyed-in input.

So now I have 2 questions:
1) Why does it work setting the prefix after the validator and not before?
2) From the fact that I had to include the "0x" inthe regular expression I am guessing that the reimplemented function (from QSpinBox) HexSpinBox::validate gets as a first argument the full text of the spin box, whereas the function valueFromText gets a stripped version. Am I guessing wrong?

norobro
17th March 2011, 20:17
Why does it work setting the prefix after the validator and not before?
setPrefix() calls HexSpinBox::validate(). In the return statement validate is an uninitialized pointer which causes a segfault.


2) ... Am I guessing wrong?
Run your code in a debugger or put in some qDebug() statements to see what's happening.