PDA

View Full Version : QGraphicsScene and QGraphicsWidget (reusable and embeddable)



OriginalCopy
4th April 2011, 11:57
I should start by saying I've tried to look at this problem from all POVs. I've also asked on freenode and I was suggested some things, yet in the end it turned out those suggestions could not possibly work.

It looked to me as though those making suggestions don't really know how the class hierarchy and the concepts hierarchy looks like.

That being said, I haven't used Qt for years, so please bear with me.

What I want to make is a graphics item (QGraphicsWidget,...Item, whatever, I don't know what class it should inherit from) that can sit on the scenery QGraphicsScene.

Imagine you wanted to create a reusable component for grid-based games like battleship (http://en.wikipedia.org/wiki/Battleship_%28game%29).

New objects (like a battleship) within this widget could be represented as bit matrices and created/inserted on the fly.

The building blocks themselves (filled QGraphicsRectItem or "empty" ones) inside it should be managed by a layout class. As a default could serve QGraphicsGridLayout, but it should allow plugging in a custom layout. This would allow new 2D layouts, like isometric.

Having a layout inside the widget would also simplify game development on top of this widget, as you'll have methods itemAt() and layers to organize the objects on the "grid".

And remember, the widget itself can sit inside the scenery.

So which concepts (grid widget, grid block, grid layout) should inherit from which qt4 (4.7.2) classes?

Note: already exiting classes/frameworks/whatever are also OK, but keep in mind I don't want to have to distribute libraries thicker than 3-4 Mb for windows users.

wysota
4th April 2011, 12:05
What's the rationale behind using QGraphicsScene in this case? To me it seems the grid based model-view approach seems suitable here. Of course you can merge the two or build your own model-view architecture on top of graphics view but maybe you don't have to. Tell us what exactly do you expect from this framework functionality-wise.

OriginalCopy
4th April 2011, 12:10
I don't want to put limits on the end user (the game developer). Are you suggesting that the whole widget should best be a QGraphicsScene, and the user could hook in by either using signals and slots, or by subclassing?

Addendum: perhaps the grid itself is only one thing in the entire scenery. perhaps the scenery of the game he's developing requires more than one grid.

That's why I think the widget itself should be part of the scenery, not the scene itself.

wysota
4th April 2011, 12:21
QGraphicsScene is not a widget.

You say you don't want to put limits on game developers. But you need to expose some abstract API and this in turn will put limits on them whether you want it or not. So my question is what do you want to add to QGraphicsScene that it doesn't already have? If all you want is to add some kind of "layout" then subclass QGraphicsScene and add virtual methods similar to what QAbstractItemView has -- indexAt() and visualRect(). This will allow your users to implement custom mappings between indexes (i.e. the grid) and items in the scene. Although from the technical point of view this is not necessary as the scene coordinate system can already map grids by itself. How you display it afterwards is a different issue.

OriginalCopy
4th April 2011, 12:32
When I said "my grid should sit on the scenery" I ment the child-parent-relationship: my grid widget will have as parent the QGraphicsScene. It should not be a subclass of QGraphicsScene for the reasons I've mentioned above.


QGraphicsScene is not a widget.

I know and I have never said that.

wysota
4th April 2011, 12:33
my grid widget will have as parent the QGraphicsScene
Very unlikely as widgets can't have non-widgets as parents.


It should not be a subclass of QGraphicsScene for the reasons I've mentioned above.
Could you point out those reasons again? I don't see anything in what you have written so far that would make subclassing the scene a bad idea.

OriginalCopy
4th April 2011, 12:46
I've put it in the title because it's very important: "(reusable and embeddable)" - the grid should be a reusable widget, easy to embed into any scene, even in large number (one scene contains more "grids").

The natural way of working would be:

- create new QGraphicsScene
- create several new MyGrids
- create some new "objects" (they would be basically QObject subclasses, with some extra features) via bit matrices for each
- put those several new MyGrids on the scene

MyGrid would be the class representing the concept of "grid" I've talked about in post #1. It has as children some rectangles managed in turn by a layout (default: QGraphicsGridLayout) for the reasons I've also mentioned in post #1.

The question is just: What should MyGrid subclass, such that I can reuse as much as possible of the features already existing qt classes provide, to let the user (the real game developer) easily create and manipulate several MyGrid instances as his game requires.

I hope it's clear now.

wysota
4th April 2011, 13:24
I've put it in the title because it's very important: "(reusable and embeddable)" - the grid should be a reusable widget, easy to embed into any scene, even in large number (one scene contains more "grids").
So you don't want a widget but rather an item. Or maybe subclasses of QGraphicsLayout.


The question is just: What should MyGrid subclass, such that I can reuse as much as possible of the features already existing qt classes provide, to let the user (the real game developer) easily create and manipulate several MyGrid instances as his game requires.

QGraphicsLayout or one of its subclasses, most probably. Although I less and less see what would such grid be useful for and how would it differ from QGraphicsGridLayout.

OriginalCopy
4th April 2011, 14:24
First of all it must be a QGraphicsItem, because:


QGraphicsScene::addItem ( QGraphicsItem * item )

QGraphicsLayout does not seem to be a subclass of QGraphicsItem.


Although I less and less see what would such grid be useful for and how would it differ from QGraphicsGridLayout. It's about the "little extra" I haven't dived into.

So, can anyone make a real,feasible suggestion, without throwing with classnames at me, please?

wysota
4th April 2011, 14:41
First of all it must be a QGraphicsItem, because:


QGraphicsScene::addItem ( QGraphicsItem * item )


QGraphicsWidget *w = new QGraphicsWidget;
w->setLayout(new MyGridLayout);
So it doesn't have to be a QGraphicsItem.


It's about the "little extra" I haven't dived into.
Then maybe you shold dive into it first before you start thinking what class to derive from. There is a really simple question here which you have to answer - what is the thing you are designing to do that QGraphicsGridLayout doesn't already do?

OriginalCopy
4th April 2011, 15:19
QGraphicsWidget *w = new QGraphicsWidget;
w->setLayout(new MyGridLayout);
So it doesn't have to be a QGraphicsItem.


Then maybe you shold dive into it first before you start thinking what class to derive from. There is a really simple question here which you have to answer - what is the thing you are designing to do that QGraphicsGridLayout doesn't already do?
Specifying objects inside the grid (e.g. a "battleship") via matrices, positioning/rotating those objects on the grid (which are graphically represented as a collection of rectangle objects), receiving signals from these objects (e.g. "battleship") instead of the individual reclangles battleships are made of.

So yeah, the grid layout looks like a good candidate, except ... you cannot put more of these on the scene.

The whole thing is for a gaming platform, so it's imperative to have one "widget" which can be manipulated as a standalone entity (friendliness for game developers, one unique API - at some point I may well want to expose this component to scripting languages like python, so it really has to be one object which can be put on the scene).

wysota
4th April 2011, 15:37
Specifying objects inside the grid (e.g. a "battleship") via matrices, positioning/rotating those objects on the grid (which are graphically represented as a collection of rectangle objects), receiving signals from these objects (e.g. "battleship") instead of the individual reclangles battleships are made of.
You can do that via any QObject, it doesn't have to be tied to QGraphicsScene in any way hence my original question - what is the rationale behind binding yourself with QGraphicsScene. The fact that a "battleship" is implemented using graphics items (or not) is completely irrelevant. What is relevant is that it emits some signals and has some slots. These will be different for each game so I don't see how your "grid" fits in here. Furthermore usually "pieces" are not standalone objects but are controlled by some kind of engine. Consider chess - you have a board which is a matrix of game pieces. You can move the pieces on the board and capture pieces with other pieces. Yet it is the board (the engine) that decides whether a particular move is legal, where each item is to be positioned, etc. Such items will not emit signals or contain slots as the whole logic is in the engine and not the item.



So yeah, the grid layout looks like a good candidate, except ... you cannot put more of these on the scene.
Hmm? You cannot put more of what on the scene? grid layouts? Why not? You can place as many of them as you want.


The whole thing is for a gaming platform, so it's imperative to have one "widget" which can be manipulated as a standalone entity (friendliness for game developers, one unique API - at some point I may well want to expose this component to scripting languages like python, so it really has to be one object which can be put on the scene).
According to my experience in games, boards and things like that, what you call a "widget" I would call a "manager" and I would not make it a visual item (widget, graphics item, whatever). It would just manipulate visual items. A game of chess is a game of chess regardless if you display the board somewhere or not. With what you are trying to do, you'd have to display the board, otherwise you wouldn't have the board at all. That's hardly a universal approach. In my opinion you are looking for something like this:

class BoardItem : public QObject {
Q_OBJECT
public:
BoardItem(BoardEngine *);
virtual ~BoardItem() = 0;
void setPos(const QPoint &);
QPoint pos() const;
signals:
void positionChanged(QPoint);
};

class BoardEngine : public QObject {
Q_OBJECT
public:
void setSize(int w, int h);
void setItem(int x, int y, BoardItem *item);
signals:
void itemPositionChanged(BoardItem*);
};
etc.

OriginalCopy
4th April 2011, 16:05
Yep, I do intend to sepparate game logic from game view. What I need to be reusable is this manager, as you call it, which should be able to manage "tiles" belonging together (let's call this aggregation of tiles a "pawn") - it is just about the graphical representation, the piece (the qobject you are proposing) itself will take care of the logic and of updating this "pawn" within the manager (or rather, tell the manager update it - thus I think the best place for functions to graphically manipulate "pawns" in all kind of ways is in the manager).


Hmm? You cannot put more of what on the scene? grid layouts? Why not? You can place as many of them as you want.
How?

wysota
4th April 2011, 16:49
it is just about the graphical representation, the piece (the qobject you are proposing) itself will take care of the logic and of updating this "pawn" within the manager
It should work the other way round. The manager knows the logic of the game, it can delegate some of its resposibility to another object (which can happen to be the visual item drawn on the board but it doesn't have to be) but still the manager is in control of everything. If we continue with the chess parallel - if you want to add a new type of piece to the game then of course the logic of the piece can be tied to the component implementing the looks of the piece but it is still the game engine that asks a particular piece for possible destinations, determines the legality of each of them in the current game state and finally asks the piece to make the move. If you decentralize the logic (as if I correctly understand that that's what you propose) then you have dozens of places that you have to coordinate, dozens of places where something can go wrong, etc. Even if we consider your approach as a sort of an "agent-like" solution then there still has to be an agent controlling the game logic and the agent would be the manager.


How?
In general through QGraphicsLayoutItem API.

QGraphicsWidget *w = new QGraphicsWidget;
QGraphicsGridLayout *l1 = new QGraphicsGridLayout;
w->setLayout(l1);
QGraphicsGridLayout *l2 = new QGraphicsGridLayout;
l1->addItem(l2, 0, 0);
// ...
QGraphicsWidget *w2 = new QGraphicsWidget;
QGraphicsGridLayout *l21 = new QGraphicsGridLayout;
w2->setLayout(l21);
QGraphicsGridLayout *l22 = new QGraphicsGridLayout;
l21->addItem(l22, 0, 0);
// ...

OriginalCopy
4th April 2011, 17:02
It should work the other way round. The manager knows the logic of the game, it can delegate some of its resposibility to another object
[/code]

Yes, this "another object" is the one I want to implement and make reusable.

wysota
4th April 2011, 17:17
The API of this "another object" is strictly tied to the logic of the game. It will be different for chess and different for say... civilization clone.

For chess I would define something like:

class ChessPieceLogic {
public:
virtual ~ChessPieceLogic(){}
virtual QList<QPoint> possibleMoves(const QPoint &from, const BoardState &state) const = 0;
virtual QList<QPoint>attackedFields(const QPoint &from, const BoardState &state) const = 0;
virtual isValidMove(const QPoint &from, const QPoint &to, const BoardState &state) const = 0;
virtual bool isUnderAttackBy(const QPoint &source, const QPoint &target) const; // to check for check/mate
virtual QList<QPair<QPoint,QPoint> > makeMove(const QPoint &from, const QPoint &to, const BoardState &state) const = 0; // to allow en passant and castles
virtual void commitMove(const QPoint &from, const QPoint &to) = 0;
};

OriginalCopy
4th April 2011, 18:24
Yes, but drawing this "other object" and the objects inside it, manipulating those visual representations, reacting to UI events, will be handled (and forwarded) by this "other object". This "other object" is "the grid" i was talking about in post #1, and needs to have the features I've mentioned.

Now, have we cleared up any misunderstanding?

Do you (finally) have a real, constructive suggestion about THIS specific issue, without walking me through all the irrelevant things about this entire gaming platform? Because frankly, my problem is a QT issue, and not an overall design issue.

It would be great if you could help me with that, instead of divagating.

I do appreciate your input so far, but I would appreciate even more an on-topic discussion.

wysota
4th April 2011, 19:03
Because frankly, my problem is a QT issue, and not an overall design issue.
Frankly your problem is a design issue and not Qt issue. If you had a clear design, you'd immediately know how to implement it. So far either you don't have a clear design of what you want to do or you can't present/explain the design because you still haven't answered the question how your grid differs from QGraphicsGridLayout. It seems you have some general ideas but no specifics.


I do appreciate your input so far, but I would appreciate even more an on-topic discussion.
I think what I'm saying is as much on-topic as you can get.

Let's go through what you have written again:

drawing this "other object" and the objects inside it
When it comes to Graphics View then: 1) items draw themselves and 2) the "scene" has no visual representation. You're trying to force the "grid" to be an item where clearly there are many cases where it is either not desired or not possible.


, manipulating those visual representations,
If by that you mean scaling/rotating then the scene takes care of that, possibly supported by the animation framework and QGraphicsTransform if needed.

reacting to UI events,
This is already in the graphics view framework as well.


will be handled (and forwarded) by this "other object".
So far this "other object" seems like the scene to me.


This "other object" is "the grid" i was talking about in post #1, and needs to have the features I've mentioned.
It's already in the framework and works out of the box as you'd expect it. The scene captures events for the items and forwards them to the items so that they can handle them, the scene can layout items in a grid (or whatever else structure you want), it can visually transform them and it delegates painting to the items. So what's exactly wrong with this behaviour that you wish to change it?