[gelöst] Qt-PopupMenus

Alles rund um die Programmierung mit Qt
Antworten
Notwist
Beiträge: 85
Registriert: 2. März 2005 14:24

[gelöst] Qt-PopupMenus

Beitrag von Notwist »

Hallo,

habe mal eine Frage zu den PopupMenus:

Ich möchte ein QPopupMenu haben, von dem ein Menu-Punkt abgeht, der sich je nach angewählten ListViewItem (PopupMenu erscheint, wenn man ein ListViewItem rechtsklickt) unterscheidet und im Prinzip die zum ListViewItem gehörigen Tabellen-Einträge auflistet. Das klappt auch ganz gut:

Code: Alles auswählen

void KonfiguratorMainWindowImpl::popupMenuOutput()
{
	QString text;
	if (outputListView->currentItem()!=0)
	{
		QPopupMenu* canMenu = new QPopupMenu( this );
		Q_CHECK_PTR( canMenu );
		QPopupMenu *submenu = new QPopupMenu( this );
		Q_CHECK_PTR( submenu );
		canMenu->insertItem(addIdent, "Add CAN-Identifier", this, SLOT(newCanEntrySlot()), tr("Ctrl+E"));
		canMenu->insertItem(addDataField, "Complete CAN-Data",this, SLOT(completeIdentifierEntrySlot()));
		canMenu->insertItem( showCanMapper, "Show associated Modbus-Entry", submenu );  // das Untermenu mit dem dynamischen Einträgen
		canMenu->insertSeparator();
		canMenu->insertItem(erase, "Remove",this, SLOT(eraseCanEntrySlot()), tr("DEL"));
		getPtrListCanEntry();
		for(int i=0; i<candata->data.anz; i++) // hier kommen die dynamischen Einträge, je nachdem, wieviele zugehörige TabellenEinträge es gibt (data.anz)
		{	
			text=text.setNum(candata->data.dataBlock[0].byte_pos);		
			submenu->insertItem( "Start-Byte "+text,this, SLOT(showCanMapperSlot(candata->type, candata->data.dataBlock[i].reg_nr )));
		}
		canMenu->exec( QCursor::pos() );
		delete canMenu;
	}
}
Wenn ich jetzt aber meinen Methode showCanMapper(QString, unsigned int) habe und auf den Menu-Punkt klicke, dann springt er da garnicht rein. Habe natürlich erst gedacht, im Designer wäre der Slot nicht richtig initialisiert, aber daran liegt es nicht. Selbst, wenn ich das Ding auf showCanMapper(QString) vereinfache, dann springt er über das PopupMenu nicht rein. Nur, wenn ich ohne Parameter aufrufe, geht das. Versuche ich die Methode in meiner Klasse einfach über showCanMapper("QSTRING") aufzurufen, geht es aber. Warum geht das also nicht über einen Slot in meinem PopupMenu? Darf man über den Slot keine Parameter übergeben? Wie macht man dann dynamische Menus? Zu meiner Aufgabe, die zu bewältigen gilt:

Im Prinzip habe ich ein ListViewItem (mehrere natürlich), zB LV1, an das unterschiedlich viele Untereinträge in einer Tabelle bestehen: UE1, UE2

LV1
-> UE1
-> UE2

und da es je nach Item unterschiedlich viele UEs gibt und diese dann natürlich per Funktion (zB LÖSCHEN) angesprochen werden sollen, muss dieses Menu dynamisch sein, da der Nutzer auswählen darf, welchen UE er löschen, verändern will und das kann ich dann ja nicht anders lösen, oder? Hoffe, Ihr habt das verstanden und könnt mir wieder einmal helfen!
Zuletzt geändert von Notwist am 29. Juni 2005 16:39, insgesamt 2-mal geändert.
Gruß, Notwist
macman
Beiträge: 1738
Registriert: 15. Juni 2005 13:33
Wohnort: Gütersloh
Kontaktdaten:

Re: Qt-PopupMenus

Beitrag von macman »

Schon mal durch nen Debugger laufen lassen? Dann sollte der Fehler zu sehen sein. Laut der Doku hat QPopupMenu nur 4 Signale:
void activated ( int id )
void highlighted ( int id )
void aboutToShow ()
void aboutToHide ()

Da ist nichts mit QString, also wirst Du auch keine Connection herstellen können. Über die id kommst Du aber an den String ran, also sehe ich kein Problem.
Notwist
Beiträge: 85
Registriert: 2. März 2005 14:24

Beitrag von Notwist »

