Problem mit QGraphicsScene und Mouse Events

Alles rund um die Programmierung mit Qt
Antworten
szapo
Beiträge: 4
Registriert: 18. April 2013 11:41

Problem mit QGraphicsScene und Mouse Events

Beitrag von szapo »

Hallo liebe Forengemeinde!

Ich bin seit einiger Zeit glücklicher Qt-User und habe ein Problem in einem meiner Projekte.

Meine Applikation besteht aus mehreren Plugins, deren Properties untereinander verlinkt werden können. Um dies graphisch darzustellen, habe ich jeweils eine von QGraphicsItem abgeleitete Klasse für die Plugins und für die Verbindungslinien zwischen ihnen. (siehe Screenshot).

Beide Objekte werden in einer Liste gehalten:

std::list<QGraphicalEditorWidget*> widgets;
std::list<QGraphicalEditorLink*> links;

Beim Laden einer Projektdatei werden für alle Plugins Widgets in der Liste erstellt und der QGraphicsScene hinzugefügt, das gleiche gilt für die Link-Objekte.
Die Widgets und Links reagieren auf Maus-Events (Hover, Klick).
Wenn ich dann ein neues Projekt laden will (zB. das gleiche nochmal), werden zunächst alle Widgets und Links aus der Szene entfernt und die Liste geleert. Dann kommt noch sicherheitshalber ein scene.clear(), und danach werden wird die Szene mit den neuen Items befüllt. (siehe Code)
Das Problem: Beim Neuladen einer Szene, die ich schon einmal geladen habe, reagieren die Link-Items nichtmehr auf irgendwelche Maus-Events, die Widgets aber schon, obwohl das Datenmanagement bei beiden gleich ist. Wenn ich dann einen neuen Link zwischen zwei Widgets erstelle und sich die Szene somit ändert, funktioniert auf einmal wieder alles.
Dieses Verhalten tritt immer auf, wenn ich ein Projekt lade, dass ich (in derselben Debug-Session) schon einmal geladen habe, auch wenn ich dazwischen ein oder mehrere Projekte lade.

Was kann da falsch sein? Bin für jede Hilfe dankbar!

Hier der Code zum Löschen:

Code: Alles auswählen

void QGraphicalEditor::clear(void)
{
	links.clear();

	while(!widgets.empty())
	{
		QGraphicalEditorWidget * tempwidget = (QGraphicalEditorWidget*)*widgets.begin();
		scene.removeItem(tempwidget);
		widgets.pop_front();
		delete tempwidget;
	}

	layout->init();
	scene.clear();
}

Code: Alles auswählen

void QGraphicalEditorLinkManager::clear(void)
{
	if(links.size()>0)
	{
		QGraphicalEditor *gEditor = QVolumeShopApplication::Get()->GetMainframe()->GetGraphicalEditor();

		while(!links.empty())
		{	
			QGraphicalEditorLink * litem = (QGraphicalEditorLink*)*links.begin();
			gEditor->removeLinkFromLayout(litem); //Lösche die Kante aus der Layout-Klasse
			gEditor->getScene()->removeItem(litem); //Aus der Szene entfernen
			links.pop_front(); //aus der Liste nehmen
			delete litem; //Element löschen
		}
	}
}
Dateianhänge
screenshot.png
screenshot.png (47.83 KiB) 6433 mal betrachtet
odt
Beiträge: 128
Registriert: 12. August 2010 11:49
Kontaktdaten:

Re: Problem mit QGraphicsScene und Mouse Events

Beitrag von odt »

Hallo Szapo

Sieht schön aus!

Es ist schwierig, so aus der Ferne etwas zu Deinem Problem zu sagen.
Darum hier nur ein paar Triviali-Fragen...
- Bekommen die Links keine Events oder liegt der Fehler irgendwo in der Logik dahinter?
- Erstellst Du die Items bei einer Aenderung komplett neu oder erstellst Du nur die Items für den neuen Link neu?
- Gibt es Code-Unterschiede zwischen dem initialen Erstellen der Items und dem Hinzufügen von Links?
- Ist der Editor wirklich ein Singleton oder gibt's mehrere davon?
- Gibt es Dinger, die Du beim Laden wiederverwendest?
- Irgendwelche Debug-Ausgaben?
- Eingrenzen, Kürzen...
Vielleicht kann ich Dir mehr sagen, wenn Du das ganze Projekt veröffentlichen würdest...

Viele Grüsse
Reto
ODT Informatik GmbH, Reto Tschofenig
szapo
Beiträge: 4
Registriert: 18. April 2013 11:41

Re: Problem mit QGraphicsScene und Mouse Events

Beitrag von szapo »

Hallo!

