Shortcut Editor Beispiel
Das Shortcut-Editor-Beispiel zeigt, wie man ein einfaches hierarchisches Modell zum Lesen und Schreiben erstellt, das mit den Standard-View- und QKeySequenceEdit -Klassen von Qt verwendet werden kann. Eine Beschreibung der Model/View-Programmierung finden Sie in der Übersicht Model/View-Programmierung.
Die Model/View-Architektur von Qt bietet einen Standardweg für Views, um Informationen in einer Datenquelle zu manipulieren, wobei ein abstraktes Modell der Daten verwendet wird, um die Art des Zugriffs zu vereinfachen und zu standardisieren. Das Modell des Shortcut-Editors stellt die Aktionen als einen Baum von Elementen dar und ermöglicht es den Ansichten, auf diese Daten über ein indexbasiertes System zuzugreifen. Ganz allgemein können Modelle verwendet werden, um Daten in Form einer Baumstruktur darzustellen, indem jedes Element als übergeordnetes Element für eine Tabelle mit untergeordneten Elementen fungieren kann.
Aufbau und Konzepte
Die Datenstruktur, die wir verwenden, um die Struktur der Daten darzustellen, hat die Form eines Baums, der aus ShortcutEditorModelItem-Objekten besteht. Jedes ShortcutEditorModelItem stellt ein Element in einer Baumansicht dar und enthält zwei Spalten mit Daten.
![]() | Struktur des Shortcut-Editors Die Daten werden intern im Modell unter Verwendung von ShortcutEditorModelItem-Objekten gespeichert, die in einer zeigerbasierten Baumstruktur miteinander verbunden sind. Im Allgemeinen hat jedes ShortcutEditorModelItem ein übergeordnetes Element und kann eine Reihe von untergeordneten Elementen haben. Das Wurzelelement in der Baumstruktur hat jedoch kein übergeordnetes Element und es wird nie außerhalb des Modells referenziert. Jedes ShortcutEditorModelItem enthält Informationen über seinen Platz in der Baumstruktur; es kann sein übergeordnetes Element und seine Zeilennummer zurückgeben. Diese Informationen sind leicht verfügbar und erleichtern die Implementierung des Modells. Da jedes Element in einer Baumansicht in der Regel mehrere Datenspalten enthält (in diesem Beispiel einen Namen und eine Abkürzung), ist es naheliegend, diese Informationen in jedem Element zu speichern. Der Einfachheit halber werden wir eine Liste von QVariant Objekten verwenden, um die Daten für jede Spalte im Element zu speichern. |
Die Verwendung einer zeigerbasierten Baumstruktur bedeutet, dass wir bei der Übergabe eines Modellindex an eine Ansicht die Adresse des entsprechenden Elements im Index aufzeichnen können (siehe QAbstractItemModel::createIndex()) und sie später mit QModelIndex::internalPointer() abrufen können. Dies erleichtert das Schreiben des Modells und stellt sicher, dass alle Modellindizes, die sich auf das gleiche Element beziehen, den gleichen internen Datenzeiger haben.
Mit der entsprechenden Datenstruktur können wir ein Baummodell mit einem Minimum an zusätzlichem Code erstellen, um Modellindizes und Daten an andere Komponenten zu liefern.
ShortcutEditorModelItem Klassendefinition
Die Klasse ShortcutEditorModelItem ist wie folgt definiert:
Die Klasse ist eine einfache C++-Klasse. Sie erbt nicht von QObject und bietet keine Signale und Slots. Sie wird verwendet, um eine Liste von QVarianten zu speichern, die Spaltendaten und Informationen über ihre Position in der Baumstruktur enthält. Die Funktionen bieten die folgenden Eigenschaften:
- Die Funktion
appendChildItem()
wird verwendet, um Daten hinzuzufügen, wenn das Modell zum ersten Mal konstruiert wird, und wird während des normalen Gebrauchs nicht verwendet. - Die Funktionen
child()
undchildCount()
ermöglichen es dem Modell, Informationen über alle untergeordneten Elemente zu erhalten. - Informationen über die Anzahl der Spalten, die mit dem Element verbunden sind, werden von
columnCount()
bereitgestellt, und die Daten in jeder Spalte können mit der Funktion data() abgerufen werden. - Die Funktionen
row()
undparent()
werden verwendet, um die Zeilennummer des Elements und das übergeordnete Element zu erhalten.
Die Daten des übergeordneten Elements und der Spalten werden in den privaten Mitgliedsvariablen parentItem
und itemData
gespeichert. Die Variable childItems
enthält eine Liste von Zeigern auf die eigenen untergeordneten Elemente des Elements.
ShortcutEditorModel Klassendefinition
Die Klasse ShortcutEditorModel
ist wie folgt definiert:
class ShortcutEditorModel : public QAbstractItemModel { Q_OBJECT class ShortcutEditorModelItem { public: explicit ShortcutEditorModelItem(const QList<QVariant> &data, ShortcutEditorModelItem *parentItem = nullptr); ~ShortcutEditorModelItem(); void appendChild(ShortcutEditorModelItem *child); ShortcutEditorModelItem *child(int row) const; int childCount() const; int columnCount() const; QVariant data(int column) const; int row() const; ShortcutEditorModelItem *parentItem() const; QAction *action() const; private: QList<ShortcutEditorModelItem *> m_childItems; QList<QVariant> m_itemData; ShortcutEditorModelItem *m_parentItem; }; public: explicit ShortcutEditorModel(QObject *parent = nullptr); ~ShortcutEditorModel() override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; Qt::ItemFlags flags(const QModelIndex &index) const override; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override; QModelIndex parent(const QModelIndex &index) const override; int rowCount(const QModelIndex &index = QModelIndex()) const override; int columnCount(const QModelIndex &index = QModelIndex()) const override; bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override; void setActions(); private: void setupModelData(ShortcutEditorModelItem *parent); ShortcutEditorModelItem *m_rootItem; };
Diese Klasse ähnelt den meisten anderen Unterklassen von QAbstractItemModel, die Modelle zum Lesen und Schreiben bereitstellen. Nur die Form des Konstruktors und die Funktion setupModelData()
sind spezifisch für dieses Modell. Zusätzlich bieten wir einen Destruktor an, um aufzuräumen, wenn das Modell zerstört wird.
Implementierung der ShortcutEditorModel-Klasse
Der Konstruktor nimmt ein Argument entgegen, das die Daten enthält, die das Modell mit den Ansichten und Delegierten teilen wird:
ShortcutEditorModel::ShortcutEditorModel(QObject *parent) : QAbstractItemModel(parent) { m_rootItem = new ShortcutEditorModelItem({tr("Name"), tr("Shortcut")}); }
Es ist Aufgabe des Konstruktors, ein Wurzelelement für das Modell zu erstellen. Dieses Element enthält der Einfachheit halber nur vertikale Kopfdaten. Wir verwenden es auch, um auf die interne Datenstruktur zu verweisen, die die Modelldaten enthält, und es wird verwendet, um ein imaginäres übergeordnetes Element der Top-Level-Elemente im Modell darzustellen.
Die interne Datenstruktur des Modells wird durch die Funktion setupModelData()
mit Elementen gefüllt. Wir werden diese Funktion am Ende dieses Dokuments gesondert untersuchen.
Der Destruktor sorgt dafür, dass das Wurzelelement und alle seine Nachkommen gelöscht werden, wenn das Modell zerstört wird:
ShortcutEditorModel::~ShortcutEditorModel() { delete m_rootItem; }
Da wir dem Modell keine Daten hinzufügen können, nachdem es konstruiert und eingerichtet wurde, vereinfacht dies die Verwaltung des internen Baums der Elemente.
Modelle müssen eine index()
Funktion implementieren, um Indizes für Views und Delegates bereitzustellen, die beim Zugriff auf Daten verwendet werden. Indizes werden für andere Komponenten erstellt, wenn sie durch ihre Zeilen- und Spaltennummern und ihren übergeordneten Modellindex referenziert werden. Wenn ein ungültiger Modellindex als übergeordnetes Element angegeben wird, obliegt es dem Modell, einen Index zurückzugeben, der einem Element der obersten Ebene im Modell entspricht.
Wenn wir einen Modellindex erhalten, prüfen wir zunächst, ob er gültig ist. Ist dies nicht der Fall, nehmen wir an, dass auf ein Element der obersten Ebene verwiesen wird; andernfalls erhalten wir den Datenzeiger aus dem Modellindex mit der Funktion internalPointer() und verwenden ihn, um auf ein TreeItem
Objekt zu verweisen. Beachten Sie, dass alle Modellindizes, die wir konstruieren, einen Zeiger auf ein vorhandenes TreeItem
enthalten, so dass wir garantieren können, dass alle gültigen Modellindizes, die wir erhalten, einen gültigen Datenzeiger enthalten.
void ShortcutEditorModel::setActions() { beginResetModel(); setupModelData(m_rootItem); endResetModel(); }
Da die Zeilen- und Spaltenargumente für diese Funktion auf ein untergeordnetes Element des entsprechenden übergeordneten Elements verweisen, erhalten wir das Element mit der Funktion TreeItem::child()
. Die Funktion createIndex() wird verwendet, um einen Modellindex zu erstellen, der zurückgegeben wird. Wir geben die Zeilen- und Spaltennummern sowie einen Zeiger auf das Element selbst an. Der Modellindex kann später verwendet werden, um die Daten des Eintrags zu erhalten.
Die Art und Weise, wie die TreeItem
Objekte definiert sind, macht das Schreiben der Funktion parent()
einfach:
QModelIndex ShortcutEditorModel::index(int row, int column, const QModelIndex &parent) const { if (!hasIndex(row, column, parent)) return QModelIndex(); ShortcutEditorModelItem *parentItem; if (!parent.isValid()) parentItem = m_rootItem; else parentItem = static_cast<ShortcutEditorModelItem*>(parent.internalPointer()); ShortcutEditorModelItem *childItem = parentItem->child(row); if (childItem) return createIndex(row, column, childItem); return QModelIndex(); }
Wir müssen nur sicherstellen, dass wir niemals einen Modellindex zurückgeben, der dem Stammelement entspricht. Um mit der Art und Weise, wie die Funktion index()
implementiert ist, konsistent zu sein, geben wir einen ungültigen Modellindex für das übergeordnete Element aller Top-Level-Elemente im Modell zurück.
Beim Erstellen eines zurückzugebenden Modellindexes müssen wir die Zeilen- und Spaltennummern des übergeordneten Elements innerhalb seines eigenen übergeordneten Elements angeben. Die Zeilennummer kann leicht mit der Funktion TreeItem::row()
ermittelt werden, aber wir folgen der Konvention, 0 als Spaltennummer des übergeordneten Elements anzugeben. Der Modellindex wird mit der Funktion createIndex() auf die gleiche Weise erstellt wie mit der Funktion index()
.
Die Funktion rowCount()
gibt einfach die Anzahl der untergeordneten Elemente für die TreeItem
zurück, die einem gegebenen Modellindex entspricht, oder die Anzahl der Elemente der obersten Ebene, wenn ein ungültiger Index angegeben wird:
QModelIndex ShortcutEditorModel::parent(const QModelIndex &index) const { if (!index.isValid()) return QModelIndex(); ShortcutEditorModelItem *childItem = static_cast<ShortcutEditorModelItem*>(index.internalPointer()); ShortcutEditorModelItem *parentItem = childItem->parentItem(); if (parentItem == m_rootItem) return QModelIndex(); return createIndex(parentItem->row(), 0, parentItem); }
Da jedes Element seine eigenen Spaltendaten verwaltet, muss die Funktion columnCount()
die Element-eigene Funktion columnCount()
aufrufen, um festzustellen, wie viele Spalten für einen bestimmten Modellindex vorhanden sind. Wie bei der Funktion rowCount()
wird bei Angabe eines ungültigen Modellindexes die Anzahl der zurückgegebenen Spalten anhand des Stammelements ermittelt:
int ShortcutEditorModel::rowCount(const QModelIndex &parent) const { ShortcutEditorModelItem *parentItem; if (parent.column() > 0) return 0; if (!parent.isValid()) parentItem = m_rootItem; else parentItem = static_cast<ShortcutEditorModelItem*>(parent.internalPointer()); return parentItem->childCount(); }
Die Daten werden über data()
aus dem Modell bezogen. Da das Element seine eigenen Spalten verwaltet, müssen wir die Spaltennummer verwenden, um die Daten mit der Funktion TreeItem::data()
abzurufen:
int ShortcutEditorModel::columnCount(const QModelIndex &parent) const { if (parent.isValid()) return static_cast<ShortcutEditorModelItem*>(parent.internalPointer())->columnCount(); return m_rootItem->columnCount(); }
Beachten Sie, dass wir in dieser Implementierung nur DisplayRole unterstützen und auch ungültige QVariant Objekte für ungültige Modellindizes zurückgeben.
Wir verwenden die Funktion flags()
, um sicherzustellen, dass die Ansichten wissen, dass das Modell schreibgeschützt ist:
QVariant ShortcutEditorModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) return QVariant(); if (role != Qt::DisplayRole && role != Qt::EditRole) return QVariant(); ShortcutEditorModelItem *item = static_cast<ShortcutEditorModelItem*>(index.internalPointer()); return item->data(index.column()); }
Die Funktion headerData()
gibt Daten zurück, die wir praktischerweise im Stammelement gespeichert haben:
Qt::ItemFlags ShortcutEditorModel::flags(const QModelIndex &index) const { if (!index.isValid()) return Qt::NoItemFlags; Qt::ItemFlags modelFlags = QAbstractItemModel::flags(index); if (index.column() == static_cast<int>(Column::Shortcut)) modelFlags |= Qt::ItemIsEditable; return modelFlags; }
Diese Informationen hätten auch auf andere Weise bereitgestellt werden können: entweder im Konstruktor angegeben oder fest in der Funktion headerData()
kodiert.
QVariant ShortcutEditorModel::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { return m_rootItem->data(section); } return QVariant(); }
TODO
void ShortcutEditorModel::setupModelData(ShortcutEditorModelItem *parent) { ActionsMap actionsMap; Application *application = static_cast<Application *>(QCoreApplication::instance()); ActionManager *actionManager = application->actionManager(); const QList<QAction *> registeredActions = actionManager->registeredActions(); for (QAction *action : registeredActions) { QString context = actionManager->contextForAction(action); QString category = actionManager->categoryForAction(action); actionsMap[context][category].append(action); } QAction *nullAction = nullptr; const QString contextIdPrefix = "root"; // Go through each context, one context - many categories each iteration for (const auto &contextLevel : actionsMap.keys()) { ShortcutEditorModelItem *contextLevelItem = new ShortcutEditorModelItem({contextLevel, QVariant::fromValue(nullAction)}, parent); parent->appendChild(contextLevelItem); // Go through each category, one category - many actions each iteration for (const auto &categoryLevel : actionsMap[contextLevel].keys()) { ShortcutEditorModelItem *categoryLevelItem = new ShortcutEditorModelItem({categoryLevel, QVariant::fromValue(nullAction)}, contextLevelItem); contextLevelItem->appendChild(categoryLevelItem); for (QAction *action : actionsMap[contextLevel][categoryLevel]) { QString name = action->text(); if (name.isEmpty() || !action) continue; ShortcutEditorModelItem *actionLevelItem = new ShortcutEditorModelItem({name, QVariant::fromValue(reinterpret_cast<void *>(action))}, categoryLevelItem); categoryLevelItem->appendChild(actionLevelItem); } } } }
TODO
bool ShortcutEditorModel::setData(const QModelIndex &index, const QVariant &value, int role) { if (role == Qt::EditRole && index.column() == static_cast<int>(Column::Shortcut)) { QString keySequenceString = value.toString(); ShortcutEditorModelItem *item = static_cast<ShortcutEditorModelItem *>(index.internalPointer()); QAction *itemAction = item->action(); if (itemAction) { if (keySequenceString == itemAction->shortcut().toString(QKeySequence::NativeText)) return true; itemAction->setShortcut(keySequenceString); } Q_EMIT dataChanged(index, index); if (keySequenceString.isEmpty()) return true; } return QAbstractItemModel::setData(index, value, role); }
TODO
Einrichten der Daten im Modell
Wir verwenden die Funktion setupModelData()
, um die Anfangsdaten im Modell einzurichten. Diese Funktion ruft den registrierten Aktionstext ab und erstellt Elementobjekte, die sowohl die Daten als auch die gesamte Modellstruktur aufzeichnen. Natürlich arbeitet diese Funktion auf eine Weise, die sehr spezifisch für dieses Modell ist. Wir beschreiben im Folgenden das Verhalten dieser Funktion und verweisen den Leser für weitere Informationen auf den Beispielcode selbst.
Um sicherzustellen, dass das Modell korrekt funktioniert, müssen lediglich Instanzen von ShortcutEditorModelItem mit den richtigen Daten und dem übergeordneten Element erstellt werden.
© 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.