Methode einer abgeleiteten Klasse von QGraphicsItem aufrufen

Alles rund um die Programmierung mit Qt
Antworten
Gruwe
Beiträge: 21
Registriert: 14. April 2011 18:15

Methode einer abgeleiteten Klasse von QGraphicsItem aufrufen

Beitrag von Gruwe »

Hallo,

ich bin derzeit dabei, eine Art Schaltplan-Editor in Qt zu programmieren. Das mache ich mit einer QGraphicsScene. Ich habe mir mehrere eigene Klassen, die von QGraphicsItem abstammen, abgeleitet, die die entsprechenden Elemente darstellen. So zum Beispiel einen Resistor.

Die Elemente lassen sich bisher ohne Probleme platzieren. Nun möchte ich Verbindungsleitungen programmieren, was auch soweit schon funktioniert. Um diese Verbindungen jedoch genau platzieren zu können, möchte ich eine Art Fang implementieren, also eine Funktion, wenn man in der Nähe eines Anschlusses des entsprechenden Elements klickt um die Verbindung zu platzieren, die Leitung direkt am Anschluss platziert wird.

Dazu hab ich dem Gebiet, dass dem GraphicsItem angehört zwei Rechtecke zugewiesen (um die Anschlusspunkte herum). Klickt man nun, so überprüft QGraphicsScene, ob sich die Maus an einem Element befindet. Ist dies der Fall, soll eine Methode des entsprechenden Items aufgerufen werden, die überprüft in welchem der beiden Rechtecke sich der angeklickte Punkt befindet und gibt dann den Punkt zurück, der dem entsprechenden Anschluss entspricht.

Das Ganze sieht so aus, am Beispiel des Elementes Resistor:

Deklaration der Klasse

Code: Alles auswählen

class ElementResistor : public QGraphicsItem
{

public:
    ElementResistor();

    QRectF boundingRect() const;

    void paint ( QPainter *painter,
                 const QStyleOptionGraphicsItem *option,
                 QWidget *widget );


    enum { Type = UserType + 1 };

        int type() const
        {
            // Enable the use of qgraphicsitem_cast with this item.
            return Type;
        }


private:
    QRectF rectNodeA;
    QRectF rectNodeB;
    QPoint pointNodeA;
    QPoint pointNodeB;

    void setRects();
    void setNodes();

public:
    QPointF assignPoint( QPointF );

};
Und hier die Implementierung der Methode assignPoint:

Code: Alles auswählen

//Methode zur Überprüfung, in welchem Rechteck sich der Übergabewert befindet
QPointF ElementResistor::assignPoint(QPointF thisPoint)
{
    //Punkt in Koordinatensystem des Elements umrechnen
    thisPoint = mapFromScene(thisPoint.x(), thisPoint.y() );

    //Wenn sich Punkt im Rechteck des Knoten A befindet
    if( rectNodeA.contains( thisPoint ) )
    {
        //den in Scene-Koordinaten umgerechneten Punkt von Knoten A zurückgeben
        return mapToScene( pointNodeA.x(), pointNodeA.y() );
    }

    //ansonsten befindet sich Punkt im Rechteck von Knoten B
    if( rectNodeB.contains( thisPoint) )
    {
        //und der Punkt von Knoten B wird zurückgegeben
        return mapToScene( pointNodeB.x(), pointNodeB.y() );
    }
}
Und hier jetzt der Aufruf in der Zeichenfunktion der Verbindung:

Code: Alles auswählen

//Wenn der angeklickte Punkt auf einem Scene-Item befindet
if ( scene->itemAt( mapToSceneWithGrid( event->x(), event->y() )))
{
          //Pointer des entsprechenden Items in ItemA einlesen
           ItemA = scene->itemAt( mapToSceneWithGrid(event->x(), event->y()));
                        
           //und den Pointer von QGraphicsItem* zu ElementResistor* casten
           ElementResistor *actualResistor = qgraphicsitem_cast<ElementResistor*>(ItemA);

           //und die Methode zum Zuweisen des Punktes des Elements aufrufen
           pointA = actualResistor->assignPoint( pointA );
}
So, soweit so gut. Jetzt zu meinem Problem!
Ich habe jetzt zwei Resistors platziert. Ich wähle die Funktion zum Platzierung einer Verbindung aus und klicke in den Bereich eines Knotens von Resistor1. Alles verläuft soi gewünscht, die Verbindung wird direkt am Anschluss angebracht. Klicke ich jedoch, um die Verbindung zu vervollständigen den zweiten Resistor an, erhalt ich einen Laufzeitfehler. Klicke ich in einen freien Bereich, in dem diese if-Anweisung nicht ausgeführt wird, stürzt das Programm nicht ab.

Es muss also was mit dem Aufruf der Methode nicht funktionieren. Ich nehme stark an, dass es etwas mit diesem Code-Abschnitt zu tun hat:

Code: Alles auswählen

enum { Type = UserType + 1 };

        int type() const
        {
            // Enable the use of qgraphicsitem_cast with this item.
            return Type;
        }
Das hab ich nämlich so aus der Doku kopiert, hab aber bisher noch nicht wirklich Ahnung, was dieser Code überhaupt macht bzw. machen soll.

Kann mir hierbei irgendjemand helfen, das Problem zu lösen?


MfG und Danke
franzf
Beiträge: 3114
Registriert: 31. Mai 2006 11:15

Re: Methode einer abgeleiteten Klasse von QGraphicsItem aufr

Beitrag von franzf »

