Suche Tipps für schnellen grafischen Bildaufbau
Suche Tipps für schnellen grafischen Bildaufbau
Hallo allerseits,
ich möchte eine Grafik aufbauen,
die aus einer hohen Anzahl (z.B. 100 000) auf einander folgenden Strichen besteht.
Diese Striche bestehen oft aus der gleichen Farbe und Dicke, also
gleicher Pen. Ab und zu muss allerdings der Pen auch gewechselt werden.
Gleichzeitig sollte die Grafik sich auch abhängig der "Strichnummer"
schnell aufbauen können. Soll heißen: Das Bild wurde gerade bis zur
Strichnummer 23 765 gezeichnet und nun soll z.B. auf Knopfdruck die Grafik
möglichst schnell mit den nächsten Strich mehr - oder schwieriger -
um den letzten Strich weniger gemalt werden.
Für ein schnelles Zeichnen ist es wahrscheinlich am besten, wenn man die Striche
in einem QPainterPath bündelt und für jeden Penwechsel einen neuen Pfad
erzeugt. Meine Fragen sind nun:
- welche QT-Klasse ist für das Zeichnen eines solchen Bildes
dazu am besten geeignet: QPixelMap, QImage oder sogar QGraphicView?
- muss ich bei jedem Strich das gesamte Bild (Pixmap) neu aufbauen oder
ist es möglich (weil eleganter, schneller, weniger flackern) nur den nächsten Strich
hinzu zu malen oder den letzten Strich irgendwie schnell zu löschen?
- wie könnte man auf dem Strichbild dann noch zusätzliche Grafikelemente
ein- und ausblenden?
Vielen Dank fürs Lesen und für konstruktive Antworten.
ff-fan (soll heissen: firefox-fan)
ich möchte eine Grafik aufbauen,
die aus einer hohen Anzahl (z.B. 100 000) auf einander folgenden Strichen besteht.
Diese Striche bestehen oft aus der gleichen Farbe und Dicke, also
gleicher Pen. Ab und zu muss allerdings der Pen auch gewechselt werden.
Gleichzeitig sollte die Grafik sich auch abhängig der "Strichnummer"
schnell aufbauen können. Soll heißen: Das Bild wurde gerade bis zur
Strichnummer 23 765 gezeichnet und nun soll z.B. auf Knopfdruck die Grafik
möglichst schnell mit den nächsten Strich mehr - oder schwieriger -
um den letzten Strich weniger gemalt werden.
Für ein schnelles Zeichnen ist es wahrscheinlich am besten, wenn man die Striche
in einem QPainterPath bündelt und für jeden Penwechsel einen neuen Pfad
erzeugt. Meine Fragen sind nun:
- welche QT-Klasse ist für das Zeichnen eines solchen Bildes
dazu am besten geeignet: QPixelMap, QImage oder sogar QGraphicView?
- muss ich bei jedem Strich das gesamte Bild (Pixmap) neu aufbauen oder
ist es möglich (weil eleganter, schneller, weniger flackern) nur den nächsten Strich
hinzu zu malen oder den letzten Strich irgendwie schnell zu löschen?
- wie könnte man auf dem Strichbild dann noch zusätzliche Grafikelemente
ein- und ausblenden?
Vielen Dank fürs Lesen und für konstruktive Antworten.
ff-fan (soll heissen: firefox-fan)
Am besten die Doku zu QImage/QPixmap lesen. QImage dient der pixelweisen Manipulation, QPixmap ist alles was du siehst (da alles intern in ein pixmap ge"QPaintet" wird). Wenn ich das angehen müsste würde ich mir vermutlich QSvg anschauen. Wenn das zu langsam ist, eigene Klassen bauen, zB
wen du das ganze in QGraphicsView/Scene/Item machst, könnte jede Linie ein QGraphicsItem sein, dann kannst du das einfach per show() / hide() malen lassen oder auch nicht
Wenn du noch zusätzliche Sachen ein/ausblenden willst ... QGraphicScene, definitiv
Was malst du denn für schöne Strichbilder?
Code: Alles auswählen
struct Stripes {
map<int, QBrush> m_brushForLineID;
map<int, QLine> m_lineWithID;
map<int, int> m_orderOfPaintingLineID;
};
class StripeImg {
Stripes m_stripes;
int paintTillID;
QPixmap m_act;
QPixmap m_prev;
// und dann eine QPixmap & gimmePixmapTillID(int id) in der du die entsprechenden Striche malst. Vielleicht das letzte Ergebnis zwischenspeichern > wenn die neue ID == alteID+1 eifach das jetzige als prev speichern, auf act kopieren und einen strich dazu ... bei neue ID == alteID-1 das alte auf act kopieren und anzeigen. Nachteil ... wenn 2 mal ID == alteID-1 musste wieder neu zeichnen.
};
Wenn du noch zusätzliche Sachen ein/ausblenden willst ... QGraphicScene, definitiv
Was malst du denn für schöne Strichbilder?
Hallo,
danke für die Antwort. Ich weiß, die Frage war sehr allgemein
gestellt. Der Zweck ist das Anzeigen von Abbildungen aus dem
Textilbereich, mehr möchte ich dazu nicht sagen.
Habe wie gesagt schon etwas mit QPainterPath in der Art ausprobiert:
Zuerst dachte ich, man könne mit der closeSubpath()-Methode alle Striche
mit zugehörigem Pen in einem QPainterPath-Objekt speichern. Das war leider
nicht der Fall, deshalb der Umweg über QVector.
Versuche auf die Malfläche(pix) mit einem Pointer zu verwenden (*pix), um diese nicht immer
neu erzeugen zu müssen (z.B. nur ein Strich dazu), führten oft dazu,
dass der Painter oft ungültig oder inaktiv wurde (z.B. bei Form-/Tab-/Focuswechsel),
was zur Folge hatte, dass dann der Pen in einer default-Dicke und schwarz malt.
Sicher aktiv ist der Painter nur, wenn man die Malfläche und den Painter immer neu erzeugt
(wie im Source oben), dann muß man aber auch wieder das ganze Bild neu malen...
Schön wäre auch, wenn man ein grafische Elemente (einen Kreis oder ein Kreuz) ein- und
auch wieder ausblenden könnte. Das scheint aber mit QPixmap aufwendig zu werden - vor allem das wegblenden.
danke für die Antwort. Ich weiß, die Frage war sehr allgemein
gestellt. Der Zweck ist das Anzeigen von Abbildungen aus dem
Textilbereich, mehr möchte ich dazu nicht sagen.
Habe wie gesagt schon etwas mit QPainterPath in der Art ausprobiert:
Code: Alles auswählen
typedef struct
{
QPainterPath path;
QPen pen;
}T_STROKEPATH;
QVector<T_STROKEPATH> vStrokes;
//irgendwo Pfade merken, in der Art:
vStrokes.push_back(einPfad, ZugehoerigerPen);
//Zeichen:
void View::paintEvent()
{
QPixmap pix;
QPainter p(&pix);
//alle gespeicherte Stroke-Pfade zeichnen
for (int i = 0; i < vStrokes.count(); i++)
{
p.strokePath(vStrokes[i].path, vStrokes[i].pen);
}
painter->end();
}
Zuerst dachte ich, man könne mit der closeSubpath()-Methode alle Striche
mit zugehörigem Pen in einem QPainterPath-Objekt speichern. Das war leider
nicht der Fall, deshalb der Umweg über QVector.
Versuche auf die Malfläche(pix) mit einem Pointer zu verwenden (*pix), um diese nicht immer
neu erzeugen zu müssen (z.B. nur ein Strich dazu), führten oft dazu,
dass der Painter oft ungültig oder inaktiv wurde (z.B. bei Form-/Tab-/Focuswechsel),
was zur Folge hatte, dass dann der Pen in einer default-Dicke und schwarz malt.
Sicher aktiv ist der Painter nur, wenn man die Malfläche und den Painter immer neu erzeugt
(wie im Source oben), dann muß man aber auch wieder das ganze Bild neu malen...
Schön wäre auch, wenn man ein grafische Elemente (einen Kreis oder ein Kreuz) ein- und
auch wieder ausblenden könnte. Das scheint aber mit QPixmap aufwendig zu werden - vor allem das wegblenden.
Ok, QGraphicsView werde ich mir diesbezüglich noch einmal genauer ansehen, sowie die Exsamples dazu.
Wenn ich dann eine elegante Lösung gefunden haben sollte, kann ich sie ja hier bekannt geben.
Vielleicht noch eine Frage, wodurch es ggf. auch Probleme mit QPainter gibt:
Die Oberfläche besteht aus verschiedenen Widgets/Tabs usw. und soll bei mir immer über den QT-Designer
konfiguriert werden. Jetzt kann man z.B. ein GraphicView als Widget hineinziehen, wie kommt man aber
an den paintEvent() dieses Objektes, das ja über ui_windows.h instanziert wird und über das man ja nur
via Zeiger (mainwindow->ui->) zugreift?
Grüße
ff-Fan
Wenn ich dann eine elegante Lösung gefunden haben sollte, kann ich sie ja hier bekannt geben.
Vielleicht noch eine Frage, wodurch es ggf. auch Probleme mit QPainter gibt:
Die Oberfläche besteht aus verschiedenen Widgets/Tabs usw. und soll bei mir immer über den QT-Designer
konfiguriert werden. Jetzt kann man z.B. ein GraphicView als Widget hineinziehen, wie kommt man aber
an den paintEvent() dieses Objektes, das ja über ui_windows.h instanziert wird und über das man ja nur
via Zeiger (mainwindow->ui->) zugreift?
Grüße
ff-Fan
1000 Linien mit QGraphicsLineItem ... du brauchst wohl eher das QGraphicsPathItem und deinen struct {} Ansatz .. bei so 10000 Linien dauerts schon lange zu zeichnen ...
Code: Alles auswählen
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QTime>
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QGraphicsLineItem>
#include <QVBoxLayout>
class Widget : public QWidget {
Q_OBJECT
public:
Widget(QWidget *parent = 0) : QWidget(parent) {
QVBoxLayout * vbl = new QVBoxLayout(this);
m_scene = new QGraphicsScene(0,0,1000,760,this);
m_view = new QGraphicsView(m_scene, this);
vbl->addWidget(m_view);
qsrand(QTime(0,0,0,0).msecsTo(QTime::currentTime()));
for(int i=0;i<1000;++i)
{
QLineF l;
rndLine(l);
addLine(l);
}
}
void addLine(const QLineF &f)
{
QPen p;
rndPen(p);
QGraphicsLineItem * line = m_scene->addLine(f,p);
m_lines.push_back(line);
qDebug("%i", m_lines.size());
}
void hideLine(int i)
{
Q_ASSERT(i < m_lines.size());
m_lines.at(i)->hide();
}
void showLine(int i)
{
Q_ASSERT(i < m_lines.size());
m_lines.at(i)->show();
}
private:
void rndLine(QLineF & l)
{
QLineF line;
while(line.length() < 25)
{
int x1 = qrand()%int(m_scene->width());
int y1 = qrand()%int(m_scene->height());
int x2 = qrand()%int(m_scene->width());
int y2 = qrand()%int(m_scene->height());
x1 = qBound(10,x1,int(m_scene->width())-10);
y1 = qBound(10,y1,int(m_scene->height())-10);
x2 = qBound(10,x2,int(m_scene->width())-10);
y2 = qBound(10,y2,int(m_scene->height())-10);
line = QLine(x1,y1,x2,y2);
}
l = line;
}
void rndPen(QPen &pp)
{
QPen p;
switch(qrand()%16)
{
case 0: p.setColor(Qt::black);break;
case 1: p.setColor(Qt::red);break;
case 2: p.setColor(Qt::darkRed);break;
case 3: p.setColor(Qt::green);break;
case 4: p.setColor(Qt::darkGreen);break;
case 5: p.setColor(Qt::blue);break;
case 6: p.setColor(Qt::darkBlue);break;
case 7: p.setColor(Qt::cyan);break;
case 8: p.setColor(Qt::darkCyan);break;
case 9: p.setColor(Qt::magenta);break;
case 10: p.setColor(Qt::darkMagenta);break;
case 11: p.setColor(Qt::yellow);break;
case 12: p.setColor(Qt::darkYellow);break;
case 13: p.setColor(Qt::gray);break;
case 14: p.setColor(Qt::darkGray);break;
case 15: p.setColor(Qt::lightGray);break;
}
p.setWidthF(1+(qrand()%25/10.0));
switch (1+qrand()%5)
{
case 1: p.setStyle(Qt::SolidLine);break;
case 2: p.setStyle(Qt::DashLine); break;
case 3: p.setStyle(Qt::DotLine); break;
case 4: p.setStyle(Qt::DashDotLine); break;
case 5: p.setStyle(Qt::DashDotDotLine);break;
}
pp = p;
}
QGraphicsScene * m_scene;
QGraphicsView * m_view;
QVector<QGraphicsLineItem*> m_lines;
};
#endif // WIDGET_Hich habe noch ein wenig rumgespielt ... ich denke du brauchst da einen anderen Ansatz. Mein Computer malt 1000 Linien ok, bei 10000+ geht er in die Knie und nix geht mehr.
Das selbe nochmal mit QGraphicsPathItems:
Das selbe nochmal mit QGraphicsPathItems:
Code: Alles auswählen
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QTime>
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QGraphicsLineItem>
#include <QGraphicsPathItem>
#include <QDebug>
#include <QVBoxLayout>
class Widget : public QWidget {
Q_OBJECT
public:
Widget(QWidget *parent = 0) : QWidget(parent) {
QVBoxLayout * vbl = new QVBoxLayout(this);
m_scene = new QGraphicsScene(0,0,1000,760,this);
m_view = new QGraphicsView(m_scene, this);
vbl->addWidget(m_view);
qsrand(QTime(0,0,0,0).msecsTo(QTime::currentTime()));
qDebug("%s %s","creating lines",qPrintable(QTime::currentTime().toString()));
QTime stopit;
stopit.restart();
// create 100.000 lines
for(int i=0;i<1000 ;++i)
{
QLineF l;
rndLine(l);
QPen p;
rndPen(p);
int penID = m_pens.key(p,-1);
if (penID == -1)
{
penID = m_pens.size();
m_pens.insert(penID,p);
}
m_lines.insert(penID,l);
}
qDebug("%s\nCreating paths", qPrintable(QTime(0,0,0,0).addMSecs(stopit.elapsed()).toString()));
qDebug("Lines: %i",m_lines.size());
qDebug("Pens: %i",m_lines.keys().size());
qDebug() << m_pens;
stopit.restart();
// foreach PEN
foreach (const int penID, m_lines.keys())
{
// tmp path
QPainterPath paintPath;
const QList<QLineF> lines = m_lines.values(penID);
for(int i=0; i< lines.size(); ++i)
{
// moveto start
paintPath.moveTo(lines.at(i).p1());
// paint to end
paintPath.lineTo(lines.at(i).p2());
}
// used pen
QGraphicsPathItem * graphicsPath = new QGraphicsPathItem(paintPath);
graphicsPath->setPen(m_pens.value(penID));
m_paths.push_back(graphicsPath);
}
qDebug("%s", qPrintable(QTime(0,0,0,0).addMSecs(stopit.elapsed()).toString()));
foreach (QGraphicsPathItem * item, m_paths)
m_scene->addItem(item);
stopit.restart();
}
void hidePath(int i)
{
Q_ASSERT(i < m_paths.size());
m_paths.at(i)->hide();
}
void showPath(int i)
{
Q_ASSERT(i < m_paths.size());
m_paths.at(i)->show();
}
private:
QTime stopit;
void rndLine(QLineF & l)
{ /* identisch */ }
void rndPen(QPen &pp)
{ /* identisch */ }
QGraphicsScene * m_scene;
QGraphicsView * m_view;
QMultiMap<int,QLineF> m_lines;
QMap <int ,QPen > m_pens;
QVector<QGraphicsPathItem*> m_paths;
};
#endif // WIDGET_HHallo!
Sorry, aber ich bin ich nicht immer online, deshalb kann die Antwort von mir auch mal länger dauern...
@padreigh: Vielen Dank dass du gleich ein Beispiel programmiert hast. Das erste Beispiel läuft und sieht künstlerisch wertvoll aus.
Beim zweiten Beispiel kam bei mir der Fehler:
can't find linker symbol for virtual table for `Widget' value
found `QVector<char*>::detach()' instead
@upsala: zum Thema: paintEvent des QGraphicsView
Bei dem Programm ist ja die Malfläche nur ein Widget, dass z.B. nach einem hide() auch mal neu gezeichnet werden muss.
Das hätte ich dann mit dem paintEvent erschlagen oder gibt es etwas eleganteres?
Also ich hätte die Randbedingungen deutlicher beschreiben sollen. Die aufeinanderfolgenden Striche sollten heißen: Jeder Endpunkt
eines Striches ist auch wieder Anfangspunkt des darauf folgenden Striches! Deshalb macht es für mich auch Sinn alle aufeinander
folgenden Striche - die den gleichen Pen haben! - mit lauter lineTo()'s in einem Pfad abzuspeichern Bei jedem Penwechsel wird dann
wieder ein neuer Pfad anlegt.
Eine weitere wichtige Bedingung ist: Die allermeisten Striche sind nur sehr kurz! Auch dadurch verringert sich die Bildaufbauzeit beträchtlich
(lange Striche brauchen natürlich mehr Zeit), so dass ich bei 70 000 auf ca. 60 - 80 ms komme.
Mein Problem sind nicht die Organisation der Striche, sondern wie ich so ein Bild am geschicktesten im Hintergrund in seinem Zustand
(Strichnummer: 34 122) halte, dann bei Bedarf wieder einschalte (show()) und von dort aus weiter machen kann. Dann von diesem
Zustand aus einen Strich dazu (+34 123) oder ein Strich weniger (-34 122) male. Und das Ganze am besten in einem ui-generierten Objekt
(wohl QGraphicsView).
Bei meinen bisherigen Versuchen kam es öfter vor, dass nach einem Tabwechsel das Strichbild nur noch in schwarz gezeichnet wurde
(Default-Pen) oder gar nicht. Um (Umschalt)Zeit zu Sparen benutzte ich für die Malfläche einen Zeiger, dass führte aber oft zu einem ungültigen
oder inaktiven Painter, ohne nachvollziehen zu können warum. Es scheint tatsächlich das Beste zu sein, die Mailfläche (GraphicsView oder Pixmap)
und den Painter in jedem PaintEvent neu zu kreiieren. Das heisst dann aber: Alle Striche immer wieder neu malen...
Vielen Dank für's Mitdenken.
Gruß ff-Fan
Sorry, aber ich bin ich nicht immer online, deshalb kann die Antwort von mir auch mal länger dauern...
@padreigh: Vielen Dank dass du gleich ein Beispiel programmiert hast. Das erste Beispiel läuft und sieht künstlerisch wertvoll aus.
Beim zweiten Beispiel kam bei mir der Fehler:
can't find linker symbol for virtual table for `Widget' value
found `QVector<char*>::detach()' instead
@upsala: zum Thema: paintEvent des QGraphicsView
Bei dem Programm ist ja die Malfläche nur ein Widget, dass z.B. nach einem hide() auch mal neu gezeichnet werden muss.
Das hätte ich dann mit dem paintEvent erschlagen oder gibt es etwas eleganteres?
Also ich hätte die Randbedingungen deutlicher beschreiben sollen. Die aufeinanderfolgenden Striche sollten heißen: Jeder Endpunkt
eines Striches ist auch wieder Anfangspunkt des darauf folgenden Striches! Deshalb macht es für mich auch Sinn alle aufeinander
folgenden Striche - die den gleichen Pen haben! - mit lauter lineTo()'s in einem Pfad abzuspeichern Bei jedem Penwechsel wird dann
wieder ein neuer Pfad anlegt.
Eine weitere wichtige Bedingung ist: Die allermeisten Striche sind nur sehr kurz! Auch dadurch verringert sich die Bildaufbauzeit beträchtlich
(lange Striche brauchen natürlich mehr Zeit), so dass ich bei 70 000 auf ca. 60 - 80 ms komme.
Mein Problem sind nicht die Organisation der Striche, sondern wie ich so ein Bild am geschicktesten im Hintergrund in seinem Zustand
(Strichnummer: 34 122) halte, dann bei Bedarf wieder einschalte (show()) und von dort aus weiter machen kann. Dann von diesem
Zustand aus einen Strich dazu (+34 123) oder ein Strich weniger (-34 122) male. Und das Ganze am besten in einem ui-generierten Objekt
(wohl QGraphicsView).
Bei meinen bisherigen Versuchen kam es öfter vor, dass nach einem Tabwechsel das Strichbild nur noch in schwarz gezeichnet wurde
(Default-Pen) oder gar nicht. Um (Umschalt)Zeit zu Sparen benutzte ich für die Malfläche einen Zeiger, dass führte aber oft zu einem ungültigen
oder inaktiven Painter, ohne nachvollziehen zu können warum. Es scheint tatsächlich das Beste zu sein, die Mailfläche (GraphicsView oder Pixmap)
und den Painter in jedem PaintEvent neu zu kreiieren. Das heisst dann aber: Alle Striche immer wieder neu malen...
Vielen Dank für's Mitdenken.
Gruß ff-Fan
Also ... interessantes Problem ...
hier ein Lösungsansatz .. alle 1000 PainterPaths wird ein Pixmap erstellt und darauf weitergemalt, die bisherigen PainerPaths werden versteckt. Da so immer nur ca 1001 GraphicsItems gemalt werden müssen ists performant ... die Rückwärtsrichtung überlass ich dir 
Um das ganze so einzusetzen wie du das brauchst, musst du vermutlich irgendwie die "nicht-pfad" Elemente seperat speichern (also vermutlich alle benutzen "addIrgendwas(...)" überladen und seperat halten (inner QList<QGraphicsItem*> zB und immer schön mit setZValue(HOHERWERT) in den Vordergrund schieben ), dann in makeSnapshot() vor dem render()'s aus- und danach wieder einblenden ... für Schrittweise vor/zurück-steppen bieten sich eigene Slots in der MyScene an, die entsprechend Sachen aus "paths" und "oldPix" hide()'en und show()'en ... oder du strickst dir was ganz anderes.
Als Gedankenspielerei reicht mir meins
Das ganze geht natürlich start zulasten des Speichers ... du hast paths/1000 Pixmaps von width*heighth die du speicherst + ne Map + Vector + ne Liste von nichtpfaditems ... aber du kannst viel malen, sogar 300k Linien von ca 15px Länge - auch wenn die mehrfach übereinander malen ...
Achja ... und alles ohne das PaintEvent anzufassen
Um das ganze so einzusetzen wie du das brauchst, musst du vermutlich irgendwie die "nicht-pfad" Elemente seperat speichern (also vermutlich alle benutzen "addIrgendwas(...)" überladen und seperat halten (inner QList<QGraphicsItem*> zB und immer schön mit setZValue(HOHERWERT) in den Vordergrund schieben ), dann in makeSnapshot() vor dem render()'s aus- und danach wieder einblenden ... für Schrittweise vor/zurück-steppen bieten sich eigene Slots in der MyScene an, die entsprechend Sachen aus "paths" und "oldPix" hide()'en und show()'en ... oder du strickst dir was ganz anderes.
Code: Alles auswählen
public slots:
void showPenNr(int i) // so circa in etwa
{
// make sure pen nr i exists (i< paths.size())
// show oldPix[(i/1000)*1000], hide all others
// hide all paths.at( 0 <= j < i/1000*1000)
// hide all paths.at( i < j < paths.size())
// show all paths.at( i/1000*1000 < j < qMin(i/1000*1000,paths.size() )
}Das ganze geht natürlich start zulasten des Speichers ... du hast paths/1000 Pixmaps von width*heighth die du speicherst + ne Map + Vector + ne Liste von nichtpfaditems ... aber du kannst viel malen, sogar 300k Linien von ca 15px Länge - auch wenn die mehrfach übereinander malen ...
Achja ... und alles ohne das PaintEvent anzufassen
Code: Alles auswählen
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QTime>
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QGraphicsLineItem>
#include <QGraphicsPathItem>
#include <QDebug>
#include <QVBoxLayout>
#include <QQueue>
#include <QPixmap>
#include <QMap>
class MyLineScene: public QGraphicsScene
{
Q_OBJECT
public:
MyLineScene( QObject * parent = 0 )
: QGraphicsScene(parent), SAVE_AT(1000)
{
setItemIndexMethod(QGraphicsScene::NoIndex);
}
MyLineScene ( const QRectF & sceneRect, QObject * parent = 0 )
: QGraphicsScene(sceneRect, parent), SAVE_AT(1000)
{
setItemIndexMethod(QGraphicsScene::NoIndex);
}
MyLineScene( qreal x, qreal y, qreal width, qreal height, QObject * parent = 0 )
: QGraphicsScene(x,y,width,height,parent), SAVE_AT(1000)
{
setItemIndexMethod(QGraphicsScene::NoIndex);
}
QGraphicsPathItem * addPath ( const QPainterPath & path,
const QPen & pen = QPen(),
const QBrush & brush = QBrush() )
{
QGraphicsPathItem * pathItem = QGraphicsScene::addPath(path,pen,brush);
showNext();
paths.push_back(pathItem);
return pathItem;
}
public slots:
void showNext()
{
const int pathSize = paths.size();
if ( pathSize < 1000) return;
if ( pathSize % SAVE_AT == 0)
{
makeSnapshot();
for (int i= ((pathSize / 1000)-1)*1000; i < (pathSize / 1000)*1000 && i < pathSize; ++i)
paths.at(i)->hide();
}
}
void showPrev()
{
}
private:
void makeSnapshot()
{
QPixmap bunch(sceneRect().size().toSize());
QPainter p(&bunch);
render(&p,sceneRect().toRect());
QGraphicsPixmapItem * pixmapItem = addPixmap(bunch);
pixmapItem->show();
if (!oldPix.isEmpty())
{
oldPix[oldPix.keys().last()]->hide();
}
oldPix.insert(paths.size(), pixmapItem);
}
private:
const int SAVE_AT;
QMap<int,QGraphicsPixmapItem*> oldPix;
QVector<QGraphicsPathItem*> paths;
};
class Widget : public QWidget {
Q_OBJECT
public:
Widget(QWidget *parent = 0) : QWidget(parent) {
const int WIDTH = 1000;
const int HEIGHT = 760;
QVBoxLayout * vbl = new QVBoxLayout(this);
m_scene = new MyLineScene(0,0,WIDTH,HEIGHT,this);
m_view = new QGraphicsView(m_scene, this);
vbl->addWidget(m_view);
qsrand(QTime(0,0,0,0).msecsTo(QTime::currentTime()));
qDebug("%s %s","creating lines",qPrintable(QTime::currentTime().toString()));
QTime stopit;
stopit.restart();
// create ca 300.000 lines
int i = 0, ppNr = 0;
for(i=0;i< 300000;)
{
QPen p;
rndPen(p);
QPainterPath pp;
++ppNr;
pp.moveTo(qrand()%WIDTH, qrand()%HEIGHT);
int count=qrand()%10+5;
for(int j=0; j < count;++j)
{
static int preX=1;
static int preY=1;
qreal dx = preX * qrand()%WIDTH/ 20.0;
qreal dy = preY * qrand()%HEIGHT/ 20.0;
static QPointF lastPos(dx,dy);
if ( (lastPos.x()+dx > WIDTH) || (lastPos.x()+dx < 0) )
{
dx*=-1;
preX*=-1;
}
if ( (lastPos.y()+dy > HEIGHT) || (lastPos.y()+dy < 0) )
{
dy*=-1;
preY*=-1;
}
lastPos.setX(lastPos.x()+dx);
lastPos.setY(lastPos.y()+dy);
pp.lineTo(lastPos);
}
m_scene->addPath(pp,p);
i+=count;
}
qDebug() << "Lines: " << i << " PainterPaths: " << ppNr << " in " << stopit.elapsed() << "ms";
}
private:
QTime stopit;
int shownTillPath;
void rndPen(QPen &pp)
{
QPen p;
switch(qrand()%16)
{
case 0: p.setColor(Qt::black);break; case 1: p.setColor(Qt::red);break; case 2: p.setColor(Qt::darkRed);break;
case 3: p.setColor(Qt::green);break; case 4: p.setColor(Qt::darkGreen);break; case 5: p.setColor(Qt::blue);break;
case 6: p.setColor(Qt::darkBlue);break; case 7: p.setColor(Qt::cyan);break; case 8: p.setColor(Qt::darkCyan);break;
case 9: p.setColor(Qt::magenta);break; case 10: p.setColor(Qt::darkMagenta);break; case 11: p.setColor(Qt::yellow);break;
case 12: p.setColor(Qt::darkYellow);break; case 13: p.setColor(Qt::gray);break; case 14: p.setColor(Qt::darkGray);break;
default: p.setColor(Qt::lightGray);break;
}
p.setWidthF(1+(qrand()%25/10.0));
switch (1+qrand()%5)
{
case 1: p.setStyle(Qt::SolidLine);break; case 2: p.setStyle(Qt::DashLine); break; case 3: p.setStyle(Qt::DotLine); break;
case 4: p.setStyle(Qt::DashDotLine); break; case 5: p.setStyle(Qt::DashDotDotLine);break;
}
pp = p;
}
MyLineScene * m_scene;
QGraphicsView * m_view;
};
#endif // WIDGET_H