Das Ziel in diesem Kapitel ist die Darstellung des Datums als “Tag.Monat.Jahr” bzw. “dd.MM.yyyy” und die Auswahl eines Tages über eine Kalender-Anzeige. Das sieht dann so aus:>
Hinweis: Seit dem letzten Artikel hat sich die Struktur des Models verändert, weil ich etwas vergessen hatte. Bitte auch in diesen Artikel auch reinschauen.
Das Ziel in diesem Kapitel ist die Darstellung des Datums als “Tag.Monat.Jahr” bzw. “tt.MM.jjjj” und die Auswahl eines Tages über eine Kalender-Anzeige. Das sieht dann so aus:
Quellcode: https://codeberg.org/Lerothas/QStyledItemDelegate-Demo
Eine neue Delegierten-Klasse
Unter QtCreator Wir erstellen eine neue Klasse über
- Rechtsklick auf die “Source Files” oder “Header Files”.
- “Add New…”
- “C/C++” -> “C++ Klasse” -> “Weiter”
- Klassenname: “DateDelegate” (oder was auch immer euch gefällt)
- Basisklasse: “” -> “QStyledItemDelegate” (die Grundlage für “alle” Delegaten)
- Weiter
Hier noch mal graphisch:
Wir haben dann eine leere Klasse ohne Funktionen – abgesehen vom Konstruktor, den wir heute aber in Ruhe lassen wollen. Wir fangen an mit der paint()-Funktion.
Ich könnt euch auch den Quellcode einfach aus meinem Repository kopieren.
paint()Header-Datei:
void paint(QPainter* painter, const QStyleOptionViewItem& option,
               const QModelIndex& index) const override;
Zur Erklärung:
- const override: QStyledItemDelegate bringt bereits eine const paint()-Funktion mit, diese überschreiben wir.
- void: keine Rückgabe
- malen: Name der Funktion
- QPainter* Maler: Das ist die Klasse, die das Objekt malt. Über die Veränerung der Eigenschaften des QPainters werden wir später sowas wie Farbe oder Strichdicke ändern.
- QStyleOptionViewItem: Darin steht sowas wie die Ausrichtung des Textes etc.
- QModelIndex: Der Index, dessen Daten dargestellt werden. Kommt vom Model und wird durch den jeweiligen View an den Delegate weitergegeben.
Nichts davon müssen wir irgendwie verändern, das macht alles das View (QTableView) für uns.
Quelldatei
void DateDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
    QRect rect = option.rect;
    rect.adjust( 5, 5, 0, 0 );
    painter->drawText( rect, index.data(0).toDate().toString("dd.MM.yyyy") );
}
Erklärung:
- QRect rect = option.rect : gibt uns das Rechteck in der Tabelle, die uns zur Verfügung steht. Es ist klug, in diesem Rechteck drinnen zu bleiben 😜
- rect.adjust( 5, 5, 0, 0 ) : Wir rücken etwas nach innen, damit wir nicht direkt am Rand des Delegierten schreiben. Dabei ist die Reihenfolgedx1, dy1, dx2 und dy2 für links, oben, rechts, unten.
- painter->drawText(…) : wir benutzen den QPainter, den wir vom QTableView (oder anderem View) erhalten, um Text zu schreiben.
- rekt : dieser Text soll in rect geschrieben werden.
- index.data(0).toDate() index.data(0).toDate(): holt Daten aus dem gegebenen Index und wandelt sie in einen QDate um. Die 0 steht dabei für die Daten. Für mehr siehe Qt::ItemDataRole.
- .toString(dd.MM.yyyy) : schreibt das Datum wie gewünscht aus.
Es ist etwas künstlich, das Datum zunächst anders zu schreiben und dann umgekehrt darstellen zu lassen. Aber für eine Sortierung der Daten ist es sehr hilfreich. Dafür werden wir das alles später aus einem Model holen, in dem es bspw. als Integer gespeichert ist. Fürür die Demo reicht uns der QString.
Das Datum wird jetzt korrekt dargestellt, aber wir haben noch keinen Editor. Würden wir jetzt doppel-klicken, könnten wir das Datum als Text bearbeiten, aber das ist wenig sinnvoll.
createEditor()
Header-Datei
QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option,
                          const QModelIndex &index) const override;
