<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??碼云GVP開源項目 12k star Uniapp+ElementUI 功能強大 支持多語言、二開方便! 廣告
                #(46):視圖和委托 前面我們介紹了模型的概念。下面則是另外一個基本元素:視圖。在?model/view 架構中,視圖是數據從模型到最終用戶的途徑。數據通過視圖向用戶進行顯示。此時,這種顯示方式不必須同模型的存儲結構相一致。實際上,很多情況下,數據的顯示同底層數據的存儲是完全不同的。 我們使用`QAbstractItemModel`提供標準的模型接口,使用?`QAbstractItemView`提供標準的視圖接口,而結合這兩者,就可以將數據同表現層分離,在視圖中利用前面所說的模型索引。視圖管理來自模型的數據的布局:既可以直接渲染數據本身,也可以通過委托渲染和編輯數據。 視圖不僅僅用于展示數據,還用于在數據項之間的導航以及數據項的選擇。另外,視圖也需要支持很多基本的用戶界面的特性,例如右鍵菜單以及拖放。視圖可以提供數據編輯功能,也可以將這種編輯功能交由某個委托完成。視圖可以脫離模型創建,但是在其進行顯示之前,必須存在一個模型。也就是說,視圖的顯示是完全基于模型的,這是不能脫離模型存在的。對于用戶的選擇,多個視圖可以相互獨立,也可以進行共享。 某些視圖,例如`QTableView`和`QTreeView`,不僅顯示數據,還會顯示列頭或者表頭。這些是由`QHeaderView`視圖類提供的。在《[QFileSystemModel](http://www.devbean.net/2013/02/qt-study-road-2-qfilesystemmodel/)》一章的最后,我們曾經提到過這個問題。表頭通常訪問視圖所包含的同一模型。它們使用`QAbstractItemModel::headerData()`函數從模型中獲取數據,然后將其以標簽 label 的形式顯示出來。我們可以通過繼承`QHeaderView`類,實現某些更特殊的功能。 正如前面的章節介紹的,我們通常會為視圖提供一個模型。拿前面我們曾經見過的一個例子來看: ~~~ QStringList data; data << "0" << "1" << "2"; model = new QStringListModel(this); model->setStringList(data); listView = new QListView(this); listView->setModel(model); QPushButton *btnShow = new QPushButton(tr("Show Model"), this); connect(btnShow, SIGNAL(clicked()), this, SLOT(showModel())); QHBoxLayout *buttonLayout = new QHBoxLayout; buttonLayout->addWidget(btnShow); QVBoxLayout *layout = new QVBoxLayout; layout->addWidget(listView); layout->addLayout(buttonLayout); setLayout(layout); ~~~ 運行一下程序,這個界面十分簡單: [![](https://box.kancloud.cn/2015-12-29_56823262a69e8.png)](http://files.devbean.net/images/2013/03/numberlist-demo.png) 跟我們前面的演示幾乎一模一樣。現在我們有一個問題:如果我們雙擊某一行,列表會允許我們進行編輯。但是,我們沒辦法控制用戶只能輸入數字——當然,我們可以在提交數據時進行檢測,這也是一種辦法,不過,更友好的方法是,根本不允許用戶輸入非法字符。為了達到這一目的,我們使用了委托。下面,我們增加一個委托: ~~~ class SpinBoxDelegate : public QStyledItemDelegate { Q_OBJECT public: SpinBoxDelegate(QObject *parent = 0) : QStyledItemDelegate(parent) {} QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const; void setEditorData(QWidget *editor, const QModelIndex &index) const; void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const; void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const; }; ~~~ 正如前面所說,委托就是供視圖實現某種高級的編輯功能。不同于經典的 Model-View-Controller(MVC)模式,model/view 沒有將用戶交互部分完全分離。一般地,視圖將數據向用戶進行展示并且處理通用的輸入。但是,對于某些特殊要求(比如這里的要求必須輸入數字),則交予委托完成。這些組件提供輸入功能,同時也能渲染某些特殊數據項。委托的接口由`QAbstractItemDelegate`定義。在這個類中,委托通過`paint()`和`sizeHint()`兩個函數渲染用戶內容(也就是說,你必須自己將渲染器繪制出來)。為使用方便,從 4.4 開始,Qt 提供了另外的基于組件的子類:`QItemDelegate`和`QStyledItemDelegate`。默認的委托是`QStyledItemDelegate`。二者的區別在于繪制和向視圖提供編輯器的方式。`QStyledItemDelegate`使用當前樣式繪制,并且能夠使用 Qt Style Sheet(我們會在后面的章節對 QSS 進行介紹),因此我們推薦在自定義委托時,使用`QStyledItemDelegate`作為基類。不過,除非自定義委托需要自己進行繪制,否則,二者的代碼其實是一樣的。 繼承`QStyledItemDelegate`需要實現以下幾個函數: * `createEditor()`:返回一個組件。該組件會被作為用戶編輯數據時所使用的編輯器,從模型中接受數據,返回用戶修改的數據。 * `setEditorData()`:提供上述組件在顯示時所需要的默認值。 * `updateEditorGeometry()`:確保上述組件作為編輯器時能夠完整地顯示出來。 * `setModelData()`:返回給模型用戶修改過的數據。 下面依次看看各函數的實現: ~~~ QWidget *SpinBoxDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem & /* option */, const QModelIndex & /* index */) const { QSpinBox *editor = new QSpinBox(parent); editor->setMinimum(0); editor->setMaximum(100); return editor; } ~~~ 在`createEditor()`函數中,parent 參數會作為新的編輯器的父組件。 ~~~ void SpinBoxDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const { int value = index.model()->data(index, Qt::EditRole).toInt(); QSpinBox *spinBox = static_cast<QSpinBox*>(editor); spinBox->setValue(value); } ~~~ `setEditorData()`函數從模型中獲取需要編輯的數據(具有`Qt::EditRole`角色)。由于我們知道它就是一個整型,因此可以放心地調用`toInt()`函數。editor 就是所生成的編輯器實例,我們將其強制轉換成`QSpinBox`實例,設置其數據作為默認值。 ~~~ void SpinBoxDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const { QSpinBox *spinBox = static_cast<QSpinBox*>(editor); spinBox->interpretText(); int value = spinBox->value(); model->setData(index, value, Qt::EditRole); } ~~~ 在用戶編輯完數據后,委托會調用`setModelData()`函數將新的數據保存到模型中。因此,在這里我們首先獲取`QSpinBox`實例,得到用戶輸入值,然后設置到模型相應的位置。標準的`QStyledItemDelegate`類會在完成編輯時發出`closeEditor()`信號,視圖會保證編輯器已經關閉,但是并不會銷毀,因此需要另外對內存進行管理。由于我們的處理很簡單,無需發出`closeEditor()`信號,但是在復雜的實現中,記得可以在這里發出這個信號。針對數據的任何操作都必須提交給`QAbstractItemModel`,這使得委托獨立于特定的視圖。當然,在真實應用中,我們需要檢測用戶的輸入是否合法,是否能夠存入模型。 ~~~ void SpinBoxDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const { editor->setGeometry(option.rect); } ~~~ 最后,由于我們的編輯器只有一個數字輸入框,所以只是簡單將這個輸入框的大小設置為單元格的大小(由`option.rect`提供)。如果是復雜的編輯器,我們需要根據單元格參數(由`option`提供)、數據(由`index`提供)結合編輯器(由`editor`提供)計算編輯器的顯示位置和大小。 現在,我們的委托已經編寫完畢。接下來需要將這個委托設置為`QListView`所使用的委托: ~~~ listView->setItemDelegate(new SpinBoxDelegate(listView)); ~~~ 值得注意的是,new 操作符并不會真的創建編輯器實例。相反,只有在真正需要時,Qt 才會生成一個編輯器實例。這保證了程序運行時的性能。 然后我們運行下程序: [![](https://box.kancloud.cn/2015-12-29_56823262ba86f.png)](http://files.devbean.net/images/2013/03/numberlist-editing-demo.png)
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看