2

I am in the process of putting Qlabels in a QGridLayout to arrange them nicely in a class derived from QWidget. However, when I try to access the coordinates, it always returns (0,0)

Here is my code :

class brick : public QWidget
{
Q_OBJECT
public:
    explicit brick(QWidget *parent);
    ...
private:
    QLabel* label;
    QPixmap* brickPic;
}

brick::brick(QWidget *parent) :
QWidget(parent)
{
rect=new QRect();
label=new QLabel;
brickPic=new QPixmap(100,15);
brickPic->fill(QColor(255,0,0));
label->setPixmap(*brickPic);
}

class Grid : public QWidget
{    
QOBJECT
public:
    void fill_grid(QWidget* parent);
    ...
private:
    QGridLayout* grid;
}
void Grid::fill_grid(QWidget* parent)
{
for (int i=0;i<10;i++)
{
    for (int j=0;j<12;j++)
    {
        brick* fillingBrick=new brick(parent);
        grid->addWidget(fillingBrick->getLabel(),j,i);
        qDebug()<<fillingBrick->parentWidget();
        brickVector.push_back(fillingBrick);
    }
}
}

These are to be shown as I said before in the following class derived from QWidget :

class render_area : public QWidget
{
Q_OBJECT
public:
    render_area(QWidget *parent = 0);
    Grid* getGrid() const;
    ...
private:
    Grid* grid;
    QRect* bar;
    ...
}

render_area::render_area(QWidget *parent)
:QWidget(parent)
{
    setFocusPolicy(Qt::StrongFocus);
    setBackgroundRole(QPalette::Base);
    setAutoFillBackground(true);

    bar=new QRect(width()/2,height()+50,150,10);
    grid=new Grid(this);
    grid->fill_grid(this);
    std::cout<<"Grid filled !"<<std::endl;

    qDebug()<<grid->getBrick(5)->pos();
    ...
}

The qDebug()<<fillingBrick->parentWidget() returns render_area which is good but qDebug()<<grid->getBrick(5)->pos() returns QPoint(0,0) which is not.

I wonder how I can get pos() or x() and y() to return the coordinates in render_area inside of which every brick is placed. I tried with cellRect from QGridLayout, MapToParent() inherited from QWidget, forcing render_area to show(); but no luck so far.

Edit : this is the loop I'm talking about :