Was kann ich denn im Debugger sehen an Fehlern? Ich sehe halt, dass er nicht in die Methode reinspringt, wenn ich den Menueintrag drücke. Und letztendlich kann ich in dem PopupMenu-Eintrag doch jede Methode ausführen lassen, die ich will, oder? Letztendlich aktiviert der Druck auf den Menueintrag doch nur den Connect-Befehl zu dem Eintrag. Und wenn ich dem in der Methode schon Parameter zugewiesen habe, sollten die doch einbetoniert sein. Oder verstehe ich da jetzt etwas grundlegend falsch?
Und natürlich könnte ich die IDs der Menueinträge speichern, hätte dann aber ja noch nicht den Bezug zu den Untereinträgen, da ich dafür mindestens 2 Werte brauche, um ihn eindeutig identifizieren zu können. Sprich, ich muss eine Verbindung über 2 Parameter vom Menueintrag zu den Tabelleneinträgen haben, um eindeutig in meinen Datenstrukturen Änderungen vorzunehmen an dem vom Benutzer gewählten Objekt! Ist das verständlich?
Gruß, Notwist
ArneStocker
Beiträge: 300
Registriert: 3. November 2004 16:15
Wohnort: Berlin

Beitrag von ArneStocker »

Also das Übergeben von Werten ist nicht möglich, wenn Du ein Signal mit einem Slot verbindest. Dabei spielt es m.E. keine Rolle ob der Wert zum Zeitpunkt des 'connect' bereits feststeht oder eine Variable ist oder ggf. eine Konstante.

Es geht nur mit der Übergabe einem Extra - Integer Parameter, den Du für jede Menü - ID setzen kannst.

Code: Alles auswählen

int menuId = 1;	// ID des ersten Menüeintrages

... 

// für jeden Aufruf von InsertItem
menuID ++;
pCanMenu->insertItem(..,..,..,SLOT(..),tr(..),menuID);
int parameter = ..  irgendwas ... ;
pCanMenu->setItemParamter(menuID,parameter);
Letztlich stehen Deine (Unter) Menüeinträge doch für irgendwelche Objekte, Texte, Dateien oder was auch immer. Du wirst nicht darum herumkommen, die o.g. Liste von Parametern mit Deinen im (Unter)Eintrag dargestellten Objekten (oder Texten oder was auch immer) zu mappen. Das sollte jedoch mit den vorhandenen CollectionClasses kein Problem sein.


Gruss Arne
macman
Beiträge: 1738
Registriert: 15. Juni 2005 13:33
Wohnort: Gütersloh
Kontaktdaten:

Beitrag von macman »

Notwist hat geschrieben:Was kann ich denn im Debugger sehen an Fehlern?
Na das keine Connection hergestellt werden konnte, weil Signal und Slot nicht zueinander passen.
Notwist hat geschrieben:Ich sehe halt, dass er nicht in die Methode reinspringt, wenn ich den Menueintrag drücke. Und letztendlich kann ich in dem PopupMenu-Eintrag doch jede Methode ausführen lassen, die ich will, oder?
Nein, die Parameter von Signal und Slot müssen identisch sein.
Notwist hat geschrieben:Ist das verständlich?
Nein :) Das Popupmenü baust Du doch erst auf wenn es gebraucht wird, oder? Je nach dem was der User angeklickt hat, erstellst Du dynamisch das Menü. Oder verstehe ich jetzt was falsch?
Notwist
Beiträge: 85
Registriert: 2. März 2005 14:24

Beitrag von Notwist »

Danke erstmal für Eure Antworten. Es hat auch Licht ins Dunkel gebracht, aber noch nicht vollständig.

@Arne: Schön und gut mit der menuId, aber ich verstehe immer noch nicht, wie ich meiner durch den MenuEintrag aufgerufenen Methode sagen kann, welchen Eintrag der User jetzt ausgewählt hat. Ich habe mal einen Screenshot zur Verdeutlichung angefügt. Folgendes Beispiel:

Links seht ihr meine ListViewItems. Die stehen in einer QPtrList. An denen hängen in einem Array bis zu 8 Blöcke (candata->data.dataBlock), in denen die Werte für die Tabelle stehen (in unserem Fall nur 2 Blöcke, nämlich zwei long-Blöcke). Den Identifier-Wert vorausgesetzt, kann ich einen Tabelleneintrag über die Informationen Input & Start-Byte genau identifizieren, da doppelte Identifier-Werte auf Input und Output-Seite möglich sind, jedoch keine doppelten Start-Byte-Einträge auf einer Seite. Jetzt möchte der User zB den ersten Tabelleneintrag löschen (Menu-Punkt schon ausgewählt im Screenshot, also Start-Byte 0). Es wird nach der Auswahl eine Methode (testSlot()) aufgerufen, der ich aber ja nicht mitteilen kann, welchen Tabellen-Eintrag der User ausgewählt hat, weil ich keine Parameter übergeben kann, nicht mal die MenuId.

Code: Alles auswählen

canMenu->insertItem(erase, "Start-Byte",this, SLOT(testSlot()), tr("DEL"), menuId);
Vielleicht sehe ich den Wald vor lauter Bäumen auch nicht und bin einfach noch zu unerfahren, mache das ja erst ein paar Monate. Hoffe, Ihr seid trotzdem bereit, einem Unwissenden zu helfen.

@macman: Über Connections, die nicht ausgeführt werden, sagt mir mein Debugger garnichts.
Dateianhänge
screenie.jpg
screenie.jpg (36.74 KiB) 7493 mal betrachtet
Gruß, Notwist
ArneStocker
Beiträge: 300
Registriert: 3. November 2004 16:15
Wohnort: Berlin

