Seite 1 von 1

SVG "interaktiv" mit Qt verwenden

Verfasst: 19. Februar 2009 09:07
von chrislo1976
Hallo zusammen!

Ich hätte heue eine Frage bezüglich der Verwendung von SVG mit Qt4.

Die verschiedenen SVG-Klassen von Qt hab ich mir schon angeschaut, ein paar Beispiele dazu auch.
Ich konnte daraus aber leider nicht ermitteln ob sich mein Vorhaben vollständig realisieren lässt.


Deshalb hab ich mir gedacht, frag ich hier mal nach ob jemand sowas schon realisiert hat oder überhaupt angegangen ist:

Ich möchte ein SVG-Bild zeichnen (eher einfach, nur ein paar einfache Objekte) und dies in Qt in ein Fenster laden. Was gehen sollte, ist dieses Bild dann dynamisch zu verändern bzw. in veränderter Form neu zu erstellen. Was ich dann vor allem benötigen würde, ist die Information auf welches Element der Bediener geklickt hat.

Es sollte im Idealfall so sein dass der Bediener ein gewisses Objekt anwählen kann und dann ggf. besondere Informationen dazu erhält. Dieses SVG-Objekt steht z.B. für eine Maschine in einer Anlage.

Ich hab das mousePressEvent schon abgefangen, und damit die Koordinaten ausgelesen, allerdings hab ich es nicht geschafft damit sicher zu prüfen ob sich diese Koordinaten innerhalb eines SVG-Objektes befinden. Denn wenn ich z.B. das Fenster in der Größe ändere, bleiben die Koordinaten die ich mit QSvgRenderer::boundingRect() auslesen kann immer gleich.


Kann mir da jemand einen leichten Stoß in die richtige Richtung geben?
Oder "HALT" schreien wenn das so sowieso nicht möglich ist???

DANKE!

Gruß,
Christian

Verfasst: 19. Februar 2009 12:34
von The User
Benutze doch einfach das Graphics-View-System.
Der malt dann zwar ganz normale Pixel, aber die Sachen sollten sich dennoch skalieren und drehen lassen. Und da QSvgGenerator auf ein QPaintDevice ist, ist so weit ich weiß dann auch relativ einfach ein SVG-Export möglich.

Viele liebe Grüße
The User

Verfasst: 19. Februar 2009 12:48
von chrislo1976
Danke für den Tipp!
Hab in der Zwischenzeit von einem Kollegen das Qt Jambi Beispielprogramm SVG Card Deck bekommen.
Dort wird mehr oder weniger genau das gemacht was ich brauchen würde, eben auch mit dem von dir erwähnten Graphics-View-System.

Werd mal versuchen das auf C++ umzusetzen.

Gruß,
Christian

Verfasst: 23. Februar 2009 11:16
von chrislo1976
Hallo zusammen!

Hab inzwischen das QtJambi-Programm mehr oder weniger (zumindest vom Prinzip her) auf C++ portiert:

mainwindow.cpp:

Code: Alles auswählen

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "svgobject.h"

#include <QSvgRenderer>
#include <QGraphicsSvgItem>
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QGraphicsSvgItem>

MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindowClass)
{
    ui->setupUi(this);

    QStringList list;
    list << "ROT" << "GELB" << "GRUEN";

    QGraphicsScene* scene = new QGraphicsScene();
    QGraphicsView*	view = new QGraphicsView();
    view->setScene(scene);

    QSvgRenderer* renderer = new QSvgRenderer(QString("D:\\Projekte\\Qt_Projekte\\svgtest\\pic.svg"), parent);

    QList<SvgObject>* objects = new QList<SvgObject>;

    for (int i = 0; i < list.count(); ++i)
    {
        SvgObject* item = new SvgObject(list.at(i), renderer); 
		
        scene->addItem(item);
        item->translate(150.0 * (i-1), 0);
    }

	this->setCentralWidget(view);
}

MainWindow::~MainWindow()
{
    delete ui;
}
svgobject.h :

Code: Alles auswählen

#ifndef SVGOBJECT_H
#define SVGOBJECT_H

#include <QPainter>
#include <QGraphicsSceneMouseEvent>
#include <QGraphicsSvgItem>

class SvgObject : public QGraphicsSvgItem
{
public:

    SvgObject::SvgObject(QString id, QSvgRenderer* renderer, QWidget* parent = 0);

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


private:

    double opacity;

protected:

    void hoverEnterEvent(QGraphicsSceneHoverEvent* event);
    
	void hoverLeaveEvent(QGraphicsSceneHoverEvent* event);

	void mousePressEvent(QGraphicsSceneMouseEvent* event);
};

#endif // SVGOBJECT_H
svgobject.cpp :

Code: Alles auswählen

#include "svgobject.h"

SvgObject::SvgObject(QString id, QSvgRenderer* renderer, QWidget* parent)
{
    setElementId(id);
    setSharedRenderer(renderer);

	setAcceptHoverEvents(true);

	opacity = 1.0;
}

void SvgObject::hoverEnterEvent(QGraphicsSceneHoverEvent* event)
{
    opacity = 0.5;

	update();
}

void SvgObject::hoverLeaveEvent(QGraphicsSceneHoverEvent* event)
{
    opacity = 1.0;

	update();
}

void SvgObject::mousePressEvent(QGraphicsSceneMouseEvent* event)
{
	rotate(30.0);

	update();
}

void SvgObject::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    painter->setOpacity(opacity);

    QGraphicsSvgItem::paint(painter, option, widget);
}
In dem SVG-File das geladen wird sind einfach nur 3 Rechtecke enthalten die ROT, GELB und GRUEN heissen.
Mit diesem Code schaffe ich es Events für die verschiedenen SVG-Objekte abzufangen, z.B. mousePressEvent, hoverEnterEvent und hoverLeaveEvent. So weit, so gut!

