switch über QObject.inherits
-
mastershybby
- Beiträge: 31
- Registriert: 24. Dezember 2008 23:10
switch über QObject.inherits
Hallo,
ich versuche nun schon seit einiger Zeit eine Sinnvolle struktur in mein Programm zu bringen. Dabei habe ich folgendes Problem:
Ich habe mehrere QWidgets (z.B. QPushButton, QSpinBox etc) welche auf einen slot aufrufen. In diesem Slot sollte dann Objektspezifisch gehandelt werden.
Nun schön wäre natürlich ein Switch case welches alle Objekte beinhaltet. Da dies aber nicht von QT/C++ unterstützt wird, wollte ich fragen ob jemand eine bessere Idee, als 100 if-else Abfragen, hat?
Gruss
mastershybby
ich versuche nun schon seit einiger Zeit eine Sinnvolle struktur in mein Programm zu bringen. Dabei habe ich folgendes Problem:
Ich habe mehrere QWidgets (z.B. QPushButton, QSpinBox etc) welche auf einen slot aufrufen. In diesem Slot sollte dann Objektspezifisch gehandelt werden.
Nun schön wäre natürlich ein Switch case welches alle Objekte beinhaltet. Da dies aber nicht von QT/C++ unterstützt wird, wollte ich fragen ob jemand eine bessere Idee, als 100 if-else Abfragen, hat?
Gruss
mastershybby
100 Unterschiedliche Widgets auf einen einzigen SLOT connecten und dann den Sender (aus den 100) wieder zu ermitteln um was spezifisches zu machen ist irgendwie auch kein so günstiges Design. Kannst du das nicht irgendwie besser managen?
Ansonsten gehen für switch keine Klassentypen, switch braucht einen integralen Typ (char, int, ...). Wenn du also wirklich dein Konstrukt beibehalten willst, gehts mit if/else.
Ansonsten gehen für switch keine Klassentypen, switch braucht einen integralen Typ (char, int, ...). Wenn du also wirklich dein Konstrukt beibehalten willst, gehts mit if/else.
Was soll denn Qt da nicht unterstützen????
Code: Alles auswählen
if(qobject_cast<QPushbutton *>(QObject::sender()) || (qobject_cast<QAction *>(QObject::sender()) == ui->action_Beenden))-
mastershybby
- Beiträge: 31
- Registriert: 24. Dezember 2008 23:10
Zuerst vielen Dank für die Antworten.
@franzf:
das bessere managen sehe ich nicht, da ich eine Art Messageing-System aufbaue. Dabei könnten Messages von allen Objekten kommen und müssen dann verschieden verarbeitet werden. Ebenfalls ist nicht bekannt wieviele Objekte senden. Deshalb wird ein klareres Managen meiner Meinung nach nicht möglich sein.
@AuE:
Wie auch schon franzf schreibt, unterstützt QT bzw. C++ nur Switch mit integralen Typen (char, int, ...) aber eben nicht QObjects o.ä.
Das ich mit if und else operieren kann ist klar aber nicht schön (Code-Style usw.).
@franzf:
das bessere managen sehe ich nicht, da ich eine Art Messageing-System aufbaue. Dabei könnten Messages von allen Objekten kommen und müssen dann verschieden verarbeitet werden. Ebenfalls ist nicht bekannt wieviele Objekte senden. Deshalb wird ein klareres Managen meiner Meinung nach nicht möglich sein.
@AuE:
Wie auch schon franzf schreibt, unterstützt QT bzw. C++ nur Switch mit integralen Typen (char, int, ...) aber eben nicht QObjects o.ä.
Das ich mit if und else operieren kann ist klar aber nicht schön (Code-Style usw.).
Es geht immer besser. Und wenn du dir deine Beschreibung der Problematik nochmal ansiehst, kommst du vielleicht darauf, wie man das machen tut:mastershybby hat geschrieben:das bessere managen sehe ich nicht, da ich eine Art Messageing-System aufbaue. Dabei könnten Messages von allen Objekten kommen und müssen dann verschieden verarbeitet werden. Ebenfalls ist nicht bekannt wieviele Objekte senden. Deshalb wird ein klareres Managen meiner Meinung nach nicht möglich sein.
"Ich möchte je nach Klasse eine andere Aktion ausführen".
(inherits bezieht sich ja nur auf Klassen)
Na, klingelts? Genau, Polymorphie
Definier dir ein Interface mit einer virtuellen Funktion "putMessage(Messager& m)", und alle deine Klassen, die sich am Messaging-System beteiligen wollen, implementieren diese Funktion dann. Da braucht es nichtmal QObject + SIGNAL/SLOT.
Im Übrigen wüsste ich jetzt nicht, wie du eine Message von einem QPushButton speziell behandeln willst. Da solltest du dir vllt. auch mal "QObject::objectName" anschauen.
Sollte das immer noch nicht das sein was du dir wünschst poste doch mal konkret was das für "Messages" werden sollen (SIGNAL/SLOT ist ja an sich schon ein Message-Sytem), wieso ein QPushButton/QLineEdit eine Message senden können soll, etc.
Dein Versuch über ihnerits() ist auch nicht mal ungefährlich, da du bei der Reihenfolge der if/else GENAU aufpassen musst, dass du auch die Vererbungshierarchie korrekt auffängst. Ein QWidget vor einem QPushButton abgefragt und du wirst dich wundern dass keine Messages eines QPushButtons auftauchen
-
mastershybby
- Beiträge: 31
- Registriert: 24. Dezember 2008 23:10
@franzf:
ok ich sehe schon du drängst mich meinen Programmaufbau nochmals zu überarbeiten
.
Gut wenn ich schon dabei bin kann ich das ganze Problem auch ein Wenig herunterbrechen. Dazu ein vereinfachtes Beispiel welches auf das gleiche Problem führt:
Aufgabe soll es sein, alle Widgets(QPushButton QLineEdit usw.) welche sich auf dem MainWindow befinden bei deren Änderung bestimmt einzufärben.
Zumbeispiel: Alle QPushButtons werden beim Drücken Rot. Alle LineEdits beim bearbeiten Grün, alle SpinBoxen Blau usw.
Meine Lösung wäre ein gemeinsamer Slot:
Der ganze connect ablauf lautet dann:
Ist das nicht geschickt?? und wenn Nein Was könnte ich besser machen?
ok ich sehe schon du drängst mich meinen Programmaufbau nochmals zu überarbeiten
Gut wenn ich schon dabei bin kann ich das ganze Problem auch ein Wenig herunterbrechen. Dazu ein vereinfachtes Beispiel welches auf das gleiche Problem führt:
Aufgabe soll es sein, alle Widgets(QPushButton QLineEdit usw.) welche sich auf dem MainWindow befinden bei deren Änderung bestimmt einzufärben.
Zumbeispiel: Alle QPushButtons werden beim Drücken Rot. Alle LineEdits beim bearbeiten Grün, alle SpinBoxen Blau usw.
Meine Lösung wäre ein gemeinsamer Slot:
Code: Alles auswählen
void MainWindow::colorSlot(){
QObject *o = QObject::sender();
if(o->inherits("QLineEdit")){
((QLineEdit*)o)->setPalette(green);
}
else{
if(o->inherits("QSpinBox")){
((QSpinBox*)o)->setPalette(blue);
}
else{
if(o->inherits("QPushButton")){
((QPushButton*)o)->setPalette(red);
}
}
}
}
Code: Alles auswählen
widgets = this->findChildren<QWidget *>();
for(int i = 0; i<widgets.size(); i++) {
if(widget[i]->inherits("QLineEdit")){
connect(((QLineEdit *)widgets[i]),SIGNAL(textEdited(QString)),this,SLOT(colorSlot()));
...(weiter mit den anderen Widgets)
}
}
Code: Alles auswählen
class WidgetChanger {
public:
void change(QLineEdit* w) { w->setPalette(lineEditPalette); }
void change(QPushButton* w) { w->setPalette(pushButtonPalette); }
};Vorteil: Es ist schneller als mit Strings vergleichen und sicherer. Vor allem sparst du dir die casts (die BTW. C sind, und C-Casts sind in C++ böse -> dynamic_cast/static_cast resp. qobject_cast verwenden)
// edit:
Sry, ist natürlich Mist
Wie viele Widgetklassen sollen denn unterschieden werden?
-
mastershybby
- Beiträge: 31
- Registriert: 24. Dezember 2008 23:10
Dann würde dein SignalMapper-part etwa so aussehen??
Code: Alles auswählen
QPushButton* buttons = this->findChildren<QPushButton *>();
for(int i = 0; i<buttons.size(); i++) {
connect(buttons[i], SIGNAL(clicked()), signalMapper, SLOT(map()));
signalMapper->setMapping(button[i], button[i]);
}
//(Wiederholung für alle anderen möglichen widgets)
connect(signalMapper, SIGNAL(mapped(QWidget *)), this, SLOT(change( QWidget *)));
-
mastershybby
- Beiträge: 31
- Registriert: 24. Dezember 2008 23:10
Nun ja ich schätze so:// edit:
Sry, ist natürlich Mist Sad Du hast ja am Ende nur ein QObject*/QWidget*, und da funktiert die Auflösung nicht. Müsstest du wieder mit className+cast ran. Da du die Widgets (den Code) nicht selber verändern kannst um eine virtuelle Methode hinzuzufügen, musst du wohl mit dem className arbeiten...
Wie viele Widgetklassen sollen denn unterschieden werden?
QLineEdit
QSpinBox
QCheckBox
QGroupBox
QTextEdit
Dies sind sicher mal die HauptWidgets die Unterstützt werden müssen.
Ich bin mir immer noch nicht sicher was du genau brauchst.. das Problem "ich-bin-ein-LineEdit-und-werde-nach-dem-Editieren-Rot" (das Objekt hat schon alle Infos) ist nicht dasselbe wie "ich-bin-ein-Button-und-auf-Klick-sollen-sich-alle-anderen-LineEdits-Rot-färben" (Message-System).
Wenn du an einer Basis-Software arbeitest, welche häufig wiederverwendet wird (weil das Verhalten mit der Einfärbung "Firmenstandard" ist), würde ich eigene Designer-Plugins machen. Dies hätte folgende Vorteile
- das MainView muss nichts davon wissen
- im konkreten Beispiel kann die Farbe von LineEdit zu LineEdit unterschiedlich sein und kann im Designer gewählt werden
- es können beliebige weitere "Firmenspezifische"-Properties eingebaut werden
- In einem gemeinsamen Interface lassen sich beliebige andere Funktionen unterbringen (auch ein Message-System)
Wenn du das nicht brauchst (weil es einmalig ist) und daher basteln möchtest: kein Problem.. aber um Casts wirst du nicht herumkommen. Du kannst das Gebastel nur noch etwas mehr oder weniger elegant hinkriegen:
Im konkreten Beispiel könntest du evt. ein "Typ-auf-Palette-Mapping" machen.. so ungefaehr:
Falls sowas möglich ist könnte das Aufsetzen des Mappings noch in eine Template-Methode verpackt werden, worauf sich der Ctor verkleinert:
Falls die Palette nur ein Beispiel war und du mehrere Slots hast, könntest auch die Operation in typenspezifische Klassen auslagern und diese z.B. in einem QMap sammeln. Z.B.
Aber eben... alles kein schönes OOP..
hth..
Wenn du an einer Basis-Software arbeitest, welche häufig wiederverwendet wird (weil das Verhalten mit der Einfärbung "Firmenstandard" ist), würde ich eigene Designer-Plugins machen. Dies hätte folgende Vorteile
- das MainView muss nichts davon wissen
- im konkreten Beispiel kann die Farbe von LineEdit zu LineEdit unterschiedlich sein und kann im Designer gewählt werden
- es können beliebige weitere "Firmenspezifische"-Properties eingebaut werden
- In einem gemeinsamen Interface lassen sich beliebige andere Funktionen unterbringen (auch ein Message-System)
Wenn du das nicht brauchst (weil es einmalig ist) und daher basteln möchtest: kein Problem.. aber um Casts wirst du nicht herumkommen. Du kannst das Gebastel nur noch etwas mehr oder weniger elegant hinkriegen:
Im konkreten Beispiel könntest du evt. ein "Typ-auf-Palette-Mapping" machen.. so ungefaehr:
Code: Alles auswählen
// CTor
lineedits = this->findChildren<QLineEdits*>();
foreach..
signalMapper->setMapping(edit, red);
// im slot:
void ... ::colorSlot(const QPalette &p) {
QWidget *w = dynamic_cast<QWidget*>(sender()); Q_ASSERT(w);
w->setPalette(p);
}
Code: Alles auswählen
setupMapping<QLineEdit*>(red);
setupMapping<QCheckBox*>(green);
...
Code: Alles auswählen
// CTor:
// mOperations ist ein QMap<QString,AbstractOperations*>
mOperations.insert("QLineEdit",new LineEditOperations());
// Slot 1:
void ...:colorSlot() {
Q_ASSERT(mOperations.contains(sender()->metaObject()->className()));
mOperations[sender()->metaObject()->className()]->setColor(sender());
}
// in LineEditOperations:
voi LineEditOperations::setColor(QObject *o) {
QLineEdit *e = dynamic_cast<QLineEdit*>(o); Q_ASSERT(e);
e->setPalette(red);
e->nochAndereAufgaben...
}
hth..
-
mastershybby
- Beiträge: 31
- Registriert: 24. Dezember 2008 23:10
@solarix:
Also das color Beispiel führt eigentlich nur auf das selbe problem. Es ist einfach nur einfacher zu verstehen, aber ich versuchs nochmals über meinen code:
Mein Hauptproblem liegt im "Sortieren von QObjects.
Stell dir vor, du hast eine Funktion welche Messages empfängt. Diese Messages beinhalten das empfänger Widget. Zum Beispiel:
message1 msg"12" QWidget"spin_box_2"
message2 msg"Hallo" QWidget"line_edit_10"
usw.
Was ich nun machen muss ist ja offensichtlich falls es ein line_edit widget ist, soll die msg mit setText gesetzt werden und falls es ein spin_box widget ist, soll die msg mit setValue gesetzt werden.
Gleichzeitig sollen alle Widgets beim editieren rot werden und bei fertiger editierung wieder eine msg absetzten (was zum gleichen Problem wie beim empfangen führt).
Also das color Beispiel führt eigentlich nur auf das selbe problem. Es ist einfach nur einfacher zu verstehen, aber ich versuchs nochmals über meinen code:
Mein Hauptproblem liegt im "Sortieren von QObjects.
Stell dir vor, du hast eine Funktion welche Messages empfängt. Diese Messages beinhalten das empfänger Widget. Zum Beispiel:
message1 msg"12" QWidget"spin_box_2"
message2 msg"Hallo" QWidget"line_edit_10"
usw.
Was ich nun machen muss ist ja offensichtlich falls es ein line_edit widget ist, soll die msg mit setText gesetzt werden und falls es ein spin_box widget ist, soll die msg mit setValue gesetzt werden.
Gleichzeitig sollen alle Widgets beim editieren rot werden und bei fertiger editierung wieder eine msg absetzten (was zum gleichen Problem wie beim empfangen führt).
Ich denke es geht ohne casts, usw...
Alles über Properties 
Ein Widget hat eh ne Palette. Aber das würde ich nicht an dieses Message-System koppeln. Das solltest du intern im MainWindow regeln. Editieren ist normalerweise fertig, wenn ein "save"-Button geklickt wurde. Dann würde ich einen bool setzen "saved" auf true. Dann an das SIGNAL QApplication::focusChanged hängen, und da entsprechend Palette neu setzen, wenn gerade saved auf true ist.
Code: Alles auswählen
void sendMessage(int value, QWidget* widget) {
widget->setProperty("value", value);
}
void sendMessage(QString text, QWidget* widget) {
widget->setProperty("text", text);
}
void sendMessage(bool val, QWidget* widget) {
widget->setProperty("checked", cal);
}Ein Widget hat eh ne Palette. Aber das würde ich nicht an dieses Message-System koppeln. Das solltest du intern im MainWindow regeln. Editieren ist normalerweise fertig, wenn ein "save"-Button geklickt wurde. Dann würde ich einen bool setzen "saved" auf true. Dann an das SIGNAL QApplication::focusChanged hängen, und da entsprechend Palette neu setzen, wenn gerade saved auf true ist.
-
mastershybby
- Beiträge: 31
- Registriert: 24. Dezember 2008 23:10
ok das sieht nach einer sehr schönen Lösung aus, aber jetzt wird die unterscheidung anhand der zuübergebenden datentyps gemacht... das in meinem fall nicht brauchbar, da ich bei einer combobox z.b. ein int index übergebe und bei der spinbox ebenfalls ein int.
aber wie sieht das aus, wenn ich zwar in der in der message ein WidgetPointer ablege, beim aufruf zur bearbeitung jedoch eine QWidget spezifische routine schreibe?
Etwa so:
würde das gehen? oder verliert meine Msg die widget-typen-information?
oder ist das genau das was du vorhin gesagt hast würde nicht gehen wegen dem auflösen?
aber wie sieht das aus, wenn ich zwar in der in der message ein WidgetPointer ablege, beim aufruf zur bearbeitung jedoch eine QWidget spezifische routine schreibe?
Etwa so:
Code: Alles auswählen
msguser.MsgAnswer(msg, msg.widget);
msguser::MsgAnswer(Msg* msg, QLineEdit*){...};
msguser::MsgAnswer(Msg* msg, QSpinBox*){...};
msguser::MsgAnswer(Msg* msg, QCheckBox*){...};
msguser::MsgAnswer(Msg* msg, QComboBox*){...};
etc.
oder ist das genau das was du vorhin gesagt hast würde nicht gehen wegen dem auflösen?