Vadixem Vadixem - 4 days ago 6
C++ Question

QTimer doesn't work outside main function

In short: this piece of code is located in main function and this works just well. This code performs swap on two graphic elements. enter image description here

QTimer timer;
timer.setTimerType(Qt::PreciseTimer);
timer.setInterval(1000.0/30.0);
timer.setSingleShot(false);

// ====================== MOVE 4 in POS of 1 ==========================
QPointF centre(QLineF(button1->pos(), button4->pos()).pointAt(0.5));
QPointF positionBut4 = button4->pos();

MyPointF centreSwap4(0, &positionBut4, &centre);
button4->myPointF = &centreSwap4;

QObject::connect(&timer, SIGNAL(timeout()), &centreSwap4, SLOT(updateDownRight()));
QObject::connect(&centreSwap4, SIGNAL(positionChanged()), button4, SLOT(slotMoveCircle()));

// ====================== MOVE 4 in POS of 1 ==========================

// ====================== MOVE 1 in POS of 4 ==========================
QPointF positionBut1 = button1->pos();

MyPointF centreSwap1(0, &positionBut1, &centre);
button1->myPointF = &centreSwap1;

QObject::connect(&timer, SIGNAL(timeout()), &centreSwap1, SLOT(updateUpLeft()));
QObject::connect(&centreSwap1, SIGNAL(positionChanged()), button1, SLOT(slotMoveCircle()));
// ====================== MOVE 1 in POS of 4 ==========================
timer.start();

QTimer::singleShot(3000, Qt::PreciseTimer, &timer, SLOT(stop()));