void render_area::update_timer()
{
int x1,x2,y1,y2;
bar->getCoords(&x1,&y1,&x2,&y2);

if(is_fixed==false)
{
    //gestion du sol
    if(yc+diametre>height())
    {
        timer.stop();
        QDialog* gameOver=new QDialog;
        gameOver->setLayout(new QGridLayout);
        gameOver->layout()->addWidget(new QPushButton("Ok"));
        gameOver->setGeometry(500,500,300,150);
        gameOver->show();
    }

    if(xc>x1 && xc<x1+150)
    {
        if(yc+diametre>y1)
        {
            vyc=-vyc;
        }
    }
    //plafond
    for (int i=0;i<12;i++)
    {
        for(int j=0;j<10;j++)
        {
            grid->getBrick(i,j)->setRect(grid->getGrid()->cellRect(i,j));
        }
    }

    for(int i=0;i<widgets.size();i++)
    {
            if(widgets.at(i)->getRect()->intersects(ballRectangle))
            {
                std::cout<<i<<std::endl;
                widgets.at(i)->getPixmap()->fill(QColor(255,255,255));
                widgets.at(i)->getLabel()->setPixmap(*(widgets.at(i)->getPixmap()));
                delete widgets.at(i);
                vyc=-vyc;
            }
            //qDebug()<<grid->getBrick(i,j)->getRect();
    }
    //bord droit
    if(xc+diametre>width())
    {
            vxc=-vxc;
    }

    //bord gauche
    if(xc<0)
    {
            vxc=-vxc;
    }

    //integration (Euler explicite)
    xc=xc+dt*vxc;
    yc=yc+dt*vyc;

}
repaint();
}
user3476114
  • 634
  • 1
  • 7
  • 17
  • Hello and thank you for editing my post (I'm unfamiliar with this post syntax) and your quick answer. I am trying to do a brick breaker game and I need the coordinates of my bricks in order to detect collision with the ball. Here's what I have for now http://i60.tinypic.com/n70ih2.jpg – user3476114 Mar 29 '14 at 18:23

1 Answers1

1

The widget positions and sizes are determined by the layout, and are calculated in calls from event loop sometime after you call show() on the window widget, and sometime before that window becomes visible.

Since you want to implement a brick breaker game, there are two approaches:

  1. Use the graphics view/scene system.

  2. Use widgets, like you want to do.

Your widget-based approach is excellent to demonstrate that the solution to your problems can be solved naturally by not checking brick positions at all.

You will be updating the ball position in a timer event, or using the animation system. In either case, the ball widget will receive a moveEvent with the new position. There, you can easily calculate the intersections of the ball rectangle with each of the brick widget rectangles. Since they are children of the same parent, they'll be in the same coordinate system. At that time, all the positions will be current. Upon detecting a collision, you reflect the ball's direction, and literally delete the brick widget. You can also start playing a "hit" sound effect.

It's reasonably easy to use the QObject property system to mark different brick widgets depending on their behavioral traits. All this can be done without having to subclass the base widget, which could be a QFrame for example.

Kuba hasn't forgotten Monica
  • 95,931
  • 16
  • 151
  • 313
  • I saw indeed in the qt doc that pos gives the wrong coordinate until you call show(). I have a Qt timer used to update my render_area and tried to get the pos() of my brick inside the loop with no luck (QPoint(0,0)) – user3476114 Mar 29 '14 at 17:17
  • I added the loop in the first post. Thanks for pointing at directions for me it's really helpful. – user3476114 Mar 30 '14 at 09:54
  • Thanks for pointing at directions for me it's really helpful. From what I understand, I could get the rectangle in which the ball is drawn and check if this rectangle intersects the brick rectangle. There is still something I do not quit get : should I check the intersection at every timer update ? If so isn't this check somehow overwhelming for the system ? Also, my ball is drawn in a QPainter `drawEllipse`, is there a way to get the rectangle where it is drawn (maybe with `childrenRect`) or should I implement it as an attribute in the render_area class ? – user3476114 Mar 30 '14 at 10:04
  • "isn't this check somehow overwhelming for the system" Not at all. Every game does that. The "check" amounts to maybe a couple hundred integer comparisons. "my ball is drawn in a QPainter drawEllipse" The ball should be a widget that's completely filled with the ellipse: `painter.drawEllipse(rect())`. Note that `rect()` is always `QRect(0, 0, width(), height())`. Then it's very easy to calculate its intersection with any other widget. – Kuba hasn't forgotten Monica Mar 30 '14 at 11:28
  • Hi again and thank you for your answers, I'm nearly done thanks to you. I still have a little problem though. I updated my `update_timer` code on the first post so you can have the last version of it. Like you said, I delete `~QWidget()` the widget when the ball intersects it. My problem is that whenever the ball reintersects the `cellRect` where there formerly was a widget, the program crashes. I tried the `QLayoutItem.isEmpty()` member function to test if it is empty but when I call it the program crashes so I choosed the alternative in my code. What could be the problem ? – user3476114 Mar 30 '14 at 11:49
  • @user3476114 There's no need for you to keep track of those `cellRect` items. Every time you run update loop, simply iterate the children of the widget that holds bricks as children. During the iteration you'll run across the ball widget, and you must ignore it. There's also no need for any `repaint()` or `update()` calls, this is handled automatically. You should not be adding any code that you have not personally verified to have an effect. Your `repaint()` does not visually change anything, it just wastes a bit of CPU time. – Kuba hasn't forgotten Monica Mar 30 '14 at 12:32
  • I dont't understand what you mean by "iterate the children of the widget that holds bricks as children". I tried without the `repaint()`as you said but without it, the `render_area` is not in motion, it's only updated when `mousePressEvent`is called. What I dont understand is that once `grid->getGrid()->itemAtPosition(i,j)->widget()->~QWidget();` is called `grid->getGrid()->itemAtPosition(i,j)->widget()` should return 0 and then be ignored so I don't understand why the program crashes. – user3476114 Mar 30 '14 at 12:52
  • That's not how you delete a widget: `grid->getGrid()->itemAtPosition(i,j)->widget()->~QWidget();`. Never do that. The correct way is: `delete grid->getGrid()->itemAtPosition(i,j)->widget();` – Kuba hasn't forgotten Monica Mar 30 '14 at 13:01
  • 1
    "iterate the children of the widget that holds bricks as children" Use the `findChildren` method to get a list of child widgets, then go over that list and find the widget that intersects the ball, then delete the widget. That's all. – Kuba hasn't forgotten Monica Mar 30 '14 at 13:01
  • Ok thank you for your help, I took into account what you said and implemented a list of bricks with findChildren. I set the collision with the ball rectangle and posted the updates on my code in the first post. I have two problems left : `delete widgets.at(i)` doesn't delete the brick : we can still see the QLabel and even though the collision is detected at the right position, the edited brick isn't the right one. Maybe its because `findChildren`ad bricks to the list columnwise ... I'm not sure though. Here's a pic to show you the problem : http://i58.tinypic.com/ixuy38.png – user3476114 Mar 30 '14 at 14:53
  • @user3476114 It seems that you are not deleting the brick that intersected the ball widget, but some other brick. As you iterate the list, you first check for intersection, then you update ball speed and delete the widget. Those two operations should be inside of the loop test: `foreach (QWidget * brick, findchildren("brick")) if (ball.geometry().intersects(brick->geometry()) { .... delete brick; }`. I'm assuming that you gave all the bricks names to differentiate them from the paddle and the ball :) – Kuba hasn't forgotten Monica Mar 30 '14 at 16:28
  • Ok thank you a lot for your help I'll get it to work I'm confident now. I appreciate the time you spent giving me advices to get me on the right direction you're awsome :) – user3476114 Mar 30 '14 at 18:12