Beitrag von ArneStocker »

also nochmal kurz :

der Parameter (int) , den Du dem mit MenuId bezeichneten Untermenüeintrag mitgibst, wird Deinem Slot als integer übergeben,

Code: Alles auswählen

// Mapinfo
class MapInfo;
{
	MapInfo();
	int m_zeile;
	int m_spalte1;
	int m_spalte2;
	int m_spalte3;
	...
}

typedef QValueList <MapInfo> MapInfoList;	

Code: Alles auswählen

// Klassendeklaration
MapInfoList m_mapInfoList;

// Programmcode
pCanMenu->insertItem(...,...,...,SLOT(meinSlot(int),...,MenuID);
MapInfo mapinfo ... // die gewünschten werte in mapinfo setzen
m_mapInfoList.append(mapInfo);	
pCanMenu->setItemParamter(menuID,m_mapInfoList.count() -1);	// posisition in mapinfolist ist gleich parameter

// Deine Slot Methode
void MeineKlasse::meinSlot(int parameter)  {
	// wertebereich prüfen !! und mapinfo auslesen
	MapInfo mapinfo = m_mapInfoList[parameter];
	// in mapinfo steht jetzt Deine Zeile und Spalte und was Du sonst reingeschreiben hast ...
}
Du könntest z.B. Collection aus einer MapInfo Klasse mappen, die aus einer Zeile (integerwert) und soviel spalten (ebenfalls integerwerten) besteht wie Du möchstest. Das ist jetzt auf die Schnelle und geht sicherlich auch eleganter , aber es funktioniert (und ein bisschen was kannst Du ja auch noch machen ;-). )

Natürlich musst Du die MapListe vor jedem Start des popupmenüs löschen (und am besten auch nach der Rückkehr in die reguläre eventschleife Deines Programms).

Code: Alles auswählen

... // auf rechte Maustaste reagieren
m_mapInfoList.clear();
... // s.o
pCanMenu->exec(QCursor::pos);
m_mapInfoList.clear();
...	
Gruss Arne
Zuletzt geändert von ArneStocker am 29. Juni 2005 11:36, insgesamt 1-mal geändert.
macman
Beiträge: 1738
Registriert: 15. Juni 2005 13:33
Wohnort: Gütersloh
Kontaktdaten:

Beitrag von macman »

Notwist hat geschrieben:

Code: Alles auswählen

canMenu->insertItem(erase, "Start-Byte",this, SLOT(testSlot()), tr("DEL"), menuId);
Versuch es mal mit:

Code: Alles auswählen

canMenu->insertItem(erase, "Start-Byte",this, SLOT(testSlot(int)), tr("DEL"), menuId);
Man achte auf das int als Parameter des Slots. Der enthält die ID des gewählten Menüeintrags und über die kommst du an den Rest.
Notwist hat geschrieben:Über Connections, die nicht ausgeführt werden, sagt mir mein Debugger garnichts.
Die Connection dürfte gar nicht erstellt werden. Zumindest bei dem normalen connect(...) kommt eine Meldung vom Debugger.
Notwist
Beiträge: 85
Registriert: 2. März 2005 14:24

Beitrag von Notwist »

Cool, ich glaub, das habe ich verstanden. ;-) Aber noch nicht auf GELÖST schalten, will´s erst ausprobieren! Trotzdem danke schon mal an Euch beide!
Gruß, Notwist
Notwist
Beiträge: 85
Registriert: 2. März 2005 14:24

Beitrag von Notwist »

Ein Problem habe ich jetzt noch. Wenn ich den Parameter an mein Slot-Methode übergebe, hat der leider nicht den Wert 0....7, sondern was ganz Komisches (0xffffffe4).

Dann klappt

Code: Alles auswählen

MapInfo mapInfo = mapInfoList[parameter];
natürlich nicht mehr.

Und noch was Komisches. Seitdem ich menuId bei

Code: Alles auswählen

canMenu->insertItem(erase, "Start-Byte",this, SLOT(testSlot(int)), tr("DEL"), menuId);
hinzugefügt habe, kriege ich im Popup immer ein komisches Zeichen im String (s. Screenshot). Mache ich menuId weg, geht auch das Zeichen weg. Ideen?
Dateianhänge
screenie.jpg
screenie.jpg (12.19 KiB) 7478 mal betrachtet
Gruß, Notwist
Notwist
Beiträge: 85
Registriert: 2. März 2005 14:24

Beitrag von Notwist »

Oh, ich hab´ den Fehler gefunden, der beides behebt. Muss so lauten:

Code: Alles auswählen

int id = submenu->insertItem("Start-Byte  "+text, this, SLOT(testSlot(int)));
submenu->setItemParameter(id, mapInfoList.count() -1);
Danke nochmal. Wäre da alleine niemals drauf gekommen mit der QValueList-Klasse. Wieder eine Erfahrung reicher.
Gruß, Notwist
Antworten