Danke für die Anregungen!
Das Projekt hat über 70 Klassen, das will ich niemandem hier antun sich das durchzuschauen :-)
Ich werde mal versuchen Deine Fragen zu beantworten:

- Bekommen die Links keine Events oder liegt der Fehler irgendwo in der Logik dahinter?
sie bekommen keine Events
- Erstellst Du die Items bei einer Aenderung komplett neu oder erstellst Du nur die Items für den neuen Link neu?
nur die neu erstellten Links werden als GraphicsItem neu erstellt, alles andere bleibt. Nur die Szene wird neu gezeichnet.
- Gibt es Code-Unterschiede zwischen dem initialen Erstellen der Items und dem Hinzufügen von Links?
:-)
- Ist der Editor wirklich ein Singleton oder gibt's mehrere davon?
Ja der ist definitiv ein Singleton
- Gibt es Dinger, die Du beim Laden wiederverwendest?
Nein, eigentlich nicht. Die Szene wird geleert (siehe Code oben, und anschließend scene.clear()) und anschließend ein Element nach dem anderen erstellt und hinzugefügt.
- Irgendwelche Debug-Ausgaben?
Leider nein, es treten auch keine Fehler auf.

Ich habe das Problem gerade mit einem (zugegeben hässlichen) Workaround lösen können: Ich erstelle die Szene beim Clear-Vorgang neu (new QGraphicsScene()) und weise sie erneut dem QGraphicsView zu. Das behebt zwar den Fehler, sollte aber eigentlich nicht notwendig sein. Das heißt für mich aber, dass die Szene beim Neuladen nicht wirklich geleert wird und "Geister" drinbleiben.

Da möchte ich auch gleich ein anderes Problem erwähnen, das ich habe. Vielleicht hängt es ja irgendwie damit zusammen:
Ich habe manchmal das Problem, dass einzelne Links beim Verschieben keine Maus-Events empfangen (unabhängig vom ersten Problem, das erst beim Neuladen auftritt) Das tritt zB. auf, wenn ich (siehe Screenshot oben) das Widget "YZ Plane Orthograph..." rechts unter das verbundene Widget "YZ Plane" verschiebe. Dann verschiebt/dreht sich der Link dazwischen natürlich um 180° und ist fallweise nicht mehr ansprechbar (wird aber korrekt gezeichnet).
Zunächst dachte ich, dass es ein Problem mit dem BoundingRect ist oder mit der shape()-Methode (beides implementiert), die ich implementiert habe. Doch dort überprüfe ich sogar, welches Widget weiter links oben ist, somit wird immer ein BoundingRect ausgegeben, dessen obere linke Ecke auch wirklich oben links ist (dh. keine "negative" Fläche von rechts unten nach links oben). Zusätzlich habe ich das GraphicsView zu einer neuen, von QGraphicsView abgeleiteten Klasse, verändert, das Maus-Events abfängt und mittels items.at(QPointF) alle Items unter dem Cursor sucht und ihnen das Event weiterleitet. Das brachte leider auch keine Lösung.
odt
Beiträge: 128
Registriert: 12. August 2010 11:49
Kontaktdaten:

Re: Problem mit QGraphicsScene und Mouse Events

Beitrag von odt »

Der Workaround schliesst ja ein sauberes Aufräumen ja nicht aus ;)

Hast Du auch ab und zu, z.B. beim Verschieben, "Artefakte"? Ich hatte da mal was... Aus dem Kopf: Das Shape war eine eine Linie mit Breite 0 oder ich hatte ein prepareGeometryChange vergessen.
ODT Informatik GmbH, Reto Tschofenig
szapo
Beiträge: 4
Registriert: 18. April 2013 11:41

Re: Problem mit QGraphicsScene und Mouse Events

Beitrag von szapo »

Artefakte hab ich eigentlich keine. Meine shape-Methode sieht folgendermaßen aus:

Code: Alles auswählen

QPainterPath QGraphicalEditorLink::shape() const
{
	QPainterPath rectPath;

	if(selflink) //zwei verlinkte Properties im selben Widget -> Schleife
	{
		qreal mx = widget1->getCenterPos().x() - SELFLINK_SIZE;
		qreal my = widget1->getCenterPos().y() + widget1->boundingRect().height()/2.0 - SELFLINK_SIZE;

		rectPath.addEllipse(mx,my,SELFLINK_SIZE*2,SELFLINK_SIZE*2);
	}
	else
	{
		rectPath.moveTo(widget1->getCenterPos().x(), widget1->getCenterPos().y()); //getCenterPos = Mittelpunkt des Widgets
		rectPath.lineTo(widget2->getCenterPos().x(), widget2->getCenterPos().y());
		
		rectPath.closeSubpath();
	}

	QPainterPathStroker stroker;
        stroker.setWidth(5);
	return stroker.createStroke(rectPath);
}
odt
Beiträge: 128
Registriert: 12. August 2010 11:49
Kontaktdaten:

