How to avoid cast using QToolBox? - c++

I know that cast should be avoided and I´m trying to do it, but I can´t see how to do it using a QToolBox.
The window I´m building has a QToolBox for the user chose which operation he wants to do and inside the toolbox are the specific parameters to each operation. The apply button is outside the QToolBox.
When the user clicks the apply button I have to get which operation he has chosen and its parameters.
The problem is that QToolBox currentWidget() method returns a QWidget that is a class that I can´t change. So I can´t use virtual methods or something like that. The only way I see to get the parameters is using cast.
Here is some code to show my problem:
class BaseOperation : QWidget {
public:
virtual int getParameter() = 0;
}
class Operation1 : public BaseOperation {
...
}
class Operation2 : public BaseOperation {
...
}
...
_ui->toolBox->addItem(new Operation1(this), "OP 1");
_ui->toolBox->addItem(new Operation2(this), "OP 2");
...
QWidget* curItem = _ui->toolBox->currentWidget();
BaseOperation* op = dynamic_cast<BaseOperation*>(op);
op.getParameter();
Is there a better way to do what I want? I thought of using the item index in the toolbox and a hash map to do it, but this does not seem very OOP.

You can track widget changes, map index to concrete Operation class (not using hashmap, but simple switch-case) and then static_cast<> it, but that's what dynamic cast does for you, basically.
Using dynamic cast is perfectly OK in this case, IMO.

A possible different approach that avoids casts entirely is to use setProperty("name", QVariant(value)) to associate the data you need to the widgets, and then get the data back from the current widget using property("name").toInt() - this avoids casts and defining separate classes if what you need is an integer value (or something else reasonably simple)

Related

Get object from signal function being called

Is possible to get the object of the control (button, checkbox, etc) which belongs
to the function being called other than declaring her 'type' ?
Example:
void Main::on_toolButton_pressed()
{
QToolButton *btn = qobject_cast<QToolButton*>(sender());
}
To something like:
void Main::on_toolButton_pressed()
{
auto *btn = qobject_cast<??>( sender() );
}
where ?? is something containing the button type.
You want to have a mix of compile time knowledge with runtime knowledge. That is not possible. What you can do is perform some "reflection stuff" using qt meta functionality like calling invokeMethod doc.qt.io/qt-6/qmetaobject.html#invokeMethod. Check QMetaObject class and what you can do. Basically if you have QObject there is a possibility to query class name, members, fields etc. If you are willing to write a code that will then cast your pointer to specific class based on moc introspection, then you will have perfectly working c++ code.

Qt C++ creating an Object according to choice in combobox