qgraphicsitem_cast liefert NULL zurück, wenn er schief geht. Das MUSST du abfangen, wenn du keinen SegFault haben willst.
Es gibt übrigens QMouseEvent::pos(), damit kannst du dir das ewige x(), y() sparen ;)
Gruwe
Beiträge: 21
Registriert: 14. April 2011 18:15

Re: Methode einer abgeleiteten Klasse von QGraphicsItem aufr

Beitrag von Gruwe »

Ok, danke erstmal!
franzf hat geschrieben:qgraphicsitem_cast liefert NULL zurück, wenn er schief geht. Das MUSST du abfangen, wenn du keinen SegFault haben willst.
Aber warum liefert dieser NULL zurück? Der Code wird ja sowieso nur dann ausgeführt, wenn die if-Abfrage TRUE ist, also wenn sich der Mauszeiger über einem QGraphicsItem befindet. Wie kann dieser dann NULL zurückliefern?

Ich habs jetzt mal mit static_cast probiert, das scheint zu funktionieren. Jedoch ist mir der Unterschied noch nicht klar zu qgraphicsitem_cast!

Dann hätte ich noch eine zweite Frage:

Kann ich irgendwie feststellen, um welchen Typ der Custom-Items, die ich angelegt habe, es sich handelt?
Weil wenn ich einfach nur auf ein Item klicke, dann kann es sich z.B. um einen Resistor oder um ein Ground handeln. Entsprechend hab ich verschiedene abgeleitete Klassen, z.B.

Code: Alles auswählen

ElementResistor : public QGraphicsItem

oder

ElementGround : public QGraphicsItem
.

Jetzt muss ich ja wissen, welches Item angeklickt wurde, ob ein Resistor oder ein Ground. Von der QGraphicsScene erhalte ich ja nur einen Pointer auf ein QGraphicsItem, das ich dann entsprechend in einen Pointer auf ElementResistor oder auf ElementGround casten muss. Kann ich das irgendwie überprüfen?

Dann wäre noch wichtig:
Benötige ich dieses "enum" in der Klassendeklaration denn wirklich?


MfG und Danke im Voraus
ceumern
Beiträge: 28
Registriert: 16. September 2011 11:25

Re: Methode einer abgeleiteten Klasse von QGraphicsItem aufr

Beitrag von ceumern »

Note: To make this function work correctly with custom items, reimplement the type() function for each custom QGraphicsItem subclass.
http://doc.qt.nokia.com/latest/qgraphic ... sitem_cast

Das könnte helfen...
Jetzt muss ich ja wissen, welches Item angeklickt wurde, ob ein Resistor oder ein Ground. Von der QGraphicsScene erhalte ich ja nur einen Pointer auf ein QGraphicsItem, das ich dann entsprechend in einen Pointer auf ElementResistor oder auf ElementGround casten muss. Kann ich das irgendwie überprüfen?
So z.B. (benötigt RTTI):

Code: Alles auswählen

ElementResistor *resistor = dynamic_cast<ElementResistor *>(pointer_from_QGraphicsScene);
if (resistor)
    qDebug() << "It's a resistor!"
franzf
Beiträge: 3114
Registriert: 31. Mai 2006 11:15

Re: Methode einer abgeleiteten Klasse von QGraphicsItem aufr

Beitrag von franzf »

qgraphicsitem_cast schaut genauso wie qobject_cast oder der hoffentlich bekannte dynamic_cast, ob das übergebene Objekt auch wirklich in den gewollten Typ gecastet werden kann. dynamic_cast ist dabei recht langsam, weil es über RTTI den tatsächlichen Typ abfragt. Qt bastelt mit qobject_cast und qgraphicsitem_cast eigene Lösungen, die sich aber genauso verhalten wie dynamic_cast: Ist das übergebene Objekt NICHT in den gewünschten Typ konvertierbar, wird NULL zurück geliefert. static_cast überprüft da nichts. Wenn es kompiliert - fein. Passt aber zur Laufzeit was nicht kann es ganz böse crashen, oder nicht, es kann nen Bluescreen geben, dein Monitor kann sich ins Schlafzimmer von Berlusconi beamen, dein Vater mit den Kleidern deiner Mutter in der Arbeit auftauchen oder die Welt untergehen - typisches "undefined behaviour"!
Damit sollte dir auch klar sein, dass nur weil an der Stelle ein QGraphicsItem liegt (dein "if"), dieses item noch lange nicht von deinem gewünschten Typ sein muss.
Dass es jetzt mit dem static_cast läuft ist Zufall!

Um festzustellen, von welchem CustomType dein Item ist, hast du doch gerade den Tanz mit dem "virtual int type() const" in deinen Kindklassen gemacht - frag nach "item->type()" und du hast deinen Type. Wenn du aber eh schon mit qgraphicsitem_cast am hantieren bist, kannst du den konkreten Typ auch an dem return festmachen - "NULL" -> nein, ansonsten -> "Bingo".
toba
Beiträge: 28
Registriert: 22. September 2011 09:28

Re: Methode einer abgeleiteten Klasse von QGraphicsItem aufr

Beitrag von toba »

Hi, hatte das gleiche Problem.

Im HEader der Abgeleiteten Klasse einfach

Code: Alles auswählen

enum { Type = UserType +1 };            // Type = 65536 +1
     int type() const { return Type; }
hinzufügen.

Abfrage später hab ich so gelöset:

Code: Alles auswählen

if (Item->type()==65537)         //UserType +1
    {
        CLagen *Itemcast;
        Itemcast = qgraphicsitem_cast<CLagen *> (Item);
     }
Falls du mit einer QGraphicsScene arbeitest benutze die scenePos wenn du itemAt aufrufst. Sonst können fehler entstehn
Antworten