Exemple d'éditeur de raccourcis
L'exemple « Éditeur de raccourcis » montre comment créer un modèle hiérarchique de base en lecture-écriture destiné à être utilisé avec les classes standard « view » et « QKeySequenceEdit » de Qt. Pour une description de la programmation modèle-vue, consultez la présentation de la programmation modèle-vue.

L'architecture modèle/vue de Qt fournit un moyen standard permettant aux vues de manipuler les informations d'une source de données, en utilisant un modèle abstrait des données afin de simplifier et de normaliser leur accès. Le modèle de l'éditeur de raccourcis représente les actions sous la forme d'une arborescence d'éléments et permet aux vues d'accéder à ces données via un système basé sur des index. Plus généralement, les modèles peuvent être utilisés pour représenter des données sous la forme d’une structure arborescente en permettant à chaque élément d’agir en tant que parent d’un tableau d’éléments enfants.
Conception et concepts
La structure de données que nous utilisons pour représenter la structure des données prend la forme d'un arbre constitué d'objets `ShortcutEditorModelItem`. Chaque `ShortcutEditorModelItem` représente un élément dans une vue arborescente et contient deux colonnes de données.
| Structure de l’éditeur de raccourcis Les données sont stockées en interne dans le modèle à l’aide d’objets `ShortcutEditorModelItem` reliés entre eux au sein d’une structure arborescente basée sur des pointeurs. En règle générale, chaque objet `ShortcutEditorModelItem` possède un élément parent et peut avoir plusieurs éléments enfants. Cependant, l’élément racine de la structure arborescente n’a pas d’élément parent et n’est jamais référencé en dehors du modèle. Chaque ShortcutEditorModelItem contient des informations sur sa position dans la structure arborescente ; il peut renvoyer son élément parent et son numéro de ligne. Le fait de disposer immédiatement de ces informations facilite la mise en œuvre du modèle. Étant donné que chaque élément d’une arborescence contient généralement plusieurs colonnes de données (un nom et un raccourci dans cet exemple), il est naturel de stocker ces informations dans chaque élément. Par souci de simplicité, nous utiliserons une liste d’objets ` QVariant ` pour stocker les données de chaque colonne de l’élément. |
L’utilisation d’une structure arborescente basée sur des pointeurs signifie que, lorsque l’on transmet un index de modèle à une vue, on peut enregistrer l’adresse de l’élément correspondant dans l’index (voir QAbstractItemModel::createIndex()) et la récupérer ultérieurement avec QModelIndex::internalPointer(). Cela facilite l’écriture du modèle et garantit que tous les index de modèle faisant référence au même élément possèdent le même pointeur de données interne.
Une fois la structure de données appropriée en place, nous pouvons créer un modèle arborescent avec un minimum de code supplémentaire pour fournir des index de modèle et des données à d’autres composants.
Définition de la classe ShortcutEditorModelItem
La classe `ShortcutEditorModelItem` est définie comme suit :
Il s’agit d’une classe C++ de base. Elle n’hérite pas de ` QObject ` et ne fournit ni signaux ni slots. Elle sert à contenir une liste de `QVariants`, renfermant les données des colonnes ainsi que des informations sur leur position dans la structure arborescente. Les fonctions offrent les fonctionnalités suivantes :
- La fonction `
appendChildItem()` sert à ajouter des données lors de la construction initiale du modèle et n’est pas utilisée en fonctionnement normal. - Les fonctions `
child()` et `childCount()` permettent au modèle d’obtenir des informations sur tous les éléments enfants. - La fonction `
columnCount()` fournit des informations sur le nombre de colonnes associées à l’élément, et les données de chaque colonne peuvent être obtenues à l’aide de la fonction `data()`. - Les fonctions `
row()` et `parent()` sont utilisées pour obtenir le numéro de ligne de l’élément et son élément parent.
L’élément parent et les données des colonnes sont stockés dans les variables membres privées ` parentItem ` et ` itemData `. La variable ` childItems ` contient une liste de pointeurs vers les éléments enfants de l’élément.
Définition de la classe ShortcutEditorModel
La classe ` ShortcutEditorModel ` est définie comme suit :
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;
};Cette classe est similaire à la plupart des autres sous-classes de ` QAbstractItemModel ` qui fournissent des modèles en lecture-écriture. Seules la forme du constructeur et la fonction ` setupModelData() ` sont spécifiques à ce modèle. De plus, nous fournissons un destructeur pour effectuer le nettoyage lorsque le modèle est détruit.
Implémentation de la classe `ShortcutEditorModel`
Le constructeur prend un argument contenant les données que le modèle partagera avec les vues et les délégués :
ShortcutEditorModel::ShortcutEditorModel(QObject *parent)
: QAbstractItemModel(parent)
{
m_rootItem = new ShortcutEditorModelItem({tr("Name"), tr("Shortcut")});
}C’est au constructeur qu’il revient de créer un élément racine pour le modèle. Cet élément ne contient que des données d’en-tête verticales, pour des raisons pratiques. Nous l’utilisons également pour référencer la structure de données interne qui contient les données du modèle, et il sert à représenter un parent imaginaire des éléments de niveau supérieur dans le modèle.
La structure de données interne du modèle est remplie d’éléments par la fonction ` setupModelData() `. Nous examinerons cette fonction séparément à la fin de ce document.
Le destructeur garantit que l’élément racine et tous ses descendants sont supprimés lorsque le modèle est détruit :
ShortcutEditorModel::~ShortcutEditorModel()
{
delete m_rootItem;
}Comme il n’est pas possible d’ajouter des données au modèle une fois qu’il a été construit et configuré, cela simplifie la gestion de l’arborescence interne des éléments.
Les modèles doivent implémenter une fonction ` index() ` afin de fournir des index que les vues et les délégués utiliseront pour accéder aux données. Des index sont créés pour d’autres composants lorsqu’ils sont référencés par leurs numéros de ligne et de colonne, ainsi que par l’index de leur modèle parent. Si un index de modèle invalide est spécifié comme parent, il appartient au modèle de renvoyer un index correspondant à un élément de niveau supérieur dans le modèle.
Lorsqu’un index de modèle nous est fourni, nous vérifions d’abord s’il est valide. S’il ne l’est pas, nous supposons qu’il s’agit d’une référence à un élément de niveau supérieur ; dans le cas contraire, nous obtenons le pointeur de données à partir de l’index du modèle via sa fonction ` internalPointer()` et l’utilisons pour référencer un objet ` TreeItem `. Notez que tous les index de modèle que nous construisons contiendront un pointeur vers un ` TreeItem` existant ; nous pouvons donc garantir que tout index de modèle valide que nous recevons contiendra un pointeur de données valide.
void ShortcutEditorModel::setActions()
{
beginResetModel();
setupModelData(m_rootItem);
endResetModel();
}Étant donné que les arguments « row » et « column » de cette fonction font référence à un élément enfant de l’élément parent correspondant, nous récupérons cet élément à l’aide de la fonction ` TreeItem::child() `. La fonction ` createIndex()` est utilisée pour créer un index de modèle à renvoyer. Nous spécifions les numéros de ligne et de colonne, ainsi qu’un pointeur vers l’élément lui-même. L’index de modèle peut ensuite être utilisé pour récupérer les données de l’élément.
La manière dont les objets ` TreeItem ` sont définis facilite l’écriture de la fonction ` parent() ` :
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();
}Il suffit de veiller à ne jamais renvoyer d’index de modèle correspondant à l’élément racine. Par souci de cohérence avec l’implémentation de la fonction ` index() `, nous renvoyons un index de modèle invalide pour le parent de tout élément de niveau supérieur du modèle.
Lors de la création d’un index de modèle à renvoyer, nous devons spécifier les numéros de ligne et de colonne de l’élément parent au sein de son propre parent. Nous pouvons facilement déterminer le numéro de ligne à l’aide de la fonction TreeItem::row(), mais nous suivons une convention consistant à spécifier 0 comme numéro de colonne du parent. L’index de modèle est créé avec createIndex() de la même manière que dans la fonction index().
La fonction ` rowCount() ` renvoie simplement le nombre d’éléments enfants pour l’index ` TreeItem ` correspondant à un index de modèle donné, ou le nombre d’éléments de niveau supérieur si un index non valide est spécifié :
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);
}Comme chaque élément gère ses propres données de colonnes, la fonction ` columnCount() ` doit appeler la fonction ` columnCount() ` de l’élément lui-même pour déterminer le nombre de colonnes présentes pour un index de modèle donné. Comme pour la fonction ` rowCount() `, si un index de modèle non valide est spécifié, le nombre de colonnes renvoyé est déterminé à partir de l’élément racine :
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();
}Les données sont obtenues à partir du modèle via data(). Étant donné que l’élément gère ses propres colonnes, nous devons utiliser le numéro de colonne pour récupérer les données à l’aide de la fonction TreeItem::data():
int ShortcutEditorModel::columnCount(const QModelIndex &parent) const
{
if (parent.isValid())
return static_cast<ShortcutEditorModelItem*>(parent.internalPointer())->columnCount();
return m_rootItem->columnCount();
}Notez que nous ne prenons en charge que la fonction ` DisplayRole ` dans cette implémentation, et que nous renvoyons également des objets ` QVariant ` non valides pour les indices de modèle non valides.
Nous utilisons la fonction ` flags() ` pour garantir que les vues sachent que le modèle est en lecture seule :
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());
}La fonction ` headerData() ` renvoie des données que nous avons judicieusement stockées dans l’élément racine :
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;
}Ces informations auraient pu être fournies d’une autre manière : soit spécifiées dans le constructeur, soit codées en dur dans la fonction ` headerData() `.
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();
}À faire
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);
}
}
}
}À faire
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);
}À faire
Configuration des données dans le modèle
Nous utilisons la fonction ` setupModelData() ` pour configurer les données initiales dans le modèle. Cette fonction récupère le texte des actions enregistrées et crée des objets `Item` qui enregistrent à la fois les données et la structure globale du modèle. Naturellement, cette fonction fonctionne d’une manière très spécifique à ce modèle. Nous fournissons ci-dessous une description de son comportement et renvoyons le lecteur au code d’exemple lui-même pour plus d’informations.
Pour garantir le bon fonctionnement du modèle, il suffit de créer des instances de `ShortcutEditorModelItem` avec les données correctes et l’élément parent approprié.
© 2026 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.