Seite 1 von 2

QGraphicsScene, QGraphicsView, QGraphicsItem, Hilfe?

Verfasst: 2. März 2010 19:59
von t_b
Hallo,

Ich versuche mich jetzt schon seit 5 Tagen durch QGraphicsScene und QGraphicsView durchzubeißen, aber ich schaffe es nicht. Auch die Hier im Forum zu diesen Themen veröffentlichten Beiträge bzw. die in Qt mitgelieferten Beispiele lieferten den gewünschten AHA Effekt. Zudem muss ich sagen, dass ich ein Neuling in Qt/C++ bin, jedoch meine Studienarbeit darin machen muss (und OOOH, ich hab nur noch 3 wochen Zeit).

Was ich möchte:

Ich möchte ein Programm schreiben, in dem ich 1 Objekt (einen Pfeil) habe.
Dieser Pfeil soll ich in der Mitte des Bildschirmes Drehen, der Pfeil soll an einem der Vier Himmelsrichtungen Stehen bleiben und dann sofort verschwinden. Die Zeit des Drehens ist randomisiert. der Benutzer soll nun die Pfeiltaste drücken in welche der Pfeil zuletzt gezeigt hat. War dies Richtig, so soll der Pfeil erneut auftauchen, ansonsten das Programm beenden (eigentlich so nicht, aber zum Verständinis würde es reichen.

Was ich bisher verstehe:

Ich will das Ganze als Widget bauen, auch wenn es später im Vollbildmodus laufen soll. Ergo nehme ich mir eine neue Klasse Graphic mit graphic.h und graphic.cpp. Diese sähen dann folgendermaßen aus:

graphic.h

Code: Alles auswählen

#ifndef GRAPHIC_H
#define GRAPHIC_H

#include <QGraphicsView>
#include <QGraphicsScene>

class Graphic : public QGraphicsView
{
public:
    Graphic();

private
	QGraphicsScene *scene; // die Scene
	QGraphicsView *view; // die dazugehoerige View
	
	void keyPressEvent(QKeyEvent *event); // die Reimplementierung von keyPressEvent
};

#endif // GRAPHIC_H
graphic.cpp

Code: Alles auswählen

#include "graphic.h"
#include <QGraphicsView> // Frage: Muss ich auf diese erneut Verweisen, wenn ich es in "graphic.h" bereits getan habe?
#include <QGraphicsScene> // gleiche Frage
#include <QtGui>

Graphic::Graphic()
{
scene = new QGraphicsScene;
scene->addPixmap(QPixmap(":/images/pfeil_gruen.svg")); // in Resourcendatei angelegt; Frage: wie nutze ich Aliasnamen? mit :Aliasname ??

view = new QGraphicsView(scene,this); // die View ist mein "Betrachtungsfenster, mit der ich meine Scene betrachte, richtig??
view.resize(400,400); // setzt die Größe der View, nicht des Fensters
view.show();
}

void Graphic::keyPressEvent(QKeyEvent *event)
{
    if (event->key() == Qt::Key_Escape) // ist erstmal eine simple Exitstrategie
    {
        QApplication::quit();
    }
}
und wenn ich dann in der main.cpp das ganze Aufrufe

Code: Alles auswählen

#include <QtGui/QApplication>
#include <QWidget>
#include "graphic.h"

int main( int argc, char* argv[] ) {
   QApplication app( argc, argv );
   Graphic *w = new Graphic;
   w->show();
   return app.exec();
}
dann bekomme ich mein Fenster mit meinem Pfeil. Ich weiss, dass es Timer gibt, und dass ich diesen via Signal/Slot mit einem slot in der Graphic-Klasse verbinden kann, um dann in regelmäßigen Abständen die Scene zu verändern.
Allerdings habe ich es noch nicht erfolgreich geschafft eine solche Verbindung inklusive Slot zu erstellen.
Das nächste Problem ist genau dieser Slot, was ja nichts anderes ist, als eine Funktion.

Code: Alles auswählen

Graphic::animiere()
{
}
hier müsste ich nun auf die eingebundene Pixmap zugreifen, nur habe ich keine ahnung, wo ich die Referenz darauf hernehmen soll. Des Weiteren müsste ich die Grafik dann entsprechend drehen. Wäre es vielleicht sinnvoller die Grafik als ein QGraphicsItem (qgi) einzubinden, da diese die Methoden "rotate" und "setPos" besitzt. Nur wie binde ich dann hier die Grafik ein?
Zum Schluss müsste man dann hier

Code: Alles auswählen

scene.update(qgi);
ausführen?

Soweit erst einmal dazu. Ich hoffe ein Sachverständiger kann mir hier weiterhelfen, denn ich geh an dem Zeitdruck fast zu grunde (ich versuche mich schon seit 3 Wochen in das Thema (Grafk in Qt, erst QPainter und nachdem ich bemerkt habe, dass es nicht das ist was ich brauche, nun dieses) einzulesen, aber umsomehr ich lese, umso weniger glaube ich, dass ich das noch verstehe). Ich habe mir das aktuelle Buch zu Qt von GallileoComputing geholt, gut geschrieben, aber leider ist genau dieses Thema nicht dabei. Und ich habe dazu auch kein einfaches, verständliches Tutorial gefunden.


Liebe Grüße und völlig verzweifelt

Torsten

Verfasst: 4. März 2010 15:21
von mathies
Hallo,

fürs erste hast Du glaube das Scene -View Konzept nicht ganz verstanden und Dein Fragenkatalog ist auch etwas undurchsichtig.

Ein paar Punkte:
1. Ich bin mir nicht sicher ob Du einfach so SVG Daten laden kannst, png und jpg ist kein Problem.
2. Wenn Du eine Klasse von dem QGraphicsView ableitest, dann braucht das Ding nicht noch nen extra View, ist ja selbst schon einer.
3. Ganz einfach gesagt, jeder View nutzt eine Scene und in dieser Scene kannst Du Items platzieren, die man auch drehen kann.

Also wenn Du einen View erzeugt hast, dann erzeugst Du eine Scene und übergibst diese dem View. Dann kannste zb ein QGraphicsPixmapItem nutzen um Deinen Pfeil darzustellen oder noch besser, malst ihn dir selber indem Du das paint() eines QGraphicsItems überschreibst. Das Item hat dann zb eine Funktion zum rotieren. Das Item musst Du natürlich dann auch der Scene übergeben, damit es dargestellt werden kann.

Hoffe das hilft Dir etwas.

Verfasst: 4. März 2010 16:01
von t_b
Hallo, danke für die Antwort. Ich war leider noch nicht dazugekommen meine aktuellen stand zu schreiben. Dies hole ich hiermit nach.

1. Ja, man kann svg laden
scene->addPixmap(QPixmap(":/images/arrow_blue.svg"));
2. Ja, den Fehler habe ich mittlerweile korrigiert

3. Das wusste ich ja, aber ich wusste nicht, wie ich auf die einzelnen Items zugreife. Mittlerweile habe ich eine Möglichkeit gefunden.
itemList = scene->items();
nur dass ich noch nicht herausgefunden habe, wie ich auf spezielle items zugreifen kann (wenn ich z.B. eine PixmapItem und TextItem hinzufüge, wie ich auf das TextItem zugreife)

Ich bin derzeit hier:
#include "graphicswidget.h"
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QGraphicsItem>

#include <QVBoxLayout>
#include <QLabel>
#include <QList>
#include <QTimer>

GraphicsWidget::GraphicsWidget(QWidget *parent) :
QWidget(parent)
{
scene = new QGraphicsScene;
scene->setItemIndexMethod(QGraphicsScene::BspTreeIndex);
scene->addPixmap(QPixmap(":/images/arrow_blue.png"));
view = new QGraphicsView(scene);
view->setBackgroundBrush(Qt::black);
QVBoxLayout *layout = new QVBoxLayout;
QLabel *label = new QLabel("Das ist die QGraphicsView.");
layout->addWidget(view);
layout->addWidget(label);
setLayout(layout);
scene->update();
view->update();
QTimer *timer = new QTimer;
connect(timer,SIGNAL(timeout()),this,SLOT(rotate()));
timer->start(10);
itemList = scene->items();
}

void GraphicsWidget::rotate()
{

foreach(QGraphicsItem *item,itemList)
{
item->rotate(2);
}
scene->update();
view->update();
}
das PNG hab ich genommen, weil ich grad unter einem anderen System arbeite, und das da nicht "will" :evil:

Ich arbeite gerade daran den "Mittelpunkt des Bildes zu setzen, da das Bild derzeit nicht um diesen rotiert.

Verfasst: 4. März 2010 16:13
von mathies
Hallo,

Du musst Dir die Itempointer dann natürlich speichern, wenn Du Dir nicht eine eigene Itemklasse anlegst, wo der Typ drin steht.

zB:

Code: Alles auswählen

mPixmapItem = new QGraphicsPixmapItem("bild");
scene->addItem(mPixmapItem);
mTextItem = new QGraphicsTextItem("text");
scene->addItem(mTextItem);
Dann kannst Du in den Funktionen Deiner Klasse auf die beiden Items zugreifen ohne immer die Liste auszulesen.

Bezüglich der Scene wird das Problem sein, das er um den Punkt 0,0 rotiert und das sollte im Normalfall die linke obere Ecke Deines Bildes sein. Habe selbst die Scene noch nicht rotiert, kannst aber mal probieren das SceneRect anzupassen, dass der Punkt 0,0 in der Mitte Deines Bildes ist.

Verfasst: 4. März 2010 16:45
von t_b
Man kann mit

Code: Alles auswählen

direction->setOffset()
den Offset setzen, ich habe es mit einem png der Größe 128x128 getestet, funktioniert so:

Code: Alles auswählen

direction = new QGraphicsPixmapItem(QPixmap(":/images/arrow_blue.png"));
direction->setOffset(-64,-64);
einwandfrei, nur bei SVG nicht. Des Weiteren kommen da noch mehr "Probleme" hinzu. Das SVG lässt sich zwar skalieren, da ganze sieht dann aber so aus



EDIT:
Es gibt auch QGraphicsSvgItem, nur wird mir bei diesem ein Compilier-Fehler angezeigt: QGraphicsSvgItem: Datei oder Verzeichnis nicht gefunden, was mich jedoch sehr wundert, da dieses schon in qt 4.2 implementiert wurde.

Verfasst: 4. März 2010 17:32
von upsala
Das ist ein skaliertes Bitmap und keine skalierte Vektrografik. Der 0-Punkt eines SVG ist in der Grafik hinterlegt.

Verfasst: 4. März 2010 17:41
von t_b
ok, sowas hab ich mir schon gedacht, nur wie ich in meinem "EDIT" geschrieben habe, bekomme ich einen Fehler bei der Nutzung von QGraphicsSvgItem, und ich habe auch keinen blassen Schmimmer woran es liegt.

Verfasst: 4. März 2010 17:58
von franzf
t_b hat geschrieben:Es gibt auch QGraphicsSvgItem, nur wird mir bei diesem ein Compilier-Fehler angezeigt: QGraphicsSvgItem: Datei oder Verzeichnis nicht gefunden, was mich jedoch sehr wundert, da dieses schon in qt 4.2 implementiert wurde.
http://doc.qt.nokia.com/4.6/qtsvg.html
-> .pro anpassen

Verfasst: 4. März 2010 22:47
von t_b
Ok, danke

Kleine Zeile, große Wirkung

Nur kann ich nun meine Methode zur "Mittelpunkt"-Korrektur nicht anwenden.

Wie kann ich in einem SVG den Nullpunkt hinterlegen?

EDIT:

Ich habe mir mal ein wenig die Spezifikationen von SVG angeschaut, aber soetwas wie den 0-Punkt nicht gefunden. Einzig den Viewport kann man angeben, und das ist aber nicht das, was ich will, da er ja nur den sichbaren bereich einstellt.

Verfasst: 5. März 2010 09:08
von upsala
Dann gib uns mal das svg-File.

Verfasst: 5. März 2010 09:17
von t_b
Ich hab mal eine ganz simple selber geschrieben :-D

Da svg upload nicht erlaubt ist, hier der code:

Code: Alles auswählen

<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg"
	xmlns:xlink="http://www.w3.org/1999/xlink"
	xmlns:ev="http://www.w3.org/2001/xml-events"
	version="1.1" baseProfile="full"
	width="128px" height="128px" viewBox="0 0  128 128">
<polygon
     points="2 34  64 34  64 6  125 64  64 122  64 94  2 94"
     id="arrow"
     style="stroke:#3465a4;stroke-width:4;fill:#729fcf" />
</svg>

Verfasst: 5. März 2010 09:54
von franzf
http://doc.qt.nokia.com/4.6/coordsys.ht ... formations
kennst du schon?
Dein GraphicsItem hat eine pos(). Per Default ist das Links oben. Das wird auch beim SvgItem so sein. Das ist der Ursprungspunkt für Transformationen.

Um das item um das Zentrum zu drehen nimmst du ein QTransform, setzt den Drehpunkt ins Zentrum mittels QTransform::translate(), dann gibst du ne Drehung mit QTransform::rotate() an. Danach dein QGraphicsItem::setTransform(), und dein Item ist gedreht.

Alternativen ergeben sich mit Qt-4.6:
QGraphicsItem::setTransformOriginPoint() + QGraphicsItem::setRotation().

Für den Rest hast du die Doku ;)

Verfasst: 5. März 2010 09:57
von t_b
Ja, nur dass ich dadurch eine weitere Variable die die Rotation durchführt einführen müsste, da setRotation statisch rotiert, wärend rotate bezogen auf die aktuelle Position rotiert. ich probiere es aber mal.

Verfasst: 5. März 2010 12:23
von upsala

Code: Alles auswählen

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
   xmlns:svg="http://www.w3.org/2000/svg"
   xmlns="http://www.w3.org/2000/svg"
   version="1.1"
   width="128"
   height="128"
   viewBox="0 0 128 128"
   id="svg2">
  <defs
     id="defs7" />
  <polygon
     points="64,6 125,64 64,122 64,94 2,94 2,34 64,34 "
     transform="translate(-50,50)"
     id="arrow"
     style="fill:#729fcf;stroke:#3465a4;stroke-width:4" />
</svg>

Verfasst: 5. März 2010 13:03
von franzf
Incl. SVG zeichnen hab ich für die von dir gepostete Aufgabenstellung 15 Minuten investiert. Naja fast, Pfeilabfrage hab ich nicht, das überlass ich dir :P.
Wenn du absolut nicht weiterkommst kann ich das hier posten.
Macht Zweimarkfünzig ;)

Grüße
Franz