Allerdings habe ich damit das Problem dass mir so alle Objekte in den Mittelpunkt der Anzeige verschoben werden, und nicht dort angezeigt werden wie es in der original SVG-Datei gezeichnet war.
Mit dem

Code: Alles auswählen

item->translate(150.0 * (i-1), 0);
stelle ich nur sicher dass die Objekte nebeneinander liegen (zum Test).


Hab dann versucht das File so zu laden:

Code: Alles auswählen

    ui->setupUi(this);

    QStringList list;
    list << "ROT" << "GELB" << "GRUEN";

    QGraphicsScene* scene = new QGraphicsScene();
    QGraphicsView*	view = new QGraphicsView();
	view->setScene(scene);

	QGraphicsSvgItem* gitem = new QGraphicsSvgItem(QString("D:\\Projekte\\Qt_Projekte\\svgtest\\pic.svg"));
	scene->addItem(gitem);

    // QSvgRenderer* renderer = new QSvgRenderer(QString("D:\\Projekte\\Qt_Projekte\\svgtest\\pic.svg"), parent);

    QList<SvgObject>* objects = new QList<SvgObject>;

    for (int i = 0; i < list.count(); ++i)
    {
        SvgObject* item = new SvgObject(list.at(i), gitem->renderer());
		
		// scene->addItem(item);
		// item->translate(150.0 * (i-1), 0);
    }

	this->setCentralWidget(view);
So werden die Objekte zwar an der richtigen Position angezeigt, aber ich bekomme keine Events mehr für die Objekte...

Hat jemand eine Idee was ich anders machen könnte um mein Ziel zu erreichen?

Gruß,
Christian

Verfasst: 23. Februar 2009 16:49
von VuuRWerK
Hallo Christian,

das selbe Problem hatte ich auch mal und hab es dadurch gelöst das ich das Scene-Rect gesetzet habe: QGraphicsScene#sceneRect-Property.

Jedoch weiß ich nicht ob das wirklich der angedachte Weg ist die Größe der "Zeichenfläche" so zu setzen. Mir wäre da auch eine Funktion lieber die sich an die Größe des Parent orientiert oder so(maybe: QGraphicsScene#fitSceneRectTo(QWidget)). Denn wenn Du ein sehr großes Rect definierst, größer als die größtmögliche darstellbare größe dann bekommt man Scrollbalken, was durchaus richtig ist da QGraphicsView wiederum eine QAbstractScrollArea ist.

Grad beim schreiben hatte ich eine Idee wie man das eventuell mit dem parent lösen könnte ... ich gugge :)

Gut Schuß
VuuRWerK (auch ein Christian) ;)

Verfasst: 24. Februar 2009 10:29
von VuuRWerK
Also hier das automatische anpassen der Größe des Views, beachtet wird auch die Größe die die items einnehmen:

def:

Code: Alles auswählen

class DerivedQGraphicsView : public QGraphicsView {
protected:
    virtual void resizeEvent(QResizeEvent*);
};
impl:

Code: Alles auswählen

void DerivedQGraphicsView::resizeEvent(QResizeEvent *resizeEvent) {
    QSize size = resizeEvent->size();
    const QRectF rect = scene()->itemsBoundingRect();
    if (rect.width() > size.width())
        size.setWidth(rect.width());

    if (rect.height() > size.height())
        size.setHeight(rect.height());

    setSceneRect(QRectF(0, 0, size.width(), size.height()));
}
Gut Schuß
VuuRWerK ;)

Verfasst: 24. Februar 2009 10:33
von chrislo1976
Hallo!

Also mit resize-Geschichten hab ich eigentlich (noch) kein Problem.
Mein Haupt-Problem ist aktuell dass ich entweder keine Events für meine SVG-Objekte bekommen, oder aber dass die Objekte nicht dort gezeichnet werden wo sie laut SVG-Datei sein sollten...

Gruß,
Christian

Verfasst: 24. Februar 2009 12:30
von franzf
Die QtSvg-Sachen sind auch nicht gedacht, einen kompletten SVG-Editor zu schreiben.
Nicht umsonst entwickeln die KDE-Leute ihr eigenes KSVG (welches AFAIK auch ein Bestand von QtWebkit ist, sollte man vllt. mal anregen das für andere Applikationen zur Verfügung zu stellen...).
Also entweder das ganze SVG rendern, oder die einzelnen Objekte extrahieren und selber Positionieren. Wenn du die korrekte Position haben willst, schau dir mit dem Texteditor das SVG an (ist ja nur XML). Da gibt es zu jedem path immer ein Attribut "d", in dem die einzelnen Pfad-Knoten stecken. Kannst ja mal versuchen, das zu extrahieren...

Ansonsten hast du es schon mal mit einem sharedRenderer versucht?

Verfasst: 24. Februar 2009 13:38
von chrislo1976
@franzf:

Ich habe wirklich nicht vor einen Editor zu machen, dafür gibts ja eh freie super-tolle Programme im Netz.
Mir gehts nur darum mit einem externen SVG-Editor eine mehr oder weniger komplexe "Scene" zeichnen zu können, diese in Qt anzuzeigen, aber zusätzlich noch darauf zu reagieren ob und welches Objekt der Bediener angeklickt (und andere Events) hat.

Meinen SvgObjects übergebe ich den SvgRenderer bereits mit

Code: Alles auswählen

setSharedRenderer()
...

Gruß,
Christian