Re: Problem mit QGraphicsScene und Mouse Events

Beitrag von odt »

ich nehme an, dass Du beim widget.itemChange on ItemPositionHasChanged über alle Links loopst und dort irgendwie den Shape und BoundingRect updatest und anschliessend prepareGeometryChange() aufrufst?
ps: das boundingRect sollte nie ne Breite oder Höhe von 0 haben.
Hier mal auszugsweise ein bissel Code, vielleicht gibt's ne Inspiration. ( Node wird von Connection verbunden, der Verbindungspunkt ist ein NodePoint, Accessor ist die PD-Klasse)

Code: Alles auswählen

	Connection::Connection( Node* aFromNode, Node* aToNode )
		:QGraphicsItem( null, null )
		,_fromNode( aFromNode )
		,_toNode( aToNode )
	{
		_fromPoint = _fromNode->createPoint( _toNode->pos() );
		_fromPoint->setConnection( this );
		_toPoint = _toNode->createPoint( _fromNode->pos() );
		_toPoint->setConnection( this );
		setFlag(QGraphicsItem::ItemIsSelectable, true);
		setFlag(QGraphicsItem::ItemIsFocusable, true);
		[b]setFlag(QGraphicsItem::ItemSendsGeometryChanges, true);[/b]
		updatePosition();
	}
	QRectF Connection::boundingRect() const{
		return _boundingRect;
	}
	QPainterPath Connection::shape() const{
		return path;
	}
	void Connection::updatePosition(){
		from = _fromPoint->pos();
		_boundingRect = QRectF( from, QSizeF( 10, 10 ) );
		path.moveTo( from );
		to = _toPoint->pos();
		_boundingRect = _boundingRect.united( QRectF( to, QSize( 10, 10 ) ) );
		path.lineTo( to );
		[b]prepareGeometryChange();[/b]
	}
	Node::Node( const Accessor& object )
		:QGraphicsItem( null, null )
		,_size( 50.0f, 150.0f )
	{
		setPos( _object->getPosition() );
		QCursor cursor( Qt::SizeAllCursor );
		setCursor( cursor );
		setFlag(QGraphicsItem::ItemIsMovable, true);
		setFlag(QGraphicsItem::ItemIsSelectable, true);
		setFlag(QGraphicsItem::ItemIsFocusable, true);
		[b]setFlag(QGraphicsItem::ItemSendsGeometryChanges, true);
		setFlag(QGraphicsItem::ItemClipsToShape, true);
		setFlag(QGraphicsItem::ItemClipsChildrenToShape, true);[/b]
	}
	void Node::updatePosition(){
		foreach( NodePoint* point, points ){
			point->updatePosition(); // bewirkt connection->updatePosition();
		}
	}

	QVariant Node::itemChange(GraphicsItemChange change, const QVariant &value){
		if (change == ItemPositionHasChanged ) {
			QPointF newPos = value.toPointF();
			_object->setPosition( newPos );
			updatePosition();
		}
		return QGraphicsItem::itemChange(change, value);
	}

ODT Informatik GmbH, Reto Tschofenig
odt
Beiträge: 128
Registriert: 12. August 2010 11:49
Kontaktdaten:

Re: Problem mit QGraphicsScene und Mouse Events

Beitrag von odt »

bold geht im Code nicht :?
ODT Informatik GmbH, Reto Tschofenig
veeman
Beiträge: 280
Registriert: 3. Oktober 2012 01:43
Kontaktdaten:

Re: Problem mit QGraphicsScene und Mouse Events

Beitrag von veeman »

Schon mal versuch die Aufräumarbeiten einfach von der Scene erledigen zu lassen, ala widgets.clear, links.clear, scene.clear? (ggf repaint/refresh)
szapo
Beiträge: 4
Registriert: 18. April 2013 11:41

Re: Problem mit QGraphicsScene und Mouse Events

Beitrag von szapo »

Das Neuzeichnen der Szene funktioniert eigentlich automatisch. Wenn ich ein Widget (was ja eigentlich ein QGraphicsItem ist!) verschiebe (hat ein Flag QGraphicsItem::ItemIsMovable), wird die Szene automatisch neu gezeichnet.
Das interessante an dem Phänomen ist, dass so ein betroffener Link oft nicht gänzlich inaktiv ist, sondern zB. auf einer Hälfte funktioniert (also von einem Widget bis zum Kreis mit der Zahl), aber auf der anderen nicht. Sieht so aus, als würde irgendein unsichtbares Element darüberliegen, nur hat dessen Grenze überhaupt keinen Zusammenhang mit den Elementen auf der Szene. :?: :?: :?:
Antworten