0

I am trying to learn Qt and I am attempting to do so by making a little titres game. Currently I have a 2d array which represents the game board.

Every second this 2d array is changed by a thread (representing the passage of time) and then this thread emits a signal telling the main GUI to update based on the new game board.

My Thread is as follows:

gamethread.h

#ifndef GAMETHREAD_H
#define GAMETHREAD_H
#include <QtCore>
#include <QThread>
#include<QMetaType>

class GameThread : public QThread
{
    Q_OBJECT

    public:
        explicit GameThread(QObject *parent = 0);
        void run();

    private:
        int board[20][10]; //[width][height]
        void reset();

    signals:
        void TimeStep(int board[20][10]);
};

#endif // GAMETHREAD_H

gamethread.cpp

#include "gamethread.h"
#include <QtCore>
#include <QtDebug>

//Game Managment
GameThread::GameThread(QObject *parent) :
    QThread(parent)
{
    reset();
}

void GameThread::reset()
{
    ...
}

//Running The Game
void GameThread::run()
{
    //Do Some Stuff
    emit TimeStep(board);
}

and the main UI which should receive the signal and update based on the new board is:

tetris.h

#ifndef TETRIS_H
#define TETRIS_H

#include <QMainWindow>
#include "gamethread.h"

namespace Ui{
    class Tetris;
}

class Tetris : public QMainWindow
{
    Q_OBJECT

    public:
        explicit Tetris(QWidget *parent = 0);
        ~Tetris();
        GameThread *mainThread;

    private:
        Ui::Tetris *ui;

    private slots:
        int on_action_Quit_activated();
        void on_action_NewGame_triggered();

    public slots:
        void onTimeStep(int board[20][10]);


};

#endif // TETRIS_H

tetris.cpp

#include <QMessageBox>
#include <QtGui>
#include <boost/lexical_cast.hpp>
#include <string>

#include "tetris.h"
#include "ui_tetris.h"

Tetris::Tetris(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::Tetris)
{
    ui->setupUi(this);
    mainThread = new GameThread(this);

    connect(mainThread, SIGNAL(TimeStep(int[20][10])),
            this, SLOT(onTimeStep(int[20][10])),
            Qt::QueuedConnection);
}

Tetris::~Tetris()
{
    delete ui;
}

void Tetris::onTimeStep(int board[20][10])
{
    //receive new board update my display
}

void Tetris::on_action_NewGame_triggered()
{
    mainThread->start();
}

When I run this I get:

QObject::connect: Cannot queue arguments of type 'int[20][10]' (Make sure 'int[20][10]' is registered using qRegisterMetaType().)

I have looked into qRegisterMetaType and Q_DECLARE_METATYPE but I am not even remotely sure how to use them or even if I must use them. Can someone give the QT newbie some assistance?

RAM
  • 2,257
  • 2
  • 19
  • 41
Filipe Teixeira
  • 3,565
  • 1
  • 25
  • 45

2 Answers2

2

You can wrap the board data in a class. It won't work if you merely typedef'd it, since Qt will try to use non-array operator new to create instances of board data. The compiler will detect it and rightfully complain.

It's bad style to derive from QThread like you are doing and use it as a generic QObject. QThread is conceptually a thread controller, not a thread itself. See this answer for the idiomatic way to do it. Your GameThread should be a QObject, not a QThread.

So:

struct Board {
  int data[20][10];
}
Q_DECLARE_METATYPE(Board);

int main(int argc, char ** argv)
{
   QApplication app(argc, argv);
   qRegisterMetatype<Board>("Board");

   ...

   Game * game = new Game;
   QThread thread;
   game->connect(&thread, SIGNAL(started()), SLOT(start());
   game->connect(game, SIGNAL(finished()), SLOT(deleteLater()));
   thread.connect(&game, SIGNAL(finished()), SLOT(quit());
   game.moveToThread(&thread);

   thread.start(); // you can start the thread later of course
   return app.exec();
}

class Game: public QObject
{
QTimer timer;
Board board;
public slots:
   void start() {
     connect(&timer, SIGNAL(timeout()), SLOT(tick()));
     timer.start(1000); // fire every second
   }
   void finish() {
     timer.stop();
     emit finished();
   }
protected slots:
   void tick() {
      ... // do some computations that may take a while
      emit newBoard(board);
      // Note: it probably doesn't apply to trivial computations in
      // a Tetris game, but if the computations take long and variable
      // time, it's better to emit the board at the beginning of tick().
      // That way the new board signal is always synchronized to the timer.
   }  
signals:
   void newBoard(const Board &);
   void finished();
}
Community
  • 1
  • 1
Kuba hasn't forgotten Monica
  • 95,931
  • 16
  • 151
  • 313
  • Hi Kuba. I have tried what you have suggested and I have hit a snag. . . The tick() is only ever called once so I don't think my timer is working the way it should since it should be emitting a timeout every second I am guessing. Any suggestions? P.S. Thanks for correcting me on my usage of QThread. I must have used an old tutorial. – Filipe Teixeira Jun 21 '12 at 15:18
  • Nevermind. . . Made a typing error in defining my connections. Was connecting directly to tick() not start() – Filipe Teixeira Jun 21 '12 at 15:23
0

What would happen if later you decided to change the size of the board? I think it would be better to encapsulate the concept of a board in an object, and pass around a pointer to said object.

Brady
  • 10,207
  • 2
  • 20
  • 59