Seite 1 von 1

QDataWidgetMapper, QComboBox und eigenes Delegate

Verfasst: 30. April 2009 16:27
von Baz
Hallo,

ich habe mir für einen DataWidgetMapper ein eigenes Delegate geschrieben. Im Grunde genommen macht es das Gleiche wie ein QSqlRelationalDelegate mit dem Unterschied, dass es auch die Möglichkeit gibt keinen Eintrag auszuwählen.

Code: Alles auswählen

void LSoftComboBoxDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
{
	QComboBox* combo = qobject_cast<QComboBox*>(editor);
	for (int i = 0; i < combo->model()->rowCount(); i++)
	{
		if (combo->model()->index(i, 0).data().toInt() == index.data().toInt())
		{
			combo->setCurrentIndex(i);
			return;
		}
	}
	combo->setCurrentIndex(-1);
}

void LSoftComboBoxDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
{
	if (!index.isValid())
		return;

	QComboBox* combo = qobject_cast<QComboBox*>(editor);

	if (!combo->currentText().isEmpty())
	{
		for (int i = 0; i < combo->model()->rowCount(); i++)
		{
			if (combo->currentText() == combo->model()->index(i, combo->modelColumn()).data().toString())
			{
				model->setData(index, combo->model()->index(i, 0).data());
				return;
			}
		}
	}
	model->setData(index, 0);
}
Ich weiß, dass das Ganze nicht so wirklich effizient ist, aber es ist momentan auch eher zum Testen. Mein Problem ist, dass die setModelData-Funktion erst aufgerufen wird, wenn ich den Datensatz wechsle. Ich hätte aber gerne, dass in dem Moment, in dem der User einen anderen Eintrag wählt, die Methode aufgerufen wird, da ich die Daten danach brauche. Gibt es da irgendeine Möglichkeit?

Mein Tablemodel hat als EditBehavior OnFieldChange und die ComboBox wird mit setModel(...) gefüllt.

Gruß Michael

Verfasst: 7. Mai 2009 16:12
von Baz
Ich stelle die Frage vielleicht mal anders ;). Ich habe mir das Verhalten von einem QLineEdit angeschaut. Nachdem das Widget den Fokus verliert, wird das Delegate aufgerufen. Wenn ich das jetzt richtig verstanden habe, wird dabei ein Eventfilter benutzt, allerdings passiert bei der ComboBox auch beim Verlieren des Fokus nichts. Hat irgendjemand damit mal Erfahrung gemacht - müsste ich mir noch meinen eigenen Eventfilter basteln?

Verfasst: 7. Mai 2009 20:52
von Baz
Okay, ich bin noch einen Schritt weiter, es liegt wohl weniger an der ComboBox, als viel mehr an der Einstellung des EditBehavior. Mit OnManualSubmit funktioniert der Spaß, mit OnFieldChange wird die setModelData() Funktion genau einmal aufgerufen (beim ersten Mal, wenn ein Widget den Fokus verliert).

Verfasst: 12. April 2010 15:11
von petric
Hat sich das Problem geloest ?

Ich stolpere gerade auch darueber - und ich will nicht auf "manual submit" gehen.

Verfasst: 12. April 2010 15:44
von Baz
Nein nicht wirklich... hatte bei Trolltech einen Bugreport eingereicht, wurde auch akzeptiert, aber passiert ist bisher mal nichts... Falls du magst, kann ich die TrackerId mal raussuchen.

Ich bin damals dann eben auf Manual Submit umgestiegen, weil ich die Mapper unbedingt brauchte...

Gruß Michael

Verfasst: 12. April 2010 19:18
von petric
Schade, dass da nix passiert ist.

Wenn ich auf Manual Submit umstelle muss ich alle Widgets auf "Focus lost" ueberwachen ...
Oder hast du das anders geloest (neben ManualSubmit-Umstellung) ?

Bei LineEdits habe ich was aehnliches. Nur die erste Aenderung fuehrt zu einem setModelData-Aufruf im Delegate. Ich benutze hier das originale QSqlRelationalDelegate - also nichts selbstgestricktes.

