SVG "interaktiv" mit Qt verwenden

Alles rund um die Programmierung mit Qt
Antworten
chrislo1976
Beiträge: 105
Registriert: 24. Februar 2008 09:45

SVG "interaktiv" mit Qt verwenden

Beitrag 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
The User
Beiträge: 62
Registriert: 11. Dezember 2008 23:48

Beitrag 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
chrislo1976
Beiträge: 105
Registriert: 24. Februar 2008 09:45

Beitrag 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
chrislo1976
Beiträge: 105
Registriert: 24. Februar 2008 09:45

Beitrag 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
Zuletzt geändert von chrislo1976 am 24. Februar 2009 07:57, insgesamt 1-mal geändert.
VuuRWerK
Beiträge: 82
Registriert: 11. Juni 2007 20:46
Wohnort: Dresden

Beitrag 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) ;)
Es gibt nur 3 natürliche Feinde des Programmierers: Tageslicht, frische Luft und das unerträgliche Gebrüll der Vögel.
Oft ist die Ursache des schwarzsehens lediglich ein verrutschen des Bretts vorm Kopf =)
VuuRWerK
Beiträge: 82
Registriert: 11. Juni 2007 20:46
Wohnort: Dresden

Beitrag 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 ;)
Es gibt nur 3 natürliche Feinde des Programmierers: Tageslicht, frische Luft und das unerträgliche Gebrüll der Vögel.
Oft ist die Ursache des schwarzsehens lediglich ein verrutschen des Bretts vorm Kopf =)
chrislo1976
Beiträge: 105
Registriert: 24. Februar 2008 09:45

Beitrag 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
franzf
Beiträge: 3114
Registriert: 31. Mai 2006 11:15

Beitrag 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?
chrislo1976
Beiträge: 105
Registriert: 24. Februar 2008 09:45

Beitrag 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
Antworten