翻譯|使用教程|編輯:龔雪|2024-05-13 14:28:36.757|閱讀 119 次
概述:本文將為大家介紹如何使用Qt Widget小部件如何實(shí)現(xiàn)一個(gè)快捷編輯器,歡迎下載最新版組件體驗(yàn)~
# 界面/圖表報(bào)表/文檔/IDE等千款熱門(mén)軟控件火熱銷(xiāo)售中 >>
相關(guān)鏈接:
Qt 是目前最先進(jìn)、最完整的跨平臺(tái)C++開(kāi)發(fā)工具。它不僅完全實(shí)現(xiàn)了一次編寫(xiě),所有平臺(tái)無(wú)差別運(yùn)行,更提供了幾乎所有開(kāi)發(fā)過(guò)程中需要用到的工具。如今,Qt已被運(yùn)用于超過(guò)70個(gè)行業(yè)、數(shù)千家企業(yè),支持?jǐn)?shù)百萬(wàn)設(shè)備及應(yīng)用。
快捷編輯器示例展示了如何創(chuàng)建一個(gè)基本的讀寫(xiě)層次模型,來(lái)與Qt的標(biāo)準(zhǔn)視圖和QKeySequenceEdit類(lèi)一起使用。
Qt技術(shù)交流群:166830288 歡迎一起進(jìn)群討論
Qt的模型/視圖架構(gòu)為視圖提供了一種標(biāo)準(zhǔn)的方式來(lái)操作數(shù)據(jù)源中的信息,使用數(shù)據(jù)的抽象模型來(lái)簡(jiǎn)化和標(biāo)準(zhǔn)化訪問(wèn)數(shù)據(jù)的方式。快捷編輯器模型將操作表示為項(xiàng)目樹(shù),并允許視圖通過(guò)基于索引的系統(tǒng)訪問(wèn)此數(shù)據(jù)。更一般地說(shuō),可以使用模型以樹(shù)結(jié)構(gòu)的形式表示數(shù)據(jù),方法是允許每個(gè)項(xiàng)作為子項(xiàng)表的父項(xiàng)。
在上文中(點(diǎn)擊這里回顧>>),我們?yōu)榇蠹医榻B了快捷編輯器的設(shè)計(jì)理念及結(jié)構(gòu)等,本文將繼續(xù)介紹一些具體的實(shí)現(xiàn)類(lèi)。
構(gòu)造函數(shù)接受一個(gè)參數(shù),其中包含模型將與視圖和委托共享的數(shù)據(jù):
ShortcutEditorModel::ShortcutEditorModel(QObject *parent) : QAbstractItemModel(parent) { m_rootItem = new ShortcutEditorModelItem({tr("Name"), tr("Shortcut")}); }
由構(gòu)造函數(shù)來(lái)為模型創(chuàng)建根項(xiàng),為方便起見(jiàn),此項(xiàng)僅包含垂直標(biāo)題數(shù)據(jù)。我們還使用它來(lái)引用包含模型數(shù)據(jù)的內(nèi)部數(shù)據(jù)結(jié)構(gòu),并使用它來(lái)表示模型中頂級(jí)項(xiàng)的假想父項(xiàng)。
模型的內(nèi)部數(shù)據(jù)結(jié)構(gòu)由setupModelData()函數(shù)填充,我們將在本文末尾單獨(dú)研究這個(gè)函數(shù)。
析構(gòu)函數(shù)確保在模型被銷(xiāo)毀時(shí)刪除根項(xiàng)及其所有子類(lèi):
ShortcutEditorModel::~ShortcutEditorModel() { delete m_rootItem; }
由于在構(gòu)造和設(shè)置模型之后我們不能向模型中添加數(shù)據(jù),因此這簡(jiǎn)化了管理內(nèi)部項(xiàng)目樹(shù)的方式。
模型必須實(shí)現(xiàn)index()函數(shù)來(lái)為視圖和委托提供索引,以便在訪問(wèn)數(shù)據(jù)時(shí)使用。當(dāng)其他組件被它們的行號(hào)、列號(hào)以及它們的父模型索引引用時(shí),為它們創(chuàng)建索引。如果將無(wú)效的模型索引指定為父索引,則由模型返回與模型中的頂級(jí)項(xiàng)對(duì)應(yīng)的索引。
當(dāng)提供模型索引時(shí),我們首先檢查它是否有效。如果不是假定引用的是頂級(jí)項(xiàng);否則我們使用模型索引的() 函數(shù)從模型索引中獲取數(shù)據(jù)指針,并使用它來(lái)引用TreeItem對(duì)象。注意我們構(gòu)造的所有模型索引都將包含一個(gè)指向現(xiàn)有TreeItem的指針,因此可以保證接收到的任何有效模型索引都將包含一個(gè)有效的數(shù)據(jù)指針。
void ShortcutEditorModel::setActions() { beginResetModel(); setupModelData(m_rootItem); endResetModel(); }
由于此函數(shù)的行和列參數(shù)引用相應(yīng)父項(xiàng)的子項(xiàng),因此我們使用TreeItem::child()函數(shù)獲得該項(xiàng),createIndex()函數(shù)用于創(chuàng)建要返回的模型索引。我們指定行號(hào)和列號(hào),以及指向項(xiàng)本身的指針,稍后可以使用模型索引來(lái)獲取項(xiàng)目的數(shù)據(jù)。
TreeItem對(duì)象的定義方式使得parent()函數(shù)的編寫(xiě)變得簡(jiǎn)單:
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(); }
我們只需要確保永遠(yuǎn)不會(huì)返回與根項(xiàng)對(duì)應(yīng)的模型索引,為了與index()函數(shù)的實(shí)現(xiàn)方式保持一致,我們?yōu)槟P椭腥魏雾敿?jí)項(xiàng)的父項(xiàng)返回一個(gè)無(wú)效的模型索引。
當(dāng)創(chuàng)建要返回的模型索引時(shí),我們必須在父項(xiàng)中指定父項(xiàng)的行號(hào)和列號(hào)。我們可以很容易地使用TreeItem::row()函數(shù)發(fā)現(xiàn)行號(hào),但是我們遵循指定0作為父列號(hào)的約定。模型索引是用createIndex()創(chuàng)建的,方法與index()函數(shù)相同。
rowCount()函數(shù)只是返回對(duì)應(yīng)于給定模型索引的TreeItem的子條目的數(shù)量,或者如果指定了無(wú)效索引則返回頂級(jí)條目的數(shù)量:
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); }
由于每個(gè)項(xiàng)目都管理自己的列數(shù)據(jù),因此columnCount()函數(shù)必須調(diào)用項(xiàng)目自己的columnCount()函數(shù)來(lái)確定給定模型索引有多少列。與rowCount()函數(shù)一樣,如果指定了無(wú)效的模型索引,則返回的列數(shù)將從根項(xiàng)確定:
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(); }
數(shù)據(jù)通過(guò)Data()從模型中獲得,由于項(xiàng)目管理它自己的列,我們需要使用列號(hào)來(lái)使用TreeItem::data()函數(shù)檢索數(shù)據(jù):
int ShortcutEditorModel::columnCount(const QModelIndex &parent) const { if (parent.isValid()) return static_cast<ShortcutEditorModelItem*>(parent.internalPointer())->columnCount(); return m_rootItem->columnCount(); }
注意,在這個(gè)實(shí)現(xiàn)中我們只支持DisplayRole,并且還為無(wú)效的模型索引返回?zé)o效的QVariant對(duì)象。
我們使用flags()函數(shù)來(lái)確保視圖知道模型是只讀的:
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()); }
headerData()函數(shù)返回我們方便地存儲(chǔ)在根項(xiàng)中的數(shù)據(jù):
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; }
這些信息可以以不同的方式提供:在構(gòu)造函數(shù)中指定,或者硬編碼到headerData()函數(shù)中。
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
我們使用setupModelData()函數(shù)在模型中設(shè)置初始數(shù)據(jù),該函數(shù)檢索已注冊(cè)的操作文本并創(chuàng)建記錄數(shù)據(jù)和整體模型結(jié)構(gòu)的項(xiàng)目對(duì)象。當(dāng)然,這個(gè)函數(shù)的工作方式是非常特定于這個(gè)模型的。
為了確保模型正確工作,只需要?jiǎng)?chuàng)建具有正確數(shù)據(jù)和父項(xiàng)的ShortcutEditorModelItem實(shí)例。
本站文章除注明轉(zhuǎn)載外,均為本站原創(chuàng)或翻譯。歡迎任何形式的轉(zhuǎn)載,但請(qǐng)務(wù)必注明出處、不得修改原文相關(guān)鏈接,如果存在內(nèi)容上的異議請(qǐng)郵件反饋至chenjj@ke049m.cn
文章轉(zhuǎn)載自:慧都網(wǎng)