Verfasst: 13. April 2010 00:29
von Baz
Das Lustige ist ja, dass wenn man das ganze auf Manual Submit hat, sich der WidgetMapper verhält wie er sollte, also wenn ein Widget den Focus verliert, wird der Inhalt ins Model geschrieben - da muss also nichts zusätzlich überwacht werden. Ich hätte nur eben gerne alle Daten sofort immer in der SQLite Db gehabt, aber gut, damit musste ich mich dann eben abfinden...

Verfasst: 13. April 2010 18:04
von petric
Nur landen die Daten dann nicht unmitttelbar im Model.

Dass das Model (hier: QSqlRelationalModel zu einer MySQL DB) seine Indexes verliert ist gut sichtbar, wenn man dieses Model gleichzeitig via TableView und DataMapper verwendet. Bei einer Aenderung eines Datenfeldes (via DataMapper verlinkt) geht die Selektion in der Tableview verloren (wird aufgehoben).

Ich denke gerade darueber nach einen eigenen DataWidgetMapper zu schreiben ...

Verfasst: 13. April 2010 18:05
von Baz
Mach mal und sag dann Bescheid :D

Verfasst: 14. April 2010 01:43
von petric
Inzwischen hatte ich eine andere Idee.

Ich leite QDataWidgetMapper ab und haenge mich in das closeEditor-Signal der Widgets dran.
Daraufhin sende ich ein eigenes Signal mit dem Index. Dann kann ich in meiner Applikation den ModelIndex wieder stabilisieren.

Unten meine Testimplementierung - einfach statt der QDataWidgetModel-Instanz einsetzen. Im der Applikation muss man halt das dataChanged-Signal anbinden und dort den Index des DataWidgetMappers neu setzen. Dann wird auch im Delegate die Methode setModelData bei wiederholten Aenderungen auch wieder aufgerufen.

DataModelWidgetMapper.h:

Code: Alles auswählen

#ifndef DATAWIDGETMAPPER_H
#define DATAWIDGETMAPPER_H

#include <QtGui/QDataWidgetMapper>

class QAbstractItemDelegate;


class DataWidgetMapper : public QDataWidgetMapper
{
  Q_OBJECT

  public:
    explicit DataWidgetMapper (QObject *parent = 0);

    void setItemDelegate (QAbstractItemDelegate *);

  signals:
    void dataChanged (int);

  private slots:
    void editorClose (QWidget *);
};

#endif // DATAWIDGETMAPPER_H
DataWidgetMapper.cpp:

Code: Alles auswählen

#include "datawidgetmapper.h"
#include <QtGui/QAbstractItemDelegate>


DataWidgetMapper::DataWidgetMapper (QObject *parent)
: QDataWidgetMapper (parent)
{
}

void DataWidgetMapper::setItemDelegate (QAbstractItemDelegate *delegate)
{
  if (itemDelegate ())
    disconnect (itemDelegate (), SIGNAL (closeEditor (QWidget *, QAbstractItemDelegate::EndEditHint)),
                this, SLOT (editorClose (QWidget *)));

  QDataWidgetMapper::setItemDelegate (delegate);

  if (delegate)
    connect (delegate, SIGNAL (closeEditor (QWidget *, QAbstractItemDelegate::EndEditHint)),
             this, SLOT (editorClose (QWidget *)));
}

void DataWidgetMapper::editorClose (QWidget *widget)
{
  int index = mappedSection (widget);
  if (index >= 0)
    emit dataChanged (index);
}
Das Teil ist natuerlich nicht perfekt - z.B. sollte man in abgeleiteten Klassen nicht-virtuelle Methoden nicht ueberschreiben ...

Ich hoffe es hilft jemand.

Gruss
Petric

Verfasst: 21. April 2010 14:39
von Baz
Hi,

das ist ne gute Idee, momentan fehlt mir etwas die Zeit mich damit eingehender zu beschäftigen, aber mich würde dein Endprodukt sehr interessieren ;). Falls ich es zeitlich mal schaffe, schaue ich ob ich ggf auch was Sinnvolles noch beisteuern kann.

Gruß Michael