But when I want to put this piece of code out of main into a function (in order to shorten the code and perform swap on elements by just giving pointers to them) QTimer refuses to work(says it's active but timeout is not triggered):

void animateSwap(QGraphicsRectWidget *w1, QGraphicsRectWidget *w2, QTimer &timer)
{
QGraphicsRectWidget *button4, *button1;
if (w1->x() > w2->x())
{
button4 = w2;
button1 = w1;
}
else
{
button4 = w1;
button1 = w2;
}
// ====================== MOVE w2 in POS of w1 ==========================
QPointF centre(QLineF(button1->pos(), button4->pos()).pointAt(0.5));
QPointF positionBut4 = button4->pos();

MyPointF centreSwap4(0, &positionBut4, &centre);
button4->myPointF = &centreSwap4;

QObject::connect(&timer, SIGNAL(timeout()), &centreSwap4, SLOT(updateDownRight()));
QObject::connect(&centreSwap4, SIGNAL(positionChanged()), button4, SLOT(slotMoveCircle()));

// ====================== !MOVE w2 in POS of w1 ==========================

// ====================== MOVE w1 in POS of w2 ==========================
QPointF positionBut1 = button1->pos();

MyPointF centreSwap1(0, &positionBut1, &centre);
button1->myPointF = &centreSwap1;

QObject::connect(&timer, SIGNAL(timeout()), &centreSwap1, SLOT(updateUpLeft()));
QObject::connect(&centreSwap1, SIGNAL(positionChanged()), button1, SLOT(slotMoveCircle()));
// ====================== MOVE w1 in POS of w2 ==========================
timer.start();

qDebug() << timer.isActive();

QTimer::singleShot(3000, Qt::PreciseTimer, &timer, SLOT(stop()));
}


Please tell me how do I fix that. Thanks in advance!

UPD:
Full code:

#include <QtCore>
#include <QtWidgets>


enum MOVE_DIRECTION{ UP, DOWN, RIGHT, LEFT};

class MyPointF : public QObject
{
Q_OBJECT
public:

/**
* @brief MyPointF Class to perform rotation on graphical item
* @param parent
* @param actualPoint Actual position of a point
* @param centre A pivotal point around which rotation is performed
* @param degree Defines on which degree position will be rotated
* @param ddr Degree Decrease rate is degree by which @param degree will be decreased every time slot update will be entered, defined as degree/degreeDecreaseRate
*/
MyPointF(QObject *parent = 0, QPointF *actualPoint = 0, QPointF *centre = 0, double degree = 180.0, double ddr = 90)
: QObject(parent), actualPoint(*actualPoint), centre(*centre), degree(degree), degreeDecreaseRate(ddr)
{
radius = QLineF(*centre, *actualPoint).length();
}
double degree;
double degreeDecreaseRate;
QPointF actualPoint;
QPointF centre;
double radius;

signals:
void positionChanged();

public slots:
void updateUpLeft()
{
// qDebug() << "upleft triggered";
degree -= 180.0 / degreeDecreaseRate;
actualPoint.setX(-cos(qDegreesToRadians(degree)) * radius + centre.x());
actualPoint.setY(-sin(qDegreesToRadians(degree)) * radius + centre.y());
emit positionChanged();
}
void updateDownRight()
{
// qDebug() << "downRight triggered";
degree -= 180.0 / degreeDecreaseRate;
actualPoint.setX(cos(qDegreesToRadians(degree)) * radius + centre.x());
actualPoint.setY(sin(qDegreesToRadians(degree)) * radius + centre.y());
emit positionChanged();
}

};
// Draws a blue rect
class QGraphicsRectWidget : public QGraphicsWidget
{
Q_OBJECT
// QPropertyAnimation anim;
int m_number;
int m_movementSpeed;
public:

static int NUMBER;
MyPointF *myPointF;

QGraphicsRectWidget(QGraphicsItem *parent = 0, int ms = 5, MyPointF *myPointF = 0)
: QGraphicsWidget(parent), m_number(NUMBER), m_movementSpeed(ms), myPointF(myPointF)
{ NUMBER++;}
void paint(QPainter *painter, const QStyleOptionGraphicsItem *,
QWidget *) Q_DECL_OVERRIDE
{
painter->fillRect(rect(), QColor(127, 63, 63));
painter->drawText(rect(), QString("%1").arg(m_number), QTextOption(Qt::AlignCenter));
}
public slots:

void slotMoveCircle()
{
setPos(myPointF->actualPoint);
}


};

int QGraphicsRectWidget::NUMBER = 1;


class GraphicsView : public QGraphicsView
{
Q_OBJECT
public:
GraphicsView(QGraphicsScene *scene, QWidget *parent = NULL) : QGraphicsView(scene, parent)
{
}

protected:
virtual void resizeEvent(QResizeEvent *event) Q_DECL_OVERRIDE
{
fitInView(scene()->sceneRect());
QGraphicsView::resizeEvent(event);
}
};


#define SWAP_HEIGHT 75


void animateSwap(QGraphicsRectWidget *w1, QGraphicsRectWidget *w2, QTimer &timer)
{
QGraphicsRectWidget *button4, *button1;
if (w1->x() > w2->x())
{
button4 = w2;
button1 = w1;
}
else
{
button4 = w1;
button1 = w2;
}
// ====================== MOVE w2 in POS of w1 ==========================
QPointF centre(QLineF(button1->pos(), button4->pos()).pointAt(0.5));
QPointF positionBut4 = button4->pos();

MyPointF centreSwap4(0, &positionBut4, &centre);
button4->myPointF = &centreSwap4;

QObject::connect(&timer, SIGNAL(timeout()), &centreSwap4, SLOT(updateDownRight()));
QObject::connect(&centreSwap4, SIGNAL(positionChanged()), button4, SLOT(slotMoveCircle()));

// ====================== !MOVE w2 in POS of w1 ==========================

// ====================== MOVE w1 in POS of w2 ==========================
QPointF positionBut1 = button1->pos();

MyPointF centreSwap1(0, &positionBut1, &centre);
button1->myPointF = &centreSwap1;

QObject::connect(&timer, SIGNAL(timeout()), &centreSwap1, SLOT(updateUpLeft()));
QObject::connect(&centreSwap1, SIGNAL(positionChanged()), button1, SLOT(slotMoveCircle()));
// ====================== MOVE w1 in POS of w2 ==========================
timer.start();

qDebug() << timer.isActive();

QTimer::singleShot(3000, Qt::PreciseTimer, &timer, SLOT(stop()));
}

int main(int argc, char **argv)
{
QApplication app(argc, argv);

QGraphicsRectWidget *button1 = new QGraphicsRectWidget;
QGraphicsRectWidget *button2 = new QGraphicsRectWidget;
QGraphicsRectWidget *button3 = new QGraphicsRectWidget;
QGraphicsRectWidget *button4 = new QGraphicsRectWidget;
button2->setZValue(1);
button3->setZValue(2);
button4->setZValue(3);
QGraphicsScene scene(0, 0, 300, 300);
scene.setBackgroundBrush(QColor(23, 0, 0));
scene.addItem(button1);
scene.addItem(button2);
scene.addItem(button3);
scene.addItem(button4);
GraphicsView window(&scene);
window.setFrameStyle(0);
window.setAlignment(Qt::AlignLeft | Qt::AlignTop);
window.setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
window.setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);


QList<QGraphicsItem*> items = window.items();
QPoint start(20, 125);
for (auto item : items)
{
QGraphicsWidget *wgt = static_cast<QGraphicsWidget*>(item);
wgt->resize(50,50);
wgt->moveBy(start.x(), start.y());
start.setX(start.x() + 70);
}

// ===================================================== WORKING SWAP ==========================================================
QTimer timer;
timer.setTimerType(Qt::PreciseTimer);
timer.setInterval(1000.0/30.0);
timer.setSingleShot(false);

// ====================== MOVE 4 in POS of 1 ==========================
QPointF centre(QLineF(button1->pos(), button4->pos()).pointAt(0.5));
QPointF positionBut4 = button4->pos();

MyPointF centreSwap4(0, &positionBut4, &centre);
button4->myPointF = &centreSwap4;

QObject::connect(&timer, SIGNAL(timeout()), &centreSwap4, SLOT(updateDownRight()));
QObject::connect(&centreSwap4, SIGNAL(positionChanged()), button4, SLOT(slotMoveCircle()));

// ====================== MOVE 4 in POS of 1 ==========================

// ====================== MOVE 1 in POS of 4 ==========================
QPointF positionBut1 = button1->pos();

MyPointF centreSwap1(0, &positionBut1, &centre);
button1->myPointF = &centreSwap1;

QObject::connect(&timer, SIGNAL(timeout()), &centreSwap1, SLOT(updateUpLeft()));
QObject::connect(&centreSwap1, SIGNAL(positionChanged()), button1, SLOT(slotMoveCircle()));
// ====================== MOVE 1 in POS of 4 ==========================
timer.start();

QTimer::singleShot(300000, Qt::PreciseTimer, &timer, SLOT(stop()));






window.resize(300, 300);
window.show();

qsrand(QTime(0,0,0).secsTo(QTime::currentTime()));

return app.exec();
}



#include "main.moc"

Answer

You are connecting the timeout() signal of the timer to e.g.

MyPointF centreSwap1(0, &positionBut1, &centre);

which is an object on the stack, and therefore local to the function and is deleted the moment the function finishes. Qt disconnects a connection if one of the objects (sender, receiver) is deleted, so the moment the timer finishes there is nothing connected to its timeout() signal.

Comments