PDA

View Full Version : Qt Tutorial #1 Add-on



ShaChris23
18th April 2007, 07:06
Hi guys,

For this tutorial here,

http://doc.trolltech.com/4.2/tutorial-t11.html

Does someone know how to do this?

"Modify the program such that the X Y location of each shot bullet is displayed on the GUI on the top right. X Y should become 0, 0 once the shot is completely gone. The definition of gone is not just the bullet being off screen since it's possible for a bullet to go off the screen and fall down."

And, no, this is not a homework! I'm just curious how such feature would be implemented!

marcel
18th April 2007, 07:14
Sure...
First you have to create two labels in the GUI ( or just one ) - one for X and one for Y.
The moving of the bullet is made in CannonField::moveShot(). You should emit a signal from here with the x and y position. The signal should be connected to a slot in the dialog that just updates the labels with the X and Y values.



void CannonField::moveShot()
{
QRegion region = shotRect();
++timerCount;

QRect shotR = shotRect();

if (shotR.x() > width() || shotR.y() > height()) {
autoShootTimer->stop();
} else {
region = region.unite(shotR);
}
update(region);
emit updateBulletPosition(shotRect().topLeft().x(), shotRect().topLeft().y() );
}


Regards

marcel
18th April 2007, 07:24
You have to create the labels in main.cpp:


QHBoxLayout *topLayout = new QHBoxLayout;
topLayout->addWidget(shoot);
topLayout->addStretch(1);
QLabel* mxLabel = new QLabel( this );
QLabel* myLabel = new QLabel( this );
topLayout->addWidget( xLabel );
topLayout->addWidget( yLabel );
connect( cannonField, SIGNAL( updateBulletPosition(int,int) ), this, SLOT( setBulletPosition(int, int ) ) );

QVBoxLayout *leftLayout = new QVBoxLayout;
leftLayout->addWidget(angle);
leftLayout->addWidget(force);

QGridLayout *gridLayout = new QGridLayout;
gridLayout->addWidget(quit, 0, 0);
gridLayout->addLayout(topLayout, 0, 1);
gridLayout->addLayout(leftLayout, 1, 0);
gridLayout->addWidget(cannonField, 1, 1, 2, 1);
gridLayout->setColumnStretch(1, 10);
setLayout(gridLayout);

angle->setValue(60);
force->setValue(25);
angle->setFocus();

In the MyWidget class declaration:


class MyWidget : public QWidget
{
public:
MyWidget(QWidget *parent = 0);

public slots:
void setBulletPosition( int x, int y );

private:
QLabel* mxLabel;
QLabel* myLabel;
};

the slot:


void MyWidget::setBulletPostion( int x, int y )
{
mxLabel->setText( QVariant(x).toString() );
myLabel->setText( QVariant(y).toString() );
}


This is it, pretty much. I'm not sure if the labels will appear correctly ( int the top-right corner ), but that can be fixed easily.

ShaChris23
20th April 2007, 00:28
Hi Marcel,

Thanks for a very helpful post. For some reason though, when I run the program, Qt reports the following:

Object::connect: No such slot QWidget::setBulletPosition(int,int)

I also dont see the x y label on the top right. Here's my current code:


From MyWidget constructor


QHBoxLayout* topLayout = new QHBoxLayout;
topLayout->addWidget(shoot);
topLayout->addStretch(1);

QLabel* mxLabel = new QLabel( this );
QLabel* myLabel = new QLabel( this );

topLayout->addWidget( mxLabel );
topLayout->addWidget( myLabel );
connect( cannonField, SIGNAL( updateBulletPosition( int, int ) ), this, SLOT( setBulletPosition( int, int ) ) );



From MyWidget class declaration


class MyWidget : public QWidget
{
public:
MyWidget( QWidget* parent = 0 );

public slots:
void setBulletPosition( int x, int y );

private:
QLabel* mxLabel;
QLabel* myLabel;
};


From MyWidget::setBulletPosition( int x, int y )