Wichtig:
- const override : wir sehen wieder, wir überschreiben eine existierende Funktion mit unserer eigenen.
- Es wird ein QWidget* zurückgegeben. D.h. wir müssen irgendein QWidget bzw. eine Unterklasse davon erstellen und zurückgeben. Dafür werden wir QDateEdit benutzen.
Der Rest ist entweder bekannt oder nicht wichtig, weil eh vom View gegeben.
Quelldatei
QWidget *DateDelegate::createEditor(QWidget *parent,
                                        const QStyleOptionViewItem &/* option */,
                                        const QModelIndex &/* index */) const
{
    QDateEdit *editor = new QDateEdit(parent); // 1
    editor->setDisplayFormat( "dd.MM.yyyy" ); // 2
    editor->setCalendarPopup( true ); // 3
    return editor; // 4
}
- Wir erstellen ein QDateEdit-Widget und geben parent mit, damit es den richtigen Bezug hat. Das parent ist das Eltern-Element im Grafikstack, nicht in der Klassen-Hierachie.
- Wir setzen die Darstellung auf dd.MM.yyyy analog zu paint().
- Wir setzen das Kalender-Popup, das den Kalender zur grafischen Auswahl eines Datums anzeigt, auf wahr.
- Wir geben den Editor als Ergebnis der Funktion zurück. Das Elternobjekt castet den jeweils passenden nach QWidget.
Lassen wir das jetzt laufen, so wird ein leerer QDateEdit erstellt, denn wir greifen an keiner Stelle auf QModelIndex bzw. QModelIndex::data() zu. Das passiert in einer getrennten Funktion!
setEditorData()
Header-Datei
void setEditorData(QWidget *editor, const QModelIndex &index) const override;
Hier geht nun der vorher erstellte Editor als QWidget* ein. Mehr gibt es dazu nicht zu sagen.
Quelldatei
void DateDelegate::setEditorData(QWidget *editor,
                                     const QModelIndex &index) const
{
    QDate value = index.data(0).toDate(); // 1
    QDateEdit *dateEdit = static_cast< QDateEdit* >(editor); // 2
    dateEdit->setDate( value ); // 3
}
- Wir holen uns analog zu paint() das Datum als QDate aus dem Text des Indexes.
- Wir casten das editor-Widget zu einem QDateEdit. (Hier musste ich vor und nach QDateEdit ein Leerzeichen setzen, weil sonst Bludit das ausgeblendet hat o.O)
- Wir setzen das Datum.
setModelData()
Was noch fehlt: Wie kommen geänderte Daten jetzt wieder an das Model?
Header-Datei
void setModelData(QWidget *editor, QAbstractItemModel *model,
                      const QModelIndex &index) const override;
Wir sehen hier neu: QAbstractItemModel. Das ist die Grund-Klasse für alle Models in Qt, interessiert uns für das Delegate aber nicht weiter. Hier könnte man natürlich auf eine bestimmte Art ein Model einschränken, aber das lohnt sich zumindest in meinem Tutorial nicht.
Quelldatei
void DateDelegate::setModelData(QWidget *editor, QAbstractItemModel *model,
                                    const QModelIndex &index) const
{
    QDateEdit *dateEdit = static_cast< QDateEdit* >(editor); // 1
    Date value = dateEdit->date(); // 2
    model->setData(index, value, Qt::EditRole); // 3
}
- Wir holen uns wieder das Editor-Widget und casten es in die Klasse QDateEdit.
- Wir holen uns das Datum als QDate über ->date().
- Wir setzen die Daten. Dabei greifen wir auf das Model zu, setzen die Daten des gegebenen Indexes auf den Wert value und zwar mit der Qt::EditRole, was das typische ist. (Qt::EditRole stimmt übrigens üblicherweise mit Qt::DisplayRole überein.)
Anstatt der EditRole könnte man auch das Design, das ToolTip,… ändern. Aber um die Dokumentation zu zitieren:
Qt::EditRole 2 Die Daten in einer für die Bearbeitung in einem Editor geeigneten Form. (QString)
Auch dabei bleiben wir.
Fazit
Wir haben einen einfachen Delegate gebaut, der ein Datum (gespeichert als QString) anzeigt und ein QDateEdit mit Kalender-Popup als Editor hat.
Quellcode
datedelegate.h
#ifndef DATEDELEGATE_H
#define DATEDELEGATE_H
#include 
class DateDelegate : public QStyledItemDelegate
{
    Q_OBJECT
public:
    DateDelegate();
    void paint(QPainter* painter, const QStyleOptionViewItem& option,
               const QModelIndex& index) const override;
    QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option,
                          const QModelIndex &index) const override;
    void setEditorData(QWidget *editor, const QModelIndex &index) const override;
    void setModelData(QWidget *editor, QAbstractItemModel *model,
                      const QModelIndex &index) const override;
};
#endif // DATEDELEGATE_H
datedelegate.cpp
#include "datedelegate.h"
#include 
#include 
#include 
DateDelegate::DateDelegate() {}
void DateDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const{
    QRect rect = option.rect;
    rect.adjust( 5, 5, 0, 0 );
    painter->drawText( rect, index.data(0).toDate().toString("dd.MM.yyyy") );
}
QWidget *DateDelegate::createEditor(QWidget *parent,
                                        const QStyleOptionViewItem &/* option */,
                                        const QModelIndex &/* index */) const
{
    QDateEdit *editor = new QDateEdit(parent);
    editor->setDisplayFormat( "dd.MM.yyyy" );
    editor->setCalendarPopup( true );
    return editor;
}
void DateDelegate::setEditorData(QWidget *editor,
                                     const QModelIndex &index) const
{
    QDate value = index.data(0).toDate();
    QDateEdit *dateEdit = static_cast< QDateEdit* >(editor);
    dateEdit->setDate( value );
}
void DateDelegate::setModelData(QWidget *editor, QAbstractItemModel *model,
                                    const QModelIndex &index) const
{
    QDateEdit *dateEdit = static_cast< QDateEdit* >(editor);
    QDate value = dateEdit->date();
    model->setData(index, value, Qt::EditRole);
}
void paint(QPainter* painter, const QStyleOptionViewItem& option,
               const QModelIndex& index) const override;void DateDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
    QRect rect = option.rect;
    rect.adjust( 5, 5, 0, 0 );
    painter->drawText( rect, index.data(0).toDate().toString("dd.MM.yyyy") );
}QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option,
                          const QModelIndex &index) const override;QWidget *DateDelegate::createEditor(QWidget *parent,
                                        const QStyleOptionViewItem &/* option */,
                                        const QModelIndex &/* index */) const
{
    QDateEdit *editor = new QDateEdit(parent); // 1
    editor->setDisplayFormat( "dd.MM.yyyy" ); // 2
    editor->setCalendarPopup( true ); // 3
    return editor; // 4
}void setEditorData(QWidget *editor, const QModelIndex &index) const override;void DateDelegate::setEditorData(QWidget *editor,
                                     const QModelIndex &index) const
{
    QDate value = index.data(0).toDate(); // 1
    QDateEdit *dateEdit = static_cast< QDateEdit* >(editor); // 2
    dateEdit->setDate( value ); // 3
}void setModelData(QWidget *editor, QAbstractItemModel *model,
                      const QModelIndex &index) const override;void DateDelegate::setModelData(QWidget *editor, QAbstractItemModel *model,
                                    const QModelIndex &index) const
{
    QDateEdit *dateEdit = static_cast< QDateEdit* >(editor); // 1
    Date value = dateEdit->date(); // 2
    model->setData(index, value, Qt::EditRole); // 3
}Qt::EditRole 2 Die Daten in einer für die Bearbeitung in einem Editor geeigneten Form. (QString)
#ifndef DATEDELEGATE_H
#define DATEDELEGATE_H
#include 
class DateDelegate : public QStyledItemDelegate
{
    Q_OBJECT
public:
    DateDelegate();
    void paint(QPainter* painter, const QStyleOptionViewItem& option,
               const QModelIndex& index) const override;
    QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option,
                          const QModelIndex &index) const override;
    void setEditorData(QWidget *editor, const QModelIndex &index) const override;
    void setModelData(QWidget *editor, QAbstractItemModel *model,
                      const QModelIndex &index) const override;
};
#endif // DATEDELEGATE_H
#include "datedelegate.h"
#include 
#include 
#include 
DateDelegate::DateDelegate() {}
void DateDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const{
    QRect rect = option.rect;
    rect.adjust( 5, 5, 0, 0 );
    painter->drawText( rect, index.data(0).toDate().toString("dd.MM.yyyy") );
}
QWidget *DateDelegate::createEditor(QWidget *parent,
                                        const QStyleOptionViewItem &/* option */,
                                        const QModelIndex &/* index */) const
{
    QDateEdit *editor = new QDateEdit(parent);
    editor->setDisplayFormat( "dd.MM.yyyy" );
    editor->setCalendarPopup( true );
    return editor;
}
void DateDelegate::setEditorData(QWidget *editor,
                                     const QModelIndex &index) const
{
    QDate value = index.data(0).toDate();
    QDateEdit *dateEdit = static_cast< QDateEdit* >(editor);
    dateEdit->setDate( value );
}
void DateDelegate::setModelData(QWidget *editor, QAbstractItemModel *model,
                                    const QModelIndex &index) const
{
    QDateEdit *dateEdit = static_cast< QDateEdit* >(editor);
    QDate value = dateEdit->date();
    model->setData(index, value, Qt::EditRole);
}
 
				
