QDataWidgetMapper, QComboBox und eigenes Delegate

Alles rund um die Programmierung mit Qt
Antworten
Baz
Beiträge: 67
Registriert: 22. August 2007 14:21

QDataWidgetMapper, QComboBox und eigenes Delegate

Beitrag 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
Baz
Beiträge: 67
Registriert: 22. August 2007 14:21

Beitrag 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?
Baz
Beiträge: 67
Registriert: 22. August 2007 14:21

Beitrag 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).
petric
Beiträge: 23
Registriert: 2. September 2008 17:42

Beitrag von petric »

Hat sich das Problem geloest ?

Ich stolpere gerade auch darueber - und ich will nicht auf "manual submit" gehen.
Baz
Beiträge: 67
Registriert: 22. August 2007 14:21

Beitrag 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
petric
Beiträge: 23
Registriert: 2. September 2008 17:42

Beitrag 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.
Baz
Beiträge: 67
Registriert: 22. August 2007 14:21

Beitrag 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...
petric
Beiträge: 23
Registriert: 2. September 2008 17:42

Beitrag 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 ...
Baz
Beiträge: 67
Registriert: 22. August 2007 14:21

Beitrag von Baz »

Mach mal und sag dann Bescheid :D
petric
Beiträge: 23
Registriert: 2. September 2008 17:42

Beitrag 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
Baz
Beiträge: 67
Registriert: 22. August 2007 14:21

Beitrag 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
Antworten