Since I am not very experienced in the design of object orientated code, I have the following question:
I have a combobox cb_GRAN with 21 entries representing 21 geometries. According to the choice made in cb_GRAN I want start different calculations of the surface of my geometry (actually there is more: more complicated calculatins, on/of-switching of LineEdits etc. but to keep it simple let`s just talk about the calculation of the surface. )
To solve this problem I created a class Geometry and 21 Classes (Geo_1, Geo_2,...Geo_21) which inherit from Geometry. I also have a virtual method
virtual void calculateSurface();
To define which class Geo_x is to create and to calculate the surface I came up with the following idea:
Geometry *GEO;
QVector < Geometry*> qvec_GEO
qvec_GEO << new Geo_1()<< new Geo_2()<< new Geo_3()<<...... << new Geo_21() ;
GEO = qvec_GEO[ui->cb_GRAN->currentIndex()];
GEO->calculateSurface();
Would that be a doable approach to solve my problem or would I run into problems when doing so?
Is it a good idea to create as much as
21 objects when I would need just one?
Question 1:
Your solution will probably work well (only based on information you provided).
Question 2:
You're totally right, creating instance of all your 21 class if you are going to use only 1 or 2 of them is probably an overkill.
You have to find a solution to instantiate only the needed class. Qt provide a Meta-object system you could use for this. Basically, you will have to use the QMetaType class.
First, you have to register the "Geo_N" classes in the meta-object system. You have many solutions to do that, but maybe the best in your case is to use the declarative macro after your class definition:
class Geo_1 {
Q_OBJECT
void Geo_1();
};
Q_DECLARE_METATYPE(Geo_1);
Please note that Q_OBJECT macro is mandatory if you want to register a class in the meta-object registry.
Then, you will be able to instantiate any registered type dynamically:
// The index of selected class (between 1 and 21)
int geoIndex = ui->cb_GRAN->currentIndex();
// Re-build the corresponding class name
QString typeName = "Geo_" + QString::number(geoIndex);
// Retrieve the type ID corresponding to the type name
int typeId = QMetaType::type(typeName.toStdString().c_str());
// Instantiate the corresponding class
Geometry* geo = (Geometry*) QMetaType::construct(typeId);
My Approach would be to define a factory class for your geometries
class GeoFactory{
enum GeometryType { GEO_1, GEO_2, ... );
std::uique_ptr<Geometry> create( GeometryType type );
}
And then add the GeometryType to your combobox items in the data role. On the current changed event you just retrieve the GeometryType from the current index and request the corresponding object from the factory.
Personally i always try to keep out Qt from the actual business logic, i just use for UI.

How to implement UserControl in WinRT

I have created a simple UserControl consisting solely of a Grid and an embraced Image.
Now I want to apply events such as "ManipulationDeltaEvent", etc. for touch-control. When I assign an event-handler like
pic->ActionToken = pic->ManipulationDelta +=
ref new ManipulationDeltaEventHandler(this, &MainPage::SwipeImageEventHandler);
pic->CompletedToken = pic->ManipulationCompleted +=
ref new ManipulationCompletedEventHandler(this, &MainPage::ImageManipulationCompletedEventHandler);
I receive valid EventRegistrationTokens, but when I want to swipe over the control, simply nothing happens (I debugged).
I read about overriding the OnManipulationDelta-method from Windows::UI::Xaml::Controls::Control, but I here I am stuck:
protected:
void OnManipulationDelta
(Windows::UI::Xaml::Input::ManipulationDeltaRoutedEventArgs^ e) override {
}
Although only barely related, for C++\CLI it states on MSDN:
The OnManipulationDelta method has no default implementation. Override OnManipulationDelta in a derived class to handle the ManipulationDelta event. Be sure to call the OnManipulationDelta method of the base class so that base classes receive the event.
Please give me a hint, thank you.
EDIT
The overriding is unnecessary
You need to specify ManipulationMode on the control and the control needs a non-null Background or Fill, e.g. Background="Transparent".

Simple acces of C++ object data from QML

I'm making a board game in Qt/C++ using qml. All the important game data is represented in a single class. My intention is to have the qml sheets access this one object and draw the game board depending on the data.
What is the simplest approach to exposing the C++ data members to QML?
Now I know the question has been asked, I've seen the answers and the Qt Documentation. I am, however, not satisfied. What I've seen, the way to do this seems to be to make Q_PROPERTY out of every single variable I want to access from QML. This looks to me tedious and unnecessary, not to mention it will stretch the code to 3x it's original length, making it significantly worse to read. Also, in most cases I won't need write function to the data members, for example.
And why bother with Q_PROPERTY overhead when I could just write Q_INVOKABLE getters for just the situations I need?
Here's an example of how simple I hoped it would be when I read in the Qt Project documentation: "This enables C++ data and functions to be accessible directly from QML, often with little or no modification."
class game : public QObject
{
Q_OBJECT
public:
explicit game(QObject *parent = 0);
colors NPC[3]; // colors being an enum declared elsewhere
player players[4]; // player is a non-QObject class containing player stats
}
...
game gMain;
QDeclarativeContext *context = viewer.rootContext();
context->setContextProperty("Game",&gMain);
QML in my ideal world:
Image {
id : Image1
source: { if (Game.NPC[0] === 0) {
if (Game.players[1].LifeCount > 0) {
return "pics/FigG.png"
}
else {
return "pics/StoneG.png"
}
}
Now how close to that can I actually get with QML and how do I go about it?
I'm especially interested in handling simple C++ style arrays and enums (have a lot of those in the game) - would I need to write helper functions, e.g. int Game.GetNPCAt(int i) instead of using just Game.NPC[i] ?
I realize that the way I DON'T want to do it is the tried and trusted, and for good reason... however in my situation (small one-man project) it seems like using a cannon to kill a fly (although the GUI building part in qml is amazingly simple and quite a joy to use) - also having to wrap around every data member including the simplest like an int seems... ridiculously excessive.
Maybe I have missed something somewhere, In which case I humbly apologize. Thank you for any thoughts on the matter.
In order:
Q_PROPERTY: When you look at the page that you quoted, they discuss using the Q_PROPERTY method to expose properties to QML. If you don't use Q_PROPERTY, it is my understanding that your variables won't be registered by QMLViewer (or what have you). The Q_PROPERTY needs a Q_INVOKABLE to get/set your variables. If you don't use Q_PROPERTY, though, your class properties will not appear in QML.
Setting the image source: If you may remember, QML is a forge between CSS and JavaScript. If you're just looking to make the image's source change depending on a condition outside of your Image element, you can just create a JavaScript function to achieve what you have quoted:
Image {
id: Image1
function getImage()
{
if (Game.NPC[0] === 0)
{
if (Game.players[1].LifeCount > 0) {
Image1.source="pics/FigG.png";
}
else {
Image1.source="pics/StoneG.png";
}
}
}
However, the function won't run by itself: you'll have to associate it with a signal, which I would create in your C++ class (put the function under a label named signals: (NOT within public -- see here on how to write signals)). Based on your example, I'm guessing that your C++ object is called Game.
Game {
id: gameKeeper //or whatever you want to name it
onUpdate: Image1.getImage() //replace onUpdate with your signal
}
Image {
id: Image1
function getImage()
{
if (gameKeeper.NPC[0] === 0)
{
if (gameKeeper.players[1].LifeCount > 0) {
Image1.source="pics/FigG.png";
}
else {
Image1.source="pics/StoneG.png";
}
}
}
In theory, you should be able to reference arrays this way with JavaScript (I'm not all that familiar with JS myself).
Array handling: On the C++ side, it looks like the best way to do it is through a QList. Fortunately, a QList iterates very similar to a normal array. I found this, which should help -- just ignore the second dimension.
Hope this helps.

C++/Qt - multiple inheritance with QGraphicsItem doesn't work as expected

I recently met a strange problem of my little program and it would be great if you help me to get the reason of this behavior.
My task is quiet simple - I want to use Qt Graphics Framework to show some objects and I want Box2D to calculate bodies position. So my class hierarchy looks like the following:
I have 1 base abstract class B2DObject. It contains some Box2D staff + some common parameters for its successors (names, some flags, etc.). It also has couple of pure virtual functions that will be reimplemented in successor classes.
Then I implement some classes that represent basic shapes: circles, rectangles, polygons, etc. I am doing it in the following way:
class ExtendedPolygon : public B2DObject, public QGraphicsPolygonItem { ... };
class ExtendedCircle : public B2DObject, public QGraphicsEllipseItem { ... };
etc.
(for those who are not familiar with Qt, QGraphics***Item is inherited from QGraphicsItem).
Also I inherited QGraphicsScene and reimplemented its mousePressEvent. In this function I request an object placed at some point on the screen using QGraphicsScene::itemAt function (which returns QGraphicsItem*), convert it to B2DObject* and try to get some internal field from this object:
void TestScene::mousePressEvent (QGraphicsSceneMouseEvent *event)
{
QGraphicsItem* item = itemAt (event->scenePos ());
if (item)
{
B2DObject* obj = reinterpret_cast < B2DObject* > (item);
QString objName = obj->Name(); // just for example,
// getting other internal fields has
// the same effect (described below)
// use retrieved field somehow (e.g. print in the screen)
}
// give the event to the ancestor
}
Unfortunately, dynamic_cast will not work here because these classes are completely unrelated.
Then I create necessary objects and add it to my scene:
ExtendedPolygon* polygon = new ExtendedPolygon (parameters);
polygon->setName (QString ("Object 1"));
...
TestScene scene;
scene.addItem (polygon);
(for those who are not familiar with Qt, here is the prototype of the last function:
void QGraphicsScene::addItem(QGraphicsItem *item);
I guess it just stores all items in internal index storage and calls QGraphicsItem::paint (...) when item needs to be repainted. I suppose QGraphicsScene doesn't make any significant changes to this item).
So my problems start when I run the program and click on an item on the screen. TestScene::mousePressEvent is called (see a piece of code above).
Mouse click position is retrieved, item is found. Casting works fine: in the debugger window (I'm using Qt Creator) I see that obj points to ExtendedPolygon (address is the same as when I add the item to the scene and in the debugger window I can see all the fields). But when I get some field, I receive garbage in any case (and it does not matter, what I'm trying to get - a QString or a pointer to some other structure).
So first of all, I would like to get any advice about my multiple inheritance. In 95% of cases I try to avoid it, but here it is very effective in the programming point of view. So I would appreciate it if you provide me with your point of view about the architecture of the classes hierarchy - does it even suppose to work as I expect it?
If on this level everything is quite fine, then it would be great if someone gets any idea why doesn't it work.
I have some ideas about workaround, but I really would like to solve this problem (just in order not to repeat the same error anymore).
Looks like I've found the root cause of my problem. It was just lack of knowledge regarding how multiple inheritance really works on data layer.
Let's assume that we have 2 basic classes, A and B. Each of them provides some internal data fields and some interfaces.
Then we create a derived class AABB, inheriting both A and B:
class AABB : public A, public B {...}
AABB could add some additional data fields and reimplement some of the interfaces, but it is not necessary.
Let's create and object of class AABB:
AABB* obj = new AABB ();
For example, obj points at address 0x8416e0. At this address starts data from ancestor class A. Data from ancestor class B starts with some offset (it should bw equal to sizeof (A)), for example, at 0x841700.
If we have some function f (B* b), and if we pass a pointer at AABB object to that function (like this: f (obj), obj is created above), actually not obj start address is passed, but rather a pointer at a start of B data section of AABB object.
Thus this misunderstanding of multiple inheritance inner works has led me to the problem I've got.
I guess Qobjects and multiple inheritance has been already treated. As an example: QObject Multiple Inheritance