void MyWidget::setBulletPosition( int x, int y )
{
mxLabel->setText( QVariant(x).toString() );
myLabel->setText( QVariant(y).toString() );
}



From CannonField::moveShot()


void CannonField::moveShot()
{
//
QRegion region = shotRect();
++timerCount;

// Get the new shot
QRect shotR = shotRect();

if( shotR.x() > width() || shotR.y() > height() )
{
autoShootTimer->stop();
}
else
{
region = region.unite(shotR);
}

// Repaint the QRegion
update(region);
emit updateBulletPosition( shotRect().topLeft().x(), shotRect().topLeft().y() );
}


Please let me know if I missed anything...I cant think of a reason why this doesnt work.

jacek
20th April 2007, 00:34
Add the Q_OBJECT macro:
class MyWidget : public QWidget
{
Q_OBJECT
public:
...and re-run qmake and make.

marcel
20th April 2007, 17:06
Did you declare the signal updateBulletPosition, in the CannonField class declaration?
Should be something like:


class CanonField : ....
{
...
signals:
void updateBulletPosition( int, int );
...
}


Regards

ShaChris23
20th April 2007, 18:06
Hi Marcel,

Yes, here's my CannonField class declaration:



class CannonField : public QWidget
{
Q_OBJECT

public:
CannonField( QWidget* parent = 0 );

int angle() const { return currentAngle; }
int force() const { return currentForce; }

public slots:
void setAngle( int angle );
void setForce( int force );
void shoot();

private slots:
void moveShot();

signals:
void angleChanged( int newAngle );
void forceChanged( int newForce );
void updateBulletPosition( int x, int y );

protected:
void paintEvent( QPaintEvent* event );

private:
void paintShot( QPainter& painter );
void paintCannon( QPainter& painter );

QRect cannonRect() const;
QRect shotRect() const;

int currentAngle;
int currentForce;

int timerCount;
QTimer* autoShootTimer;
float shootAngle;
float shootForce;
};

ShaChris23
20th April 2007, 18:23
Hmm..this is strange. I put in Q_OBJECT into MyWidget class declaration, and it doesnt complain anymore. But when I press shoot, the program crashes at

qstring.h

here -->
inline int size() const { return d->size; }

under class


class Q_CORE_EXPORT QString

But when I take out Q_OBJECT, the program doesnt crash. Of course, the x y coordinate still doesnt show up when I press the shoot button.

I then looked around a little bit, turns out these 2 lines caused the crash:



void MyWidget::setBulletPosition( int x, int y )
{
mxLabel->setText( QVariant(x).toString() );
myLabel->setText( QVariant(y).toString() );
}


Once I comment out those two lines, it doesnt crash anymore. Do you happen to know why?

I also tried putting a cout debugging like this:



void MyWidget::setBulletPosition( int x, int y )
{
cout << x << " " << y << "\n";
}


and I saw that the x y are "real" numbers, not some garbage values.

I'm using Qt 4.2.3 with VS STUDIO 05.

marcel
20th April 2007, 20:28
Are mxLabel and myLabel valid pointers when you try to set their value?
Do you delete them somehow after you create them and add them to the layout?

Because I can't really see how it could crash in those lines...

Try not setting the actual coordinate. Call the setText methods for a constant string, "1" for example. See what it does then. If it still crashes, then your two pointers gost somehow corrupted.

Regards

ShaChris23
24th April 2007, 00:28
Hi Marcel,

I'm sorry to waste your time...the bug was in my MyWidget's constructor

where



QLabel* mxLabel = new QLabel( this );
QLabel* myLabel = new QLabel( this );


should have been



mxLabel = new QLabel( this );
myLabel = new QLabel( this );



Since I made mxLabel and myLabel part of the class private data members. +_=. So you were right. Sorry about that.

Anyway, how can I make the labels reset to 0 0 when the shot is completely off the screen?

ShaChris23
24th April 2007, 00:29
Also, does anyone know how to do the following?

"Re-paint the cannon to red when the shot is in the air. Then paint the cannon blue when the shot is gone."

Thanks!