SQL Widget Mapper Beispiel
Das SQL Widget Mapper Beispiel zeigt, wie man Informationen aus einer Datenbank auf Widgets in einem Formular abbildet.
Im Combo Widget Mapper Beispiel haben wir gezeigt, wie man eine benannte Zuordnung zwischen einem Widget Mapper und einem QComboBox Widget mit einem speziellen Modell verwendet, um Werte im Modell mit einer Liste von Auswahlmöglichkeiten zu verbinden.
Auch hier erstellen wir eine Klasse Window
mit einer fast identischen Benutzeroberfläche, die ein Kombinationsfeld bereitstellt, mit dem die Adressen als "Privat", "Arbeit" oder "Sonstige" klassifiziert werden können. Anstatt jedoch ein separates Modell für diese Adresstypen zu verwenden, nutzen wir eine Datenbanktabelle für die Beispieldaten und eine weitere für die Adresstypen. Auf diese Weise speichern wir alle Informationen am selben Ort.
Definition der Fensterklasse
Die Klasse bietet einen Konstruktor, einen Slot, um die Schaltflächen auf dem neuesten Stand zu halten, und eine private Funktion, um das Modell einzurichten:
class Window : public QWidget { Q_OBJECT public: Window(QWidget *parent = nullptr); private slots: void updateButtons(int row); private: void setupModel(); QLabel *nameLabel; QLabel *addressLabel; QLabel *typeLabel; QLineEdit *nameEdit; QTextEdit *addressEdit; QComboBox *typeComboBox; QPushButton *nextButton; QPushButton *previousButton; QSqlRelationalTableModel *model; QItemSelectionModel *selectionModel; QDataWidgetMapper *mapper; int typeIndex; };
Neben dem Objekt QDataWidgetMapper und den Steuerelementen, die die Benutzeroberfläche bilden, verwenden wir ein QStandardItemModel, um unsere Daten zu speichern, und ein QStringListModel, um Informationen über die Adresstypen zu speichern, die auf die Daten jeder Person angewendet werden können.
Implementierung der Fensterklasse
Der erste Schritt des Konstruktors der Klasse Window
besteht darin, das Modell für die Beispieldaten einzurichten. Da dies ein wichtiger Teil des Beispiels ist, werden wir es zuerst betrachten.
Das Modell wird in der Funktion setupModel()
des Fensters initialisiert. Hier erstellen wir eine SQLite-Datenbank, die eine Tabelle "Person" mit den Feldern Primärschlüssel, Name, Adresse und Typ enthält.
void Window::setupModel() { QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE"); db.setDatabaseName(":memory:"); if (!db.open()) { QMessageBox::critical(0, tr("Cannot open database"), tr("Unable to establish a database connection.\n" "This example needs SQLite support. Please read " "the Qt SQL driver documentation for information how " "to build it."), QMessageBox::Cancel); return; } QSqlQuery query; query.exec("create table person (id int primary key, " "name varchar(20), address varchar(200), typeid int)"); query.exec("insert into person values(1, 'Alice', " "'<qt>123 Main Street<br/>Market Town</qt>', 101)"); query.exec("insert into person values(2, 'Bob', " "'<qt>PO Box 32<br/>Mail Handling Service" "<br/>Service City</qt>', 102)"); query.exec("insert into person values(3, 'Carol', " "'<qt>The Lighthouse<br/>Remote Island</qt>', 103)"); query.exec("insert into person values(4, 'Donald', " "'<qt>47338 Park Avenue<br/>Big City</qt>', 101)"); query.exec("insert into person values(5, 'Emma', " "'<qt>Research Station<br/>Base Camp<br/>" "Big Mountain</qt>', 103)");
In jeder Zeile der Tabelle fügen wir Standardwerte für diese Felder ein, einschließlich der Werte für die Adresstypen, die den Adresstypen entsprechen, die in einer separaten Tabelle gespeichert sind.
Wir erstellen eine Tabelle "addresstype", die die in der Tabelle "person" verwendeten Bezeichner und die entsprechenden Zeichenketten enthält:
query.exec("create table addresstype (id int, description varchar(20))"); query.exec("insert into addresstype values(101, 'Home')"); query.exec("insert into addresstype values(102, 'Work')"); query.exec("insert into addresstype values(103, 'Other')"); model = new QSqlRelationalTableModel(this); model->setTable("person"); model->setEditStrategy(QSqlTableModel::OnManualSubmit); typeIndex = model->fieldIndex("typeid"); model->setRelation(typeIndex, QSqlRelation("addresstype", "id", "description")); model->select(); }
Das Feld "typeid" in der Tabelle "person" ist mit dem Inhalt der Tabelle "addresstype" über eine Relation in einer QSqlRelationalTableModel verbunden. Diese Art von Modell erledigt die gesamte notwendige Arbeit, um die Daten in einer Datenbank zu speichern, und ermöglicht es außerdem, beliebige Relationen als eigenständige Modelle zu verwenden.
In diesem Fall haben wir eine Relation für das Feld "typeid" in der Tabelle "person" definiert, die es mit dem Feld "id" in der Tabelle "addresstype" verknüpft und die bewirkt, dass der Inhalt des Feldes "description" überall dort verwendet wird, wo die "typeid" dem Benutzer angezeigt wird. (Siehe die Dokumentation QSqlRelationalTableModel::setRelation() für weitere Einzelheiten).
Der Konstruktor der Klasse Window
kann in drei Teilen erklärt werden. Im ersten Teil richten wir das Modell ein, das die Daten enthält, und anschließend die Widgets, die für die Benutzeroberfläche verwendet werden:
Window::Window(QWidget *parent) : QWidget(parent) { setupModel(); nameLabel = new QLabel(tr("Na&me:")); nameEdit = new QLineEdit(); addressLabel = new QLabel(tr("&Address:")); addressEdit = new QTextEdit(); typeLabel = new QLabel(tr("&Type:")); typeComboBox = new QComboBox(); nextButton = new QPushButton(tr("&Next")); previousButton = new QPushButton(tr("&Previous")); nameLabel->setBuddy(nameEdit); addressLabel->setBuddy(addressEdit); typeLabel->setBuddy(typeComboBox);
Wir erhalten ein Modell für das Kombinationsfeld aus dem Hauptmodell, basierend auf der Beziehung, die wir für das Feld "typeid" eingerichtet haben. Der Aufruf der Funktion setModelColumn() des Kombinationsfeldes wählt das Feld in dem Feld des Modells aus, das angezeigt werden soll.
Beachten Sie, dass dieser Ansatz dem im Combo Widget Mapper-Beispiel verwendeten ähnlich ist, da wir ein Modell für das Kombinationsfeld einrichten. In diesem Fall erhalten wir jedoch ein Modell, das auf einer Beziehung in QSqlRelationalTableModel basiert, anstatt ein separates Modell zu erstellen.
Als nächstes richten wir den Widget-Mapper ein, der jedes Eingabe-Widget mit einem Feld im Modell verknüpft:
QSqlTableModel *relModel = model->relationModel(typeIndex); typeComboBox->setModel(relModel); typeComboBox->setModelColumn(relModel->fieldIndex("description")); mapper = new QDataWidgetMapper(this); mapper->setModel(model); mapper->setItemDelegate(new QSqlRelationalDelegate(this)); mapper->addMapping(nameEdit, model->fieldIndex("name")); mapper->addMapping(addressEdit, model->fieldIndex("address")); mapper->addMapping(typeComboBox, typeIndex);
Für das Kombinationsfeld kennen wir den Index des Feldes im Modell bereits aus der Funktion setupModel()
. Wir verwenden QSqlRelationalDelegate als Proxy zwischen dem Mapper und den Eingabe-Widgets, um die "typeid"-Werte im Modell mit denen im Modell des Kombinationsfeldes abzugleichen und das Kombinationsfeld mit Beschreibungen anstelle von Integer-Werten zu füllen.
Als Ergebnis kann der Benutzer ein Element aus dem Kombinationsfeld auswählen, und der zugehörige Wert wird in das Modell zurückgeschrieben.
Der Rest des Konstruktors richtet Verbindungen und Layouts ein:
connect(previousButton, &QPushButton::clicked, mapper, &QDataWidgetMapper::toPrevious); connect(nextButton, &QPushButton::clicked, mapper, &QDataWidgetMapper::toNext); connect(mapper, &QDataWidgetMapper::currentIndexChanged, this, &Window::updateButtons); QGridLayout *layout = new QGridLayout(); layout->addWidget(nameLabel, 0, 0, 1, 1); layout->addWidget(nameEdit, 0, 1, 1, 1); layout->addWidget(previousButton, 0, 2, 1, 1); layout->addWidget(addressLabel, 1, 0, 1, 1); layout->addWidget(addressEdit, 1, 1, 2, 1); layout->addWidget(nextButton, 1, 2, 1, 1); layout->addWidget(typeLabel, 3, 0, 1, 1); layout->addWidget(typeComboBox, 3, 1, 1, 1); setLayout(layout); setWindowTitle(tr("SQL Widget Mapper")); mapper->toFirst(); }
Der Vollständigkeit halber zeigen wir die Implementierung des updateButtons()
Slots:
void Window::updateButtons(int row) { previousButton->setEnabled(row > 0); nextButton->setEnabled(row < model->rowCount() - 1); }
Zusammenfassung und weiterführende Literatur
Die Verwendung eines separaten Modells für das Kombinationsfeld und eines speziellen Delegaten für den Widget-Mapper ermöglicht es uns, dem Benutzer ein Menü mit Auswahlmöglichkeiten zu präsentieren. Obwohl die Auswahlmöglichkeiten in derselben Datenbank wie die Daten des Benutzers gespeichert sind, werden sie in einer separaten Tabelle gehalten. Mit diesem Ansatz können wir zu einem späteren Zeitpunkt vollständige Datensätze rekonstruieren und dabei die Datenbankfunktionen angemessen nutzen.
Wenn keine SQL-Modelle verwendet werden, ist es immer noch möglich, mehr als ein Modell zu verwenden, um dem Benutzer Auswahlmöglichkeiten zu präsentieren. Dies wird durch das Combo Widget Mapper Beispiel abgedeckt.